Home | Fractal Benchmark
This is an extension of the (non-scientific) Fractal Benchmark project by Erik Wrenholt. The idea is to implement the same program in different scripting languages and compare their execution speed. It is a simple version of The Computer Language Benchmarks Game, which continues The Great Computer Language Shootout by Doug Bagley. A mirror of Doug's project can be viewed in the Internet Archive. See the link section for other similar projects.
These are the languages from Erik's web page (arbitrarily classified):
The only program I can't test on my Linux PC is the AppleScript program. I omit the vectorized Io version by Steve Dekorte because I think its structure is too different.
It follows a list of the languages I have added. I'm an adept programmer in only a subset (probably the empty set) of these languages. If you observe my coding technique to be blatantly stupid in one of the programs you may try to contact me. If I won't answer I might have temporarily no time for this project or have completely lost interest in this kind of geekiness or be deceased. The probability of one these alternatives to be true increases with the time span since the last modification of these pages.
The following programs can be found on the Internet:
The Haskell, Cobra and Erlang versions have been included in the benchmark. The K version version cannot be run because the 'q' Linux executable by Kx Systems fails with an "Illegal instruction" on my Athlon Thunderbird. There's also a Ruby vs. JRuby comparison by madlep. Dedi Eko Cahyono included Rubinius. The Python program has been tested with Psycho by Paddy3118.
An archive file containing this project is available for download. The programs that run on my computer can also be viewed on a single web page, to which every language is linked from the result table.
The reference implementation is written in C. This is the AWK version:
#!/bin/gawk -f
# by Erik Wrenholt, ported from C to GNU AWK 3.1.6 by Theo Wollenleben
function mandelbrot(x, y)
{
cr = y - 0.5
ci = x
zi = 0.0
zr = 0.0
i = 0
while(1) {
i ++
temp = zr * zi
zr2 = zr * zr
zi2 = zi * zi
zr = zr2 - zi2 + cr
zi = temp + temp + ci
if (zi2 + zr2 > BAILOUT)
return i
if (i > MAX_ITERATIONS)
return 0
}
}
BEGIN {
BAILOUT = 16
MAX_ITERATIONS = 1000
init_time=systime()
for (y = -39; y < 39; y++) {
printf("\n")
for (x = -39; x < 39; x++) {
i = mandelbrot(x/40.0, y/40.0)
if (i==0)
printf("*")
else
printf(" ")
}
}
printf("\n")
query_time = systime() - init_time
printf("GAWK Elapsed %d\n", query_time)
exit
}
The output is a fractal ASCII image of the Mandelbrot set. I also wrote a concise Haskell version with slightly different edge conditions. It is less efficient than the benchmarked Haskell version, but it shows more clearly what is happening:
-- by Theo Wollenleben
-- based on the Fractal Benschmark program by Erik Wrenholt
module Main where
import Data.Complex
import Control.Monad
import Text.Printf
bailout = 4
maxIterations = 1000
i = 0 :+ 1
iterate_at c (i, z) = (succ i, z^2 + c)
with_initial_values = (0, 0)
break_condition_is_true (i, z) = (magnitude z) > bailout || i > maxIterations
mandelbrot c =
let (_, z) = until break_condition_is_true
(iterate_at c)
with_initial_values
in (magnitude z)
main = do
forM [-39..38] (\y -> do
forM [-39..38] (\x -> do
let c = ((fromIntegral y) + (fromIntegral x)*i)/40 - 1/2
if mandelbrot c > bailout
then putStr " "
else putStr "*")
putStr "\n")
This program can be compiled with GHC. Some type declarations are necessary to make it run with Hugs (see the file Mandelbrot-complex.hs contained in the project download).
Here are some results from running the programs on my computer. I don't make use of the time measured by the program (it is wall clock time in some cases). Instead I use the bash built-in 'time' to measure the CPU time, including compile or startup time. There are three exceptions: For Axiom and Maple 'time' reports a wrong (very small) value and the cmd.exe program can't be started from the shell. I have chosen a logarithmic scale for relative speed (one unit more means twice as much running time).
Remarks:
The above table includes time needed for compilation. In the following table only the time for running the compiled files are taken into account:
CPU time t in seconds | log2 of t/tmin | Language |
---|---|---|
0.06 | 0.00 | C GCC 4.3.2 |
0.07 | 0.13 | GNU Sather 1.2.3 GCC 4.3.2 |
0.08 | 0.34 | Haskell jhc 0.6.0 GCC 4.3.2 |
0.08 | 0.36 | Mercury 0.13.1 GCC 4.3.2 |
0.08 | 0.40 | Fortran 95 G95 0.92 |
0.09 | 0.51 | Gobo Eiffel 3.9 |
0.10 | 0.73 | Pascal FPC 2.2.2 |
0.11 | 0.84 | Objective Caml 3.10.2 compiled |
0.15 | 1.31 | Fortran 95 GCC 4.3.2 |
0.22 | 1.86 | Common Lisp CMUCL 19f compiled |
0.24 | 1.97 | Haskell GHC 6.10.2 compiled |
0.29 | 2.28 | Java GCJ 4.3.2 compiled |
0.75 | 3.63 | Java OpenJDK/IcedTea 6 |
4.56 | 6.24 | Oz Mozart 1.4.0 |
5.86 | 6.60 | Java GCJ/GIJ 4.3.2 |
These are the most bizarre examples.
Tcsh is compatible to the Berkeley UNIX C shell, which is not very suited for writing scripts. It is not possible to define functions, which can be emulated by aliases.
#!/usr/bin/tcsh
# by Erik Wrenholt, ported from C to tcsh 6.15.00 by Theo Wollenleben
# 32 bit signed integer arithmetic, output differs slightly
@ SCALE = (1 << 24) SCALE2 = (1 << 12)
@ BAILOUT = 16 * $SCALE
set MAX_ITERATIONS = 1000
alias mandelbrot 'set x = \!:1 set y = \!:2 \
\
@ cr = $y - $SCALE / 2 \
set ci = $x \
set zi = 0 \
set zr = 0 \
set i = 0 \
\
while (1) \
@ i ++ \
@ temp = ($zr / $SCALE2) * ($zi / $SCALE2) \
@ zr2 = ($zr / $SCALE2) * ($zr / $SCALE2) \
@ zi2 = ($zi / $SCALE2) * ($zi / $SCALE2) \
@ zr = ($zr2 - $zi2) + $cr \
@ zi = ($temp + $temp) + $ci \
if ($zi2 + $zr2 > $BAILOUT) then \
echo $i; break \
endif \
if ($i > $MAX_ITERATIONS) then \
echo 0; break \
endif \
end'
set y = -39
while ($y < 39)
echo
set x = -39
while ($x < 39)
@ arg_x = ($x * $SCALE) / 40
@ arg_y = ($y * $SCALE) / 40
set i = `mandelbrot $arg_x $arg_y`
if ($i == 0) then
echo -n "*"
else
echo -n " "
endif
@ x ++
end
@ y ++
end
echo
set time = ( 0 "%U+%S" )
echo -n "tcsh Elapsed "
time
Subroutines are called by the CALL command similar to the GOTO command. GOTO:eof terminates a subroutine. To get intermediate values of variables in loops (using !variable!) the EnableDelayedExpansion option is needed. Printing without newline is done by a trick using the SET command.
@echo off
rem by Erik Wrenholt, ported from C to Windows XP cmd.exe by Theo Wollenleben
setlocal EnableDelayedExpansion
rem 32 bit signed integer precision, output differs slightly
set /a SCALE="1<<24"
set /a SCALE2="1<<12"
set /a BAILOUT = 16 * SCALE
set MAX_ITERATIONS=1000
call:main & endlocal & goto:eof
rem wall clock time in seconds
:wall_time
for /f "tokens=3-5 delims=:. " %%a in ('echo.^|time') do (
set /a %1 = ^(%%a*60+%%b^)*60+%%c & goto:eof)
goto:eof
:mandelbrot
set x=%2 & set y=%3
set /a cr = y - SCALE / 2
set /a ci = x
set zi=0
set zr=0
set i=0
:while
set /a i += 1
set /a temp = (zr / SCALE2) * (zi / SCALE2)
set /a zr2 = (zr / SCALE2) * (zr / SCALE2)
set /a zi2 = (zi / SCALE2) * (zi / SCALE2)
set /a zr = zr2 - zi2 + cr
set /a zi = temp + temp + ci
set /a r2 = zi2 + zr2
if %r2% gtr %BAILOUT% (
set /a %1 = %i% & goto:eof)
if %i% gtr %MAX_ITERATIONS% (
set /a %1 = 0 & goto:eof)
goto while
goto:eof
:main
call:wall_time init_time
for /l %%y in (-39, 1, 38) do (
echo.
for /l %%x in (-39, 1, 38) do (
set /a arg_x = %%x * SCALE / 40
set /a arg_y = %%y * SCALE / 40
call:mandelbrot i !arg_x! !arg_y!
if !i! equ 0 (
<nul set/p output="*"
) else (
<nul set/p output= )
)
)
echo.
call:wall_time final_time
set /a query_time = final_time - init_time
echo cmd Elapsed %query_time%
goto:eof
XSLT is not a procedural language. It has no variables and no built-in loops. That's why the following code looks quite different from the others. Loops can be realized with recursions. I haven't used existing XSLT extensions for loops and functions. I'm not a real XSLT programmer, so there is probably room for improvement.
<?xml version="1.0" encoding="utf-8"?>
<!-- echo '<x/>'|xsltproc Mandelbrot.xslt - -->
<!-- by Erik Wrenholt, ported from C to XSLT by Theo Wollenleben -->
<!-- no time functions in XSLT -->
<!-- there is a 'timing' option for xsltproc, see also http://exslt.org/date/ -->
<!-- no output until xsltproc has finished -->
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="no"/>
<xsl:variable name="BAILOUT" select="16"/>
<xsl:variable name="MAX_ITERATIONS" select="1000"/>
<xsl:template name="while">
<xsl:param name="i"/>
<xsl:param name="temp"/>
<xsl:param name="zr2"/>
<xsl:param name="zi2"/>
<xsl:param name="cr"/>
<xsl:param name="ci"/>
<xsl:if test="$zi2 + $zr2 <= $BAILOUT and $i <= $MAX_ITERATIONS">
<xsl:call-template name="mandelbrot">
<xsl:with-param name="cr" select="$cr"/>
<xsl:with-param name="ci" select="$ci"/>
<xsl:with-param name="zr" select="$zr2 - $zi2 + $cr"/>
<xsl:with-param name="zi" select="$temp + $temp + $ci"/>
<xsl:with-param name="i" select="$i"/>
</xsl:call-template>
</xsl:if>
<xsl:if test="$zi2 + $zr2 > $BAILOUT">
<xsl:text> </xsl:text>
</xsl:if>
<xsl:if test="$i > $MAX_ITERATIONS">
<xsl:text>*</xsl:text>
</xsl:if>
</xsl:template>
<xsl:template name="mandelbrot">
<xsl:param name="cr"/>
<xsl:param name="ci"/>
<xsl:param name="zr"/>
<xsl:param name="zi"/>
<xsl:param name="i"/>
<xsl:call-template name="while">
<xsl:with-param name="i" select="$i + 1"/>
<xsl:with-param name="temp" select="$zr * $zi"/>
<xsl:with-param name="zr2" select="$zr * $zr"/>
<xsl:with-param name="zi2" select="$zi * $zi"/>
<xsl:with-param name="cr" select="$cr"/>
<xsl:with-param name="ci" select="$ci"/>
</xsl:call-template>
</xsl:template>
<xsl:template name="for_x">
<xsl:param name="x"/>
<xsl:param name="y"/>
<xsl:if test="$x < 39">
<xsl:call-template name="mandelbrot">
<xsl:with-param name="cr" select="$y div 40 - 0.5"/>
<xsl:with-param name="ci" select="$x div 40"/>
<xsl:with-param name="zr" select="0.0"/>
<xsl:with-param name="zi" select="0.0"/>
<xsl:with-param name="i" select="0"/>
</xsl:call-template>
<xsl:call-template name="for_x">
<xsl:with-param name="x" select="$x + 1"/>
<xsl:with-param name="y" select="$y"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template name="for_y">
<xsl:param name="y"/>
<xsl:if test="$y < 39">
<xsl:value-of select="' '"/>
<xsl:call-template name="for_x">
<xsl:with-param name="x" select="-39"/>
<xsl:with-param name="y" select="$y"/>
</xsl:call-template>
<xsl:call-template name="for_y">
<xsl:with-param name="y" select="$y + 1"/>
</xsl:call-template>
</xsl:if>
</xsl:template>
<xsl:template match="/">
<xsl:call-template name="for_y">
<xsl:with-param name="y" select="-39"/>
</xsl:call-template>
</xsl:template>
</xsl:stylesheet>