简体   繁体   English

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

[英]Benchmarking Java, Groovy, Jython and Python

I'm trying to benchmark a Monte Carlo calculation of PI (3.14159) throwing darts.我正在尝试对 PI (3.14159) 投掷飞镖的蒙特卡罗计算进行基准测试。 I've implemented my code in Java, Groovy, BeanShell, Julia, Jython and Python (Python2 implemented in C).我已经用 Java、Groovy、BeanShell、Julia、Jython 和 Python(用 C 实现的 Python2)实现了我的代码。

Here is my original Java code "MonteCarloPI.java":这是我的原始 Java 代码“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");
      }
}

Here is my Groovy code in the file "MonteCarloPI.groovy":这是我在“MonteCarloPI.groovy”文件中的 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")
       

Alternatively, I've removed the definitions such as "float" and "int" (ie loose types).或者,我已经删除了诸如“float”和“int”之类的定义(即松散类型)。 This checks the performance using "loose" types.这会使用“松散”类型检查性能。

I've renamed "MonteCarloPI.groovy" to a BeanShell script file "MonteCarloPI.bsh" (BeanShell has a very similar syntax as Groovy)我已将“MonteCarloPI.groovy”重命名为 BeanShell 脚本文件“MonteCarloPI.bsh”(BeanShell 的语法与 Groovy 非常相似)

In the case of the standard Python language, the code "MonteCarloPI_CPython.py" looks as this:在标准 Python 语言的情况下,代码“MonteCarloPI_CPython.py”如下所示:

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

This code is executed either in CPython 2.7.18 (Python implemented in C) or Jython 2.7.2 (Java implementation).此代码在 CPython 2.7.18(用 C 实现的 Python)或 Jython 2.7.2(Java 实现)中执行。 For Python 3.8.3 ("Python3"), replace "xrange" with "range".对于 Python 3.8.3(“Python3”),将“xrange”替换为“range”。

I also implemented the same algorithm in JRuby (MonteCarloPI.rb):我也在 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}"

Here the code using Julia:这里使用 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) )

I ran "MonteCarloPI.java", "MonteCarloPI.groovy", "MonteCarloPI.py", "MonteCarloPI.bsh" and MonteCarloPI.rb inside DataMelt editor.我在DataMelt编辑器中运行了“MonteCarloPI.java”、“MonteCarloPI.groovy”、“MonteCarloPI.py”、“MonteCarloPI.bsh”和 MonteCarloPI.rb。 The julia code was processed using locally installed julia-1.5.0/bin. julia 代码是使用本地安装的 julia-1.5.0/bin 处理的。

Here is the benchmark results on Intel(R) Core(TM) i5-4690K CPU @ 3.50GHz (ubuntu 20.04, 8 GB memory), with 2048 MB allocated for JDK9 when running Groovy, Jython, BeanShell code:这是在 Intel(R) Core(TM) i5-4690K CPU @ 3.50GHz(ubuntu 20.04,8 GB 内存)上的基准测试结果,在运行 Groovy、Jython、BeanShell 代码时为 JDK9 分配了 2048 MB:

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

As you can see, Java and Groovy calculations take about the same time (about 2 sec).如您所见,Java 和 Groovy 计算花费的时间大致相同(大约 2 秒)。 With loose types in Groovy, the execution takes 9 times slower.使用 Groovy 中的松散类型,执行速度要慢 9 倍。 Python is a factor 12 slower than Java and Groovy. Python 比 Java 和 Groovy 慢 12 倍。 Python3 is even slower. Python3 甚至更慢。 JRuby is as slow as Python. JRuby 和 Python 一样慢。 PyPy is rather fast (but slower than Java/Groovy). PyPy 相当快(但比 Java/Groovy 慢)。 But BeanShell cannot do this calculation at all (takes forever, and my computer never stops processing this file).但是 BeanShell 根本不能做这个计算(需要永远,而且我的电脑永远不会停止处理这个文件)。

Any wisdom on this?这有什么智慧吗?

Nice work.干得好。 Interesting comparison you have got there.有趣的比较你已经到了。 As a python developer I would like to add some additional view on Python.作为一名 Python 开发人员,我想添加一些关于 Python 的额外视图。

I assume it is slower mostly because of dynamic typing.我认为它比较慢主要是因为动态类型。 Another reason is that you are computing scalar values (ie using for loop and computing one number at a time).另一个原因是您正在计算标量值(即使用 for 循环并一次计算一个数字)。 One of the advantages of Python is a vector computing using NumPy library (this allows to compute multiple numbers at the same time). Python 的优势之一是使用 NumPy 库进行向量计算(这允许同时计算多个数字)。 So, here is my implementation of the algorithm.所以,这是我对算法的实现。 Note: I am using python 3.6.注意:我使用的是 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))

Here are benchmark results on my i7 x64 computer (Windows 10):以下是我的 i7 x64 计算机(Windows 10)的基准测试结果:

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

As you can see, the original python code run on my computer is slower than the python code on your computer.如你所见,我电脑上运行的原始python代码比你电脑上的python代码慢。 So, the optimized version might be even faster than Java or Groovy.因此,优化后的版本可能比 Java 或 Groovy 还要快。

Hope this helps.希望这可以帮助。

For the Julia code, you are including compilation time in your benchmark.对于 Julia 代码,您在基准测试中包括编译时间。 The other thing to note is that you are using global variables, which is well known to kill performance.另一件要注意的事情是您正在使用全局变量,众所周知,这会降低性能。 With your baseline version, the execution time on my machine is 17.7 seconds.使用您的基准版本,我机器上的执行时间为 17.7 秒。 Moving everything into a function, I get down to 0.83 seconds.把所有的东西都变成一个函数,我得到了 0.83 秒。 Removing compilation time from this gets me down to 713.625 ms.从中删除编译时间使我减少到 713.625 毫秒。 The final version of my code is this (note you counted one too many in your loop).我的代码的最终版本是这样的(注意你在循环中数了太多)。

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)

Note that further improvements are possible.请注意,进一步的改进是可能的。 It could be beneficial to allocate an array of random numbers outside of the loop rather than calling rand twice per iteration.在循环之外分配一个随机数数组而不是每次迭代调用 rand 两次可能是有益的。 You can find other performance tips here: https://docs.julialang.org/en/v1/manual/performance-tips/您可以在此处找到其他性能提示: https : //docs.julialang.org/en/v1/manual/performance-tips/

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM