Comparazione linguaggi script per la geometria frattale

23
ago/09
5

In questo articolo metterò a confronto le ultime incarnazioni di ruby, con le ultime di python, groovy, php, lua, perl e anche java, per avere un metro di paragone con un linguaggio precompilato. Vedremo, infatti, come si comportano i linguaggi di script applicati alla geometria frattale, più precisamente un algoritmo della famiglia di Mandelbrot.
Navigando, ho trovato un confronto molto interessante ma un pò datato, risale a più di due anni fa. Da allora sono cambiate un pò di cose e ne ho approfittato per fare un aggiornamento anche se non includerò tutti quei linguaggi molti dei quali semi sconosciuti. Questa è l’occasione per mettere a confronto ruby e python anche nelle loro versioni java e .net, un intenzione che avevo già da un pò di tempo.

Usare un frattale come benchmark è oltretutto comodo: se un tentativo di ottimizzazione non va a buon fine se ne ha subito l’evidenza ed essendo disegnato in tempo reale, si riesce a percepire la velocità di esecuzione. Il frattale è disegnato in ascii anche perchè l’utilizzo di librerie esterne ne avrebbe drogato l’esito.

                                       *
                                       *
                                       *
                                       *
                                       *
                                      ***
                                     *****
                                     *****
                                      ***
                                       *
                                   *********
                                 *************
                                ***************
                             *********************
                             *********************
                              *******************
                              *******************
                              *******************
                              *******************
                            ***********************
                              *******************
                              *******************
                             *********************
                              *******************
                              *******************
                               *****************
                                ***************
                                 *************
                                   *********
                                       *
                                ***************
                            ***********************
                         * ************************* *
                         *****************************
                      * ******************************* *
                       *********************************
                      ***********************************
                    ***************************************
               *** ***************************************** ***
               *************************************************
                ***********************************************
                 *********************************************
                 *********************************************
                ***********************************************
                ***********************************************
              ***************************************************
               *************************************************
               *************************************************
              ***************************************************
              ***************************************************
         *    ***************************************************    *
       *****  ***************************************************  *****
       ****** *************************************************** ******
      ******* *************************************************** *******
    ***********************************************************************
    ********* *************************************************** *********
       ****** *************************************************** ******
       *****  ***************************************************  *****
              ***************************************************
              ***************************************************
              ***************************************************
              ***************************************************
               *************************************************
               *************************************************
              ***************************************************
                ***********************************************
                ***********************************************
                  *******************************************
                   *****************************************
                 *********************************************
                **** ****************** ****************** ****
                 ***  ****************   ****************  ***
                  *    **************     **************    *
                         ***********       ***********
                         **  *****           *****  **
                          *   *                 *   *

Questi sono i dati del sistema:
Dell Inspiron 9400, Centrino Duo, T7200 @ 2Ghz 4Mb Cache L1, Ram 2Gb @ 667Mhz
Windows XP pro SP3
Java 6 update 15
Microsoft .NET 3.5 SP1

Questi sono i risultati prestazionali ottenuti da una media di cinque rilevazioni catturate dopo qualche tentativo (non mi sono fidato dello startup delle VM):

Linguaggio      Tempo (in secondi)  n più lento di java
_____________________________________________________________
Java 6 update 15    0,153
Lua 5.1.4           0,815	           5x
Php 5.3.0           2,083	          14x
Python 2.6.2        2,269 	          15x
Python 3.1.1        1,566 	          10x
Jython 2.5.0        2,850 	          19x
Jruby 1.3.1         2,466 	          16x
Groovy 1.6.3        6,491 	          42x
Ruby 1.9.1 p129	    2,688 	          18x
Ruby 1.8.6 p368	    6,863 	          45x
Ruby 1.8.6 p111	    9,709 	          63x
IronRuby 0.9.0	    6,038 	          39x
IronPyhon 2.0.2     0,978 	           6x
Perl 5.10.0         2,722 	          18x

Questo è il grafico, naturalmente valori più bassi indicano una prestazione migliore

Chart

Questi sono gli script usati per generare il frattale, andavano benissimo quelli di Erik Wrenholt, mi sono limitato a qualche semplice modifica per far funzionare python 3.1 o per migliorare lievemente la già ottima leggibilità in ruby e lua.

Java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
// by Erik Wrenholt
import java.util.*;
 
class Bench1
{  
	static int BAILOUT = 16;
	static int MAX_ITERATIONS = 1000;
 
	private static int iterate(float x, float y)
	{
		float cr = y-0.5f;
		float ci = x;
		float zi = 0.0f;
		float zr = 0.0f;
		int i = 0;
		while (true) {
			i++;
			float temp = zr * zi;
			float zr2 = zr * zr;
			float zi2 = zi * zi;
			zr = zr2 - zi2 + cr;
			zi = temp + temp + ci;
			if (zi2 + zr2 > BAILOUT)
				return i;
			if (i > MAX_ITERATIONS)
				return 0;
		}
	}
 
	public static void main(String args[])
	{
		Date d1 = new Date();
		int x,y;
		for (y = -39; y < 39; y++) {
			System.out.print("\n");
			for (x = -39; x < 39; x++) {
				if (iterate(x/40.0f,y/40.0f) == 0) 
					System.out.print("*");
				else
					System.out.print(" ");
			}
		}
		Date d2 = new Date();
		long diff = d2.getTime() - d1.getTime();
		System.out.println("\nJava Elapsed " + diff/1000.0f);
 
	}
}

Lua

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
-- By Erik Wrenholt
 
local BAILOUT = 16
local MAX_ITERATIONS = 1000
 
function iterate(x,y)
 
  local cr = y-0.5
  local ci = x
  local zi = 0.0
  local zr = 0.0
  local i = 0
 
  while 1 do
    i = i+1
    local temp = zr * zi
    local zr2 = zr*zr
    local zi2 = zi*zi
    zr = zr2-zi2+cr
    zi = temp+temp+ci
    if (zi2+zr2 > BAILOUT) then
      return i
    end
    if (i > MAX_ITERATIONS) then
      return 0
    end
  end
end
 
function bench1()
  local t = os.clock()
  for y = -39, 38 do
    for x = -39, 38 do
    if (iterate(x/40.0, y/40) == 0) then io.write("*") else io.write(" ") end
    end
    io.write("\n")
  end
  io.write(string.format("Time Elapsed %.3fn", os.clock() - t))
end
 
bench1()

Php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
<?php
define("BAILOUT",16);
define("MAX_ITERATIONS",1000);
 
class Bench1
{
 
	function Bench1()
	{
		$d1 = microtime(1);
		for ($y = -39; $y < 39; $y++) {
			echo("\n");
			for ($x = -39; $x < 39; $x++) {
				if ($this->iterate($x/40.0,$y/40.0) == 0) 
					echo("*");
				else
					echo(" ");
			}
		}
		$d2 = microtime(1);
		$diff = $d2 - $d1;
		printf("\nPHP Elapsed %0.3f", $diff);
	}
 
	function iterate($x,$y)
	{
		$cr = $y-0.5;
		$ci = $x;
		$zi = 0.0;
		$zr = 0.0;
		$i = 0;
		while (true) {
			$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;
		}
	}
}
 
new Bench1();
?>

Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import sys, time
stdout = sys.stdout
 
BAILOUT = 16
MAX_ITERATIONS = 1000
 
class Bench1:
  def __init__(self):
    print ('Rendering...')
    for y in range(-39, 39):
      stdout.write('n')
      for x in range(-39, 39):
        i = self.start(x/40.0, y/40.0)
 
        if i == 0:
          stdout.write('*')
        else:
          stdout.write(' ')
 
  def start(self, x, y):
    cr = y - 0.5
    ci = x
    zi = zr = 0.0
    i = 0
 
    while True:
      i += 1
      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
 
t = time.time()
Bench1()
print ('\nPython Elapsed %.3f' % (time.time() - t))

Groovy

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
//Created By Marco Mastrodonato 22/09/2009
 
class Bench1{
    public int BAILOUT = 16
    public int MAX_ITERATIONS = 1000
 
    def Bench1(){
        println("Rendering...")
        for (y in -39..39){
            println("")
            for (x in -39..39){
                if (iterate(x/40.0, y/40.0) == 0){
                    print("*")
                } else {
                    print(" ")
                }
            }
        }
    }
 
    def iterate(x,y){
        float cr = y-0.5
        float ci = x
        float zi = 0.0
        float zr = 0.0
        def i = 0
        while(1){
            i += 1
            float temp = zr * zi
            float zr2 = zr * zr
            float zi2 = zi * zi
            zr = zr2 - zi2 + cr
            zi = temp + temp + ci
            if (zi2 + zr2 > BAILOUT){ 
                return i
            }
            if (i > MAX_ITERATIONS){ 
                return 0
            } 
        }
    }
 
}
 
time1 = new Date().time
new Bench1()
time2 = new Date().time
float elapsed = (time2 - time1)/1000
println("\nGroovy Elapsed ${elapsed}")

Ruby

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
BAILOUT = 16
MAX_ITERATIONS = 1000
 
class Bench1
 
  def initialize
    puts "Rendering..."
    for y in -39..39
      print "\n"
      for x in -39..39
        i = iterate x/40.0, y/40.0
        if i == 0 then print "*" else print " " end
      end
    end
  end
 
  def iterate(x,y)
    cr = y-0.5
    ci = x
    zi = zr = 0.0
    i = 0
    while true
      i += 1
      temp = zr * zi
      zr2 = zr * zr
      zi2 = zi * zi
      zr = zr2 - zi2 + cr
      zi = temp + temp + ci
      return i if zi2 + zr2 > BAILOUT
      return 0 if i > MAX_ITERATIONS
    end
  end
end
 
time = Time.now
Bench1.new
puts "\nRuby Elapsed %.3f" % (Time.now - time)

Perl

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
# Ported from C to Perl by Anders Bergh <anders1@gmail.com>
 
$BAILOUT=16;
$MAX_ITERATIONS=1000;
 
$begin = time();
 
sub mandelbrot {
       local $x = $_[0];
       local $y = $_[1];
 
       local $cr = $y - 0.5;
       local $ci = $x;
       local $zi = 0.0;
       local $zr = 0.0;
       local $i = 0;
 
       while (1)
       {
               $i = $i + 1;
               local $temp = $zr * $zi;
               local $zr2 = $zr * $zr;
               local $zi2 = $zi * $zi;
               $zr = $zr2 - $zi2 + $cr;
               $zi = $temp + $temp + $ci;
               if ($zi2 + $zr2 > $BAILOUT)
               {
                       return $i;
               }
               if ($i > $MAX_ITERATIONS)
               {
                       return 0;
               }
       }
}
 
for ($y = -39; $y < 39; $y++)
{
       print("\n");
       for ($x = -39; $x < 39; $x++)
       {
               $i = mandelbrot($x/40.0, $y/40.0);
               if ($i == 0)
               {
                       print("*");
               }
               else
               {
                       print(" ");
               }
       }
}
print("\n");
 
$end = time() - $begin;
 
printf ("Perl Elapsed %.3fn",$end);

Commenti:

La velocità di Lua è ormai nota, solamente 5 volte più lento rispetto al codice java compilato, il miglior risultato. La sua semplicità è la sua forza, forse è proprio questo che lo rende così veloce? E’ stato adottato dalla Blizzard all’interno del gioco World of Warcraft e se l’hanno fatto loro un motivo ci sarà. Non è ad oggetti o meglio, nativamente non li supporta anche se c’è il progetto LOOP che lo estende a questo modello di programmazione.
Php e Perl non hanno bisogno di commenti.
Tra le versioni C di Ruby e Python è chiaramente avanti quest’ultimo. Il paragone equo sarebbe la Rb1.8.6 con la Py2.6.2 e la Rb1.9.1 con la Py3.1.1.
La sfida tra le versioni che sfruttalo la Java VM: Groovy, Jython e JRuby, vede in vantaggio quest’ultimo. Groovy è molto indietro come prestazioni ma il dubbio più grosso che ho è: ma a chi è destinato? Come sintassi non è male ma ruby è ancora più scorrevole e poi ha quel rake che è tanto comodo per tante cose.
Le versioni ruby e python per .Net vede incredibilmente avanti IronPython! Ma che gli hanno messo dentro, la dinamite? Sarà molto interessante esaminare il nuovo framework MVC di ASP.NET da poco arrivato alla versione 1 e che sarà incluso nel framework .Net 4, esistono progetti sia per IronRuby che per IronPython.
Se questo articolo è risultato interessante, forse potrai trovare qualcos’altro tra gli annunci del mio sponsor, si trova nella colonna a destra, grazie!

Commenti (5) Trackback (0)
  1. Michele
    01:26 on 30 agosto 2009

    Un consiglio, ordina i risultati in base alle prestazioni, renderebbe più chiaro il risultato.

    Ciao Michele.

  2. Marco Mastrodonato
    14:27 on 01 settembre 2009

    Ciao Michele,
    ho scelto questo tipo di ordinamento per avere un’evidenza del confronto in base alla relazione con la propria piattaforma es. jruby, jython e groovy, tra i due iron ecc.

  3. Fabio
    04:55 on 12 settembre 2009

    Ciao Marco, secondo me potrebbe avere senso includere anche una versione in Scala (http://www.scala-lang.org), un linguaggio multi-purpose basato sulla JVM e utilizzabile anche per scripting che sta guadagnando molti consensi in questo periodo. Teoricamente le prestazioni dovrebbero essere allineate a quelle di Java, sarebbe interessante verificarlo.

  4. Michele
    15:01 on 21 settembre 2009

    Beh, se volessimo considerare altri linguaggi, forse prima di scala, ci aggiungerei c e c++.
    Già Java e i vari Iron* j* sono comunque compilati/precompilati, quindi fuori dal contesto script… Aggiungere un altro linguaggio compilato non so se valga la pena, ma questo è ovviamente a discrezione dell’autore.
    P.S. se vuoi provare l’ebrezza di testare di tutto di più, su questo sito trovi un centinaio di linguaggi
    http://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

    Ciao Michele.

  5. Marco Mastrodonato
    14:33 on 30 settembre 2009

    @Fabio
    Ciao Fabio, mi piacerebbe includere una versione di scala, è solo un problema di tempo.

    @Michele
    Ciao Michele, solo java è stato eseguito da bytecode e solo per avere un metro, tutti gli altri da script. C++, come puoi immaginare e come si vede dalla vecchia comparazione, è chiaramente più veloce ma non l’ho incluso perchè non rientrava nello scopo. Grazie per la lista.

Lascia un commento

Ancora nessun trackback.