对Java,Groovy,Jython和Python进行基准测试

时间:2019-01-20 22:57:35

标签: java python groovy jruby jython

我正在尝试对PI(3.14159)投掷飞镖的蒙特卡洛计算进行基准测试。我已经用Java,Groovy,BeanShell,Jython和Python(用C实现的Python2)实现了代码。

这是我的原始Java代码“ MonteCarloPI.java”:

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;
         for (int i = 0; i < events; i++) {
            x = Math.random();      // Throw a dart
            y = Math.random();
            nThrows++;
            if ( x*x + y*y <= 1 )  nSuccess++;
       }
 int itime = (int)((System.nanoTime() - then)/1e9);
 System.out.println("Time for calculations (sec): " + itime+"\n");
 System.out.println("Pi = " + 4*(double)nSuccess/(double)nThrows +"\n");
      }
}

这是我的Groovy代码放在文件“ MonteCarloPI.groovy”中:

int nThrows = 0; int nSuccess = 0;
double x, y;
long then = System.nanoTime();
int events=(int)1e8;
for (int i = 0; i < events; i++)   {
   x = Math.random(); y = Math.random();     // Throw a dart                   
   nThrows++;
   if ( x*x + y*y <= 1 )  nSuccess++;
}
int itime = (int)((System.nanoTime() - then)/1e9);
System.out.println("Time for calculations (sec): " + itime+"\n");
System.out.println("Pi = " + 4*(float)nSuccess/(float)nThrows +"\n");

这是我的Jython代码“ MonteCarloPI.py”:

from java.util import Random
from java.lang import *

nThrows,nSuccess = 0,0
then = System.nanoTime()
events=int(1e8)
for i in xrange(events):
    x,y = Math.random(),Math.random();      # Throw a dart                   
    nThrows +=1
    if ( x*x + y*y <= 1 ):  nSuccess+=1
itime = (int)((System.nanoTime() - then)/1e9)
print "Time for calculations (sec): ",itime
print "Pi = ", 4*nSuccess/float(nThrows)

我已将“ MonteCarloPI.groovy”重命名为BeanShell脚本文件“ MonteCarloPI.bsh”(BeanShell的语法与Groovy非常相似)

对于标准Python,代码为“ MonteCarloPI_CPython.py”,如下所示:

import random
import 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 for calculations (sec): ",itime
print "Pi = ", 4*nSuccess/float(nThrows)

我还在JRuby(MonteCarloPI.rb)中实现了相同的算法:

require "java"
java_import java.lang.System;
java_import java.lang.Math;

nThrows = 0; nSuccess = 0;
xthen = System.nanoTime();
events=1e8;
for i  in 0 .. events do
   x = Math.random(); y = Math.random();     #  Throw a dart                   
   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}"

我在DataMelt编辑器中运行了“ MonteCarloPI.java”,“ MonteCarloPI.groovy”,“ MonteCarloPI.py”,“ MonteCarloPI.bsh”和MonteCarloPI.rb。

这是我的i7 x64计算机(Linux Mint)上的基准测试结果,运行Groovy,Jython,BeanShell代码时,为JDK9分配了2048 MB:

Java   code:   3 sec Pi = 3.14176584  -> executed in DataMelt/JDK9
Groovy code:   3 sec Pi = 3.14144832  -> executed in DataMelt/JDK9
Python code:   3 sec Pi = 3.14188036  -> executed using PyPy
Groovy code:  14 sec Pi = 3.14141132  -> when using loose types for x,y 
Python code:  28 sec Pi = 3.14188036  -> executed in Python (CPython)
JRuby  code:  31 sec Pi = 3.14187860  -> executed in DataMelt/JDK9
Jython code:  40 sec Pi = 3.14187860  -> executed in DataMelt/JDK9
BeanShell code: takes forever?!       -> executed in DataMelt/JDK9
Jython after replacing xrange() with range() -> takes forever?!

如您所见,Java和Groovy计算大约需要相同的时间(3秒)。 Python比Java和Groovy慢十倍。 JRuby和Python一样慢。 PyPy相当快(与Java / Groovy一样快)。 但是Jython和BeanShell根本无法进行此计算(花了很长时间,而且我的计算机从未停止处理此文件)。

最后一个示例使用了Jython版本2.7.2a。我已经使用Jython本身附带的“ jython.sh”脚本在DataMelt外部重复了Jython代码执行,并且得到了相同的结果(即Jython计算永远需要)。

我知道Jython对于处理高级Java类很有用,但是我没想到在使用“ range”而不是“ xrange”的情况下,Jython不能对这种简单的数字情况起到太大的作用。 与Groovy中的脚本编写相比,Python还出奇的慢。

对此有何见识?

2 个答案:

答案 0 :(得分:1)

好工作。比较有趣的比较。作为python开发人员,我想在Python上添加一些其他视图。

我认为它的速度较慢主要是因为动态键入。另一个原因是您正在计算标量值(即使用for循环并一次计算一个数字)。 Python的优点之一是使用NumPy库进行矢量计算(这允许同时计算多个数字)。因此,这是我对算法的实现。注意:我使用的是python 3.6。

import numpy as np
import time

start = time.time()

events = int(1e8)
nThrows, nSuccess = 0, 0

x, y = np.random.uniform(size=(2, events))
nSuccess = (x*x + y*y <= 1).sum()
nThrows = events
pi = 4*nSuccess/float(nThrows)

stop = time.time()
print('Time: {}, Pi = {}'.format(stop-start, pi))

以下是我的i7 x64计算机(Windows 10)上的基准测试结果:

Python (original code):      42.6s  Pi = 3.1414672
Python (my optimized code):  4.7s   Pi = 3.1417642

如您所见,在我的计算机上运行的原始python代码比您计算机上的python代码慢。因此,优化的版本甚至可能比Java或Groovy更快。

希望这会有所帮助。

答案 1 :(得分:1)

对于Julia代码,您将编译时间包括在基准中。要注意的另一件事是您正在使用全局变量,众所周知这会降低性能。 对于您的基准版本,我的计算机上的执行时间为17.7秒。 将所有内容移至一个函数中,我的时间减少到0.83秒。 从中删除编译时间使我下降到713.625毫秒。 我的代码的最终版本是这样(请注意,您在循环中计数过多)。

using Random
using BenchmarkTools

function benchmark()
    nThrows = 0
    nSuccess = 0
    events = 100_000_000
    for j in 1:events
            x = rand()      #  Throw a dart
            y = rand()
            nThrows += 1
            if  x^2 + y^2 <= 1
                nSuccess += 1
            end
    end
    4.0*nSuccess/nThrows
end

pi = @btime benchmark()
println( "Pi = ",  pi)

请注意,可能会有进一步的改进。在循环外分配一个随机数数组比在每次迭代中两次调用rand可能是有益的。您可以在此处找到其他性能提示:https://docs.julialang.org/en/v1/manual/performance-tips/