Software Development - Programming Language Comparison - RoboProg's

RoboProg's / Software Development

Last Month


Dec 20, 2009

My Language Is Better Than Yours!

Really? Better [1] in what situation?

Perl is the fastest! No, Java is the fastest! What I really meant to say is, C is the fastest! Would you believe that all of these answers are correct, under the right circumstances? Of course you would. (but which circumstances?)

A while back, I put together a test to compare forking and threading, using several implementations. I recently expanded my choice of languages a bit, as well as adding an option to run the entire task set sequentially, just to get an idea how each language would handle a batch. Keep in mind that the bulk of the "work" done by these demo programming examples is creating / updating / discarding strings, just like most real business applications, rather than calculating fibonacci sequences, prime number generation, supernova simulations, or tracking the path of a projectile from the BFG.

So, here are the scenarios I considered, as well as the language [2] that did the best in each category.

I have summarized the results below

Host Implementation Method Elapsed Time
(sec)
Relative Speed
(Perl + Sequential = 1,
0 = instant)
Atom C Sequential 0.34 1.06
Atom C Fork 9.96

31.1

Atom C Thread [3] N/A
Atom Perl Sequential 0.32

1

Atom Perl Fork 36.45 114
Atom Perl Thread [4] 57.88 181
Atom Java Sequential 1.04 3.25
Atom Java Fork [5] N/A
Atom Java Thread 1.30

4.06

Atom Python Sequential 0.73 2.28
Atom Python Fork 52.79 165
Atom Python Thread 7.80 24.4
Atom Ruby Sequential 0.82 2.56
Atom Ruby Fork 23.46 73.3
Atom Ruby Thread [4] 2.39 7.47

Pentium D C Sequential 0.16 1.23
Pentium D C Fork 3.66

28.2

Pentium D C Thread [3] N/A
Pentium D Perl Sequential 0.13

1

Pentium D Perl Fork 66.50 512
Pentium D Perl Thread [4] 106.05 816
Pentium D Java Sequential 3.29 25.3
Pentium D Java Fork [5] N/A
Pentium D Java Thread 3.87

29.8

Pentium D Python Sequential 0.98 7.54
Pentium D Python Fork 136.34 1049
Pentium D Python Thread 40.17 309
Pentium D Ruby Sequential 0.45 3.46
Pentium D Ruby Fork 33.56 258
Pentium D Ruby Thread [4] 4.13 31.8

Each task was run 10,001 times [6], using the method (sequential; fork; thread) inticated. The median time of 5 trials is the time reported above.

Further details: The Atom is a "slow processor, fast memory bus" combination (1600 MHz CPU, 512 KB cache with 1024 MB of 533 MHz memory); while the Pentium-D is a "fast processor, slow memory bus" combination (3000 MHz CPU, 1024 KB cache with 1024 MB of 300 - 400 MHz memory -- I lost the papers on the RAM, so I am not completely sure of its unannounced speed). Also, I seem to have managed to burden each machine with additional crud since last spring, so they are now running a little slower. For the Atom, most of the slow down might be due to enabling the hyper-threading. For the Pentium-D, the change must be due to additional server bloat I've gratuitously installed. Nevertheless, these are the level, but bumpy, playing fields I have provided for each implementation.

Please feel free to download the source (and my logs) and see how these work on your system(s). I would like to know if there are any unexpected results on any much slower or faster systems.

Some parting thoughts: Generating a bunch of tasks, via fork or new threads, turns out to be a really bad way to handle incoming requests on a server. A better way [7] is to have a front end process which inserts commands/requests into a queue, which is itself dequeued by a small number of worker tasks, and another queue which communicates results back from the workers to the front end, or some variation on this theme. But that is not the subject matter for today's rant.



Notes:

  1. By "better" I mean "faster", of course. Some would argue that other factors matter, but for the sake of this discussion, let's pretend that they don't.
  2. Purists will note that one does not speak of the speed of a language, but rather of its implementation. So replace "language" with "implementation" in the rest of this discussion. Note that the run logs in the source archive have a blurb about the implementation (version) of each language used.
  3. Technically, C of course has threads. However, by the time you replace various and sundry runtime library calls with the reentrant versions, it might as well be another language. Put another way, I was too lazy to bother with threaded C.
  4. Some would argue that green threads, such as those used by Perl and Ruby, are not real threads. While they do not do much to exploit multiprocessor systems, they do at least let you handle tasks asynchronously.
  5. You could use the Java Native Interface to implement a call to fork (or download a library that already does it). But as soon as you use JNI, it's not really 100% pure Java anymore, is it? And, it would not work on Windows (which everybody needs?). And, the size of the JVM, combined with page table updates (due to copy-on-write) caused by the GC, would probably make it prohibitively slow anyway. Plus, I'm lazy anyway.
  6. There are runs of 1; 5,001; 10,001 and 20,001 iterations in the archived run logs as well, so as to allow comparison of start up times, as well as to see how linear the time taken per iteration was.
  7. Again, "better" meaning "faster", not better meaning conceptually simple.


Note that there were no articles in October and November.


Contact me:
r
o
b
o
p
r
o
g
@
yahoo.com

Copyright 2009, Robin R Anderson