Home | Fractal Benchmark

Content

Introduction

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 program

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).

Results

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).

CPU time t
in seconds
log2
of t/tmin
Language
0.310.00Pascal FPC 2.2.2
0.390.34Common Lisp CMUCL 19f compiled
0.410.41C GCC 4.3.2
0.540.81Fortran 95 GCC 4.3.2
0.540.82Fortran 95 G95 0.92
1.151.90Objective Caml 3.10.2 compiled
1.942.66Lua 5.1.4
2.302.91Pike 7.8.116
2.903.24Mercury 0.13.1 GCC 4.3.2
3.243.40Java OpenJDK/IcedTea 6
3.773.62Haskell GHC 6.10.2 compiled
4.073.73Maple 10 compiled GCC 4.3.2
4.553.89Objective Caml 3.10.2 bytecode
4.823.97Oz Mozart 1.4.0
4.964.01Java GCJ 4.3.2 compiled
8.744.83GNU AWK 3.1.6
8.934.86Scheme 48 1.8
8.964.87Erlang R12B5
9.194.91PHP 5.2.6
9.224.91Java GCJ/GIJ 4.3.2
9.514.95Axiom jan2009
10.095.04Tcl 8.5.5
10.485.09newLISP 10.0.2
11.855.27Python 2.6.0
11.885.28GNU Sather 1.2.3 GCC 4.3.2
12.205.31JavaScript SpiderMonkey 1.7.0
13.975.51Perl 5.10.0 optimized
14.225.54PostScript Ghostscript 8.62
15.015.61Haskell jhc 0.6.0 GCC 4.3.2
18.765.93Gobo Eiffel 3.9
20.736.08SWI-Prolog 5.6.59
20.826.09Perl 5.10.0
28.716.55Ruby 1.8.7
29.646.59Cobra 0.8.0
33.506.77Scheme Guile 1.8.5
33.536.77Emacs 22.3 Lisp
34.486.81Common Lisp CMUCL 19f bytecode
36.836.91Mathematica 5.2 compiled
37.916.95Maxima 5.17.1 CMUCL 19f compiled
43.167.14GAP 4
46.217.24Haskell GHC 6.10.2 bytecode
50.627.37Eiffel tecomp 0.17
69.127.82GNU bc 1.06
82.238.07Haskell Hugs 98
85.218.12Mathematica 5.2
88.288.17Scilab 5.1
122.258.64Common Lisp CMUCL 19f
124.078.66KornShell 93t
168.099.10XSLT libxslt 1.1.24
213.019.44dash 0.5.5.1
223.499.51Maple 10
234.419.58Maxima 5.17.0 CLISP 2.44.1 compiled
242.199.63Octave 3.0.3
264.449.75Z shell 4.3.6
273.089.80Io 20080330
317.3110.02Yacas 1.2.2 core
331.7910.08Singular 3.0.4
344.1910.13Maxima 5.17.1 CMUCL 19f
584.0210.90Yacas 1.2.2
708.1711.17bash 3.2
2185.9612.80Maxima 5.17.0 CLISP 2.44.1
2389.4012.93tcsh 6.15.00
220000.0019.45cmd.exe Windows XP VirtualBox 2.0.6

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.060.00C GCC 4.3.2
0.070.13GNU Sather 1.2.3 GCC 4.3.2
0.080.34Haskell jhc 0.6.0 GCC 4.3.2
0.080.36Mercury 0.13.1 GCC 4.3.2
0.080.40Fortran 95 G95 0.92
0.090.51Gobo Eiffel 3.9
0.100.73Pascal FPC 2.2.2
0.110.84Objective Caml 3.10.2 compiled
0.151.31Fortran 95 GCC 4.3.2
0.221.86Common Lisp CMUCL 19f compiled
0.241.97Haskell GHC 6.10.2 compiled
0.292.28Java GCJ 4.3.2 compiled
0.753.63Java OpenJDK/IcedTea 6
4.566.24Oz Mozart 1.4.0
5.866.60Java GCJ/GIJ 4.3.2

Gallery

These are the most bizarre examples.

tcsh

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

cmd.exe for Windows XP

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

XSL Transformations

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 &lt;= $BAILOUT and $i &lt;= $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 &lt; 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 &lt; 39">
   <xsl:value-of select="'&#10;'"/>
   <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>

Other projects about programming languages


Last modification date: 2018-05-17
Theo Wollenleben (alpha0x89 yahoo de)
Free Web Hosting