我正在尝试对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还出奇的慢。
对此有何见识?
答案 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/