J'essaie d'évaluer un calcul de Monte Carlo de PI (3.14159) en lançant des fléchettes. J'ai implémenté mon code en Java, Groovy, BeanShell, Julia, Jython et Python (Python2 implémenté en C).
Voici mon code Java original "MonteCarloPI.java" :
import java.util.Random;
public class MonteCarloPI {
public static void main(String[] args)
{
int nThrows = 0;
int nSuccess = 0;
double x, y;
long then = System.nanoTime();
int events=(int)1e8;
Random r = new Random();
for (int i = 0; i < events; i++) {
x = r.nextFloat(); // Throw a dart
y = r.nextFloat();
nThrows++;
if ( x*x + y*y <= 1 ) nSuccess++;
}
double itime = ((System.nanoTime() - then)/1e9);
System.out.println("Time for calculations (sec): " + itime+"\n");
System.out.println("Pi = " + 4*(double)nSuccess/(double)nThrows +"\n");
}
}
Voici mon code Groovy dans le fichier "MonteCarloPI.groovy" :
import java.util.Random
int nThrows = 0
int nSuccess = 0
double x, y
long then = System.nanoTime()
int events=1e8
r = new Random()
for (int i = 0; i < events; i++) {
x = r.nextFloat() // Throw a dart
y = r.nextFloat()
nThrows++
if ( x*x + y*y <= 1 ) nSuccess++
}
itime = ((System.nanoTime() - then)/1e9)
System.out.println("Time for calculations (sec): " + itime+"\n")
System.out.println("Pi = " + 4*(double)nSuccess/(double)nThrows +"\n")
Alternativement, j'ai supprimé les définitions telles que "float" et "int" (c'est-à-dire les types libres). Ceci permet de vérifier les performances en utilisant des types "loose".
J'ai renommé "MonteCarloPI.groovy" en un fichier BeanShell script "MonteCarloPI.bsh" (BeanShell a une syntaxe très similaire à Groovy)
Dans le cas du langage Python standard, le code "MonteCarloPI_CPython.py" ressemble à ceci :
import random,time
nThrows,nSuccess = 0,0
then = time.time()
events=int(1e8)
for i in xrange(events):
x,y = random.random(),random.random(); # Throw a dart
nThrows +=1
if ( x*x + y*y <= 1 ): nSuccess+=1
itime = time.time() - then
print ("Time: ",itime,"sec Pi = ",4*nSuccess/float(nThrows))
Ce code est exécuté soit en CPython 2.7.18 (Python implémenté en C), soit en Jython 2.7.2 (implémentation Java). Pour Python 3.8.3 ("Python3"), remplacez "xrange" par "range".
J'ai également implémenté le même algorithme dans JRuby (MonteCarloPI.rb) :
require "java"
java_import java.lang.System;
java_import java.util.Random;
nThrows = 0; nSuccess = 0;
xthen = System.nanoTime();
events=1e8;
r = Random.new();
for i in 0 .. events do
x = r.nextFloat(); # Throw a dart
y = r.nextFloat();
nThrows +=1
if ( x*x + y*y <= 1 )
nSuccess += 1
end
end
itime = (System.nanoTime() - xthen)/1e9;
xpi=(4.0*nSuccess)/nThrows
puts "Time for calculations (sec): #{itime}"
puts "Pi = #{xpi}"
Voici le code utilisant Julia :
using Random
nThrows = 0
nSuccess = 0
events=1e8
then = time()
for j in 0:events
x = rand(); # Throw a dart
y = rand();
global nThrows += 1;
if x*x + y*y <= 1
global nSuccess += 1;
end
end
itime = time() - then
println( "Time for calculations (sec):", itime, " sec")
println( "Pi = ", 4.0*nSuccess/float(nThrows) )
J'ai exécuté "MonteCarloPI.java", "MonteCarloPI.groovy", "MonteCarloPI.py", "MonteCarloPI.bsh" et MonteCarloPI.rb à l'intérieur. DataMelt éditeur. Le code julia a été traité en utilisant julia-1.5.0/bin installé localement.
Voici les résultats du benchmark sur un CPU Intel(R) Core(TM) i5-4690K @ 3.50GHz (ubuntu 20.04, 8 GB de mémoire), avec 2048 MB alloués pour JDK9 lors de l'exécution de code Groovy, Jython, BeanShell :
Java code: 1.7 sec Pi = 3.14176584 -> executed in DataMelt/JDK9
Groovy code: 2.1 sec Pi = 3.14144832 -> executed in DataMelt/JDK9
Groovy code: 18 sec Pi = 3.14141132 -> same but with "loose" types
Julia code: 15 sec Pi = 3.14156104 -> executed in julia-1.5.0
Python code: 24 sec Pi = 3.14188036 -> executed in CPython 2.7.18
Python code: 30 sec Pi = 3.14188230 -> executed in CPython 3.2.8
Python code: 3 sec Pi = 3.14188036 -> executed using PyPy
Jython code: 24 sec Pi = 3.14187860 -> executed in DataMelt/JDK9
JRuby code: 25 sec Pi = 3.14187860 -> executed in DataMelt/JDK9
BeanShell code: takes forever?! -> executed in DataMelt/JDK9
Comme vous pouvez le constater, les calculs Java et Groovy prennent à peu près le même temps (environ 2 secondes). Avec des types lâches dans Groovy, l'exécution est 9 fois plus lente. Python est un facteur 12 plus lent que Java et Groovy. Python3 est encore plus lent. JRuby est aussi lent que Python. PyPy est plutôt rapide (mais plus lent que Java/Groovy). Mais BeanShell ne peut pas du tout faire ce calcul (cela prend une éternité, et mon ordinateur n'arrête jamais de traiter ce fichier).
Un avis sur la question ?