Python + Psyco vs Java vs C++ vs C
Posted on July 11th, 2008 in C/C++, Java, Python | 9 Comments »
In my last post, I demonstrated how Psyco can be used to speed up Python code using the example of a brute-force password cracker. To see how the speed of the Python-Psyco combination compared to the speed of other languages I ported to code to Java, C++ and C. In each language I wrote the code as if it were a regular program written in that language (i.e. I didn’t try to optimize the code) - in C++ I used C++ STL Strings, in Java I used the StringBuilder class and in C I used char arrays.
The timings for the Python+Psyco code are as follows (see last post for full details):
Password | Time taken zzzz | 0m0.134s zzzzz | 0m3.486s zzzzzz | 2m8.056s
Here is the Java code (I’m not a Java programmer at all - I just know the basics so I apologise if the code is poorly written):
class BrutePass { static int count = 0; public static void main( String args[] ) { if( args.length < 1 ) { System.out.println( "Usage: java BrutePass <password>" ); return; } String pwd = args[0]; int maxlen = 10; StringBuilder p = new StringBuilder(); for( int w=0; w<maxlen; w++ ) if( crack( w+1, 0, p, pwd ) > 0 ) break; System.out.println( "Found password " + p.toString() + " with " + count + " attempts." ); } public static int crack( int w, int pos, StringBuilder p, String pwd ) { String chars = "abcdefghijklmnopqrstuvwxyz"; for( int i=0; i<chars.length(); i++ ) { p.append( chars.charAt( i ) ); if( pos == w-1 ) { count++; if( pwd.compareTo( p.toString() ) == 0 ) return 1; } int ret = 0; if( pos < w-1 ) ret = crack( w, pos+1, p, pwd ); if( ret > 0 ) return 1; p.delete( p.length() - 1, p.length() ); } return 0; } }
Here are the timings for the java program:
Password | Time taken zzzz | 0m0.176s zzzzz | 0m1.334s zzzzzz | 0m31.369s
Much faster than the Psyco+Python version. The reason that the four-character password appears to take longer to crack in Java than in Python is that the JVM takes a while to get going. Now for the C++ code:
#include <iostream> #include <string> using namespace std; int crack( int w, int pos, string& p, string& pass ); int main( int argc, char **argv ) { if( argc < 2 ) { cout << "Usage: " << argv[0] << " <password>" << endl; return( 0 ); } int maxlen = 10; if( argc > 2 ) maxlen = atoi( argv[2] ); string pwd( argv[1] ); string p; int count; for( int i=0; i<maxlen; i++) if( count = crack( i+1, 0, p, pwd ) ) break; cout << "Found password " << p << " with " << count << " attempts." << endl; return 0; } int crack( int w, int pos, string& p, string& pass ) { static int count = 0; string chars( "abcdefghijklmnopqrstuvwxyz" ); for( int i=0; i<chars.length(); i++) { p.push_back( chars[i] ); if( pos == w-1 ) { if( p == pass ) return( count ); count++; } int ret = 0; if( pos < w-1 ) ret = crack( w, pos+1, p, pass ); if( ret ) return( count ); p.erase( p.end()-1 ); } return( 0 ); }
And the timings:
Password | Time taken zzzz | 0m0.036s zzzzz | 0m0.730s zzzzzz | 0m18.999s
I’d heard that Java is in many cases faster than C++; clearly this is not one of those cases. Now the C version:
#include <stdio.h> #include <stdlib.h> #include <string.h> unsigned long crack( int w, int pos, char *p, char *pass ); int main( int argc, char **argv ) { int maxlen = 10, i; unsigned long count; char *pwd, *p; if( argc < 2 ) { printf( "Usage: %s <password>\n", argv[0] ); return( 1 ); } if( argc > 2 ) maxlen = atoi( argv[2] ); pwd = argv[1]; p = (char *)calloc( maxlen + 1, sizeof(char) ); for( i=0; i<maxlen; i++) if( count = crack( i+1, 0, p, pwd ) ) break; printf( "Found password %s with %lu attempts.\n", p, count ); return 0; } unsigned long crack( int w, int pos, char *p, char *pass ) { static unsigned long count = 0; int i, ret; char *chars = "abcdefghijklmnopqrstuvwxyz"; for( i=0; i<strlen(chars); i++) { p[strlen(p)] = chars[i]; if( pos == w-1 ) { count++; if( !strcmp( p, pass ) ) return( count ); } ret = 0; if( pos < w-1 ) ret = crack( w, pos+1, p, pass ); if( ret ) return( count ); p[strlen(p)-1] = 0; } return( 0 ); }
And the timings:
Password | Time taken zzzz | 0m0.022s zzzzz | 0m0.381s zzzzzz | 0m9.663s
That’s pretty much twice the speed of the C++ version! This shows how much overhead there is for using STL Strings rather than C strings.
Although Psyco provided a fantastic speed boost, it has not eliminated the need to know C as some people have claimed - the native C version of the program performed 13.25x faster than the Psyco version.