A mass of stupid benchmarks. You have been warned.

A few weeks ago Wlofie [1] and I were talking about computer languages and I mentioned the language I wrote in college [2] (it was more than the simple DSL (Domain Specific Language) shown there), so I cleaned up the code so it would compile (most of it was written in 1993; last date of modification was March 5^th, 1995) and just because I was curious, I decided to run a stupid benchmark on it.

What's more stupid than just adding up the first 1,000,000 integers?

>
```
: main
0 0
begin
swap over + swap 1 +
dup 1000000 < while
repeat
drop "%d#n" string print ;
main 0 exit
```

(Oh, did I mention the language was based off Forth [3]?)

Running that code on my modern 2.6GHz (Gigahertz) machine takes a full 2.1 seconds (and that's after I removed a huge bottle neck in the runtime engine). The equivalent C code:

>
```
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char *argv[])
{
int i;
int total;
for (i = 0 , total = 0 ; i < 1000000 ; i++)
total += i;
printf("%d\n",total);
return EXIT_SUCCESS;
}
```

takes a mere 0.004 seconds to run.

Okay, that's expected—it's C. But what about Perl? 0.47 seconds.

Heh. A hundred times slower.

Hey! What about Python? It's not as fast as Perl, but it comes in at around 0.58 seconds.

I'm not even going to attempt a comparrison between my language and Ruby [4]—it'd be too depressing to lose out to a known slow language.

But Wlofie did mention one other language he was playing around with—Lua [5]. Okay, how fast is Lua? Same benchmark as above: 0.15 seconds.

Wow.

It blew Perl right out of the water.

This required a bit more investigation. And a better test than just summing up the first 1,000,000 integers. I settled upon the Jumble program [6]. It involves I/O (Input/Output) and has one non-trivial operation (sorting the letters in a word). And on my current system, the C version can check 483,523 words in 0.18 seconds (the timings I gave back in 2008 were for my older, 120MHz (Megahertz) development machine).

The Lua code is straightforward:

>
```
#!/usr/local/bin/lua
WORDS = "/usr/share/dict/words"
-- ******************************************************
function sortword(word)
local t = {}
for i=1,string.len(word) do t[i] = string.byte(word,i) end
table.sort(t)
return string.char(unpack(t))
end
-- ********************************************************
if #arg < 1 then
io.stderr:write("usage: %s word\n",arg[0])
os.exit(1)
end
word = sortword(string.upper(arg[1]))
dict = io.open(WORDS,"r")
if dict == nil then
io.stderr:write("Houston, we have a problem ... \n")
os.exit(1)
end
for line in dict:lines() do
dictsort = sortword(string.upper(line))
if dictsort == word then
print(line)
end
end
dict:close()
os.exit(0)
```

sortword() has quite a bit of work to do—it has to go through the string, placing each letter into an array, sort the array, and then convert the array back into a string. Unlike C, which treats strings as an array of characters (and thus can be sorted without the data gymnastics), Lua (and pretty much all other modern scripting languages) treat strings at a single unit.

So it takes 7.9 seconds for Lua to run through the same 483,523 words.

Ouch. And I think it's pretty obvious where the time is being spent—in sortword(), pulling apart strings, sorting arrays, making new strings. But Lua can be easily extended using C. I rewrite sortword() in C:

>
```
#include <stdlib.h>
#include <string.h>
#include <lua.h>
#include <lauxlib.h>
#include <lualib.h>
/*******************************************/
static int cmp(const void *l,const void *r)
{
int cl;
int cr;
cl = *(const char *)l;
cr = *(const char *)r;
return cl - cr;
}
/************************************/
static int sortword(lua_State *L)
{
const char *word;
size_t len;
word = luaL_checkstring(L,1);
len = strlen(word);
char buffer[len];
memcpy(buffer,word,len);
buffer[len] = '\0';
qsort(buffer,len,1,cmp);
lua_pushlstring(L,buffer,len);
return 1;
}
/***********************************/
int luaopen_sortword(lua_State *L)
{
lua_register(L,"sortword",sortword);
return 0;
}
```

And now it only takes 2.2 seconds. Not a bad return upon investment.

Now we turn to Perl:

>
```
#!/usr/bin/perl
use strict;
#***************************
sub sortword
{
my $str = shift;
my @t = unpack("(C)*",$str);
@t = sort(@t);
return pack("(C)*",@t);
}
#*******************************
if (scalar(@ARGV) == 0)
{
printf(STDERR "usage: %s word\n",$0);
exit(1);
}
my $word = sortword(uc($ARGV[0]));
open(DICT,"/usr/share/dict/words")
or die("Houston, we have a problem ... \n");
while(my $line = <DICT>)
{
chop $line;
my $dictsort = sortword(uc($line));
if ($dictsort eq $word)
{
print $line . "\n";
}
}
close(DICT);
exit(0);
```

Again, we need to pull apart the string into an array, sort that, and recreate the string. And when we run this lovely bit of Perl code it only takes 10.5 seconds.

Wow.

And here I thought Perl was the text crunching Master of the Universe.

Guess not.

Well, the Perl version of sortword() could be tightened up a bit …

>
```
sub sortword
{
return pack("(C)*",sort(unpack("(C)*",shift)));
}
```

That takes us down to 8.6 seconds—nearly two full seconds to shuffle data between variables. Perl isn't looking all that good. Well, there is the function overhead, and given that we got sortword() down to one line, maybe if we inline it we'll get an improvement.

And we do—it's not 7.2 seconds. We finally beat unoptimized Lua code, but now with a more cryptic Perl version.

Way to go, Perl!

But given that Lua was easy to extend with C, can we do the same with Perl? A bit of searching lead me to the Inline::C Perl Module [7]. Ten minutes to download and install (much better than I expected) and if anything, it was easier to embed C in Perl than in Lua:

>
```
#!/usr/bin/perl
use Inline C;
use strict;
# same code as above, minus the sortword() subroutine
exit(0);
__END__
__C__
static int cmp(const void *l,const void *r)
{
int cl;
int cr;
cl = *(const char *)l;
cr = *(const char *)r;
return cl - cr;
}
char *sortword(char *word)
{
size_t len;
len = strlen(word);
qsort(word,len,1,cmp);
return word;
}
```

And we're down to 2.1 seconds. Just a bit quicker than the Lua/C version.

Well, that's after the 4.1 second initial run where it dumped a bunch of directories and files (lovely that).

Lua appears to be a bit faster than Perl, or at the very least, as fast as Perl.

And everything (even, quite possibly, Ruby) is faster than my own homebrewed language … sigh.

[1] http://wlofie.dyndns.org/

[2] /boston/2007/01/23.1

[3] http://forth.org/

[4] http://www.theregister.co.uk/2008/04/21/bray_ruby_rails/

[5] http://www.lua.org/

[6] /boston/2008/06/27.1

[7] http://search.cpan.org/~sisyphus/Inline-0.45/C/C.pod

Gemini Mention this post

Contact the author