【问题标题】:Benchmarking Java, Groovy, Jython and Python对 Java、Groovy、Jython 和 Python 进行基准测试
【发布时间】:2019-01-20 22:57:34
【问题描述】:

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

这是我的原始 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");
      }
}

这是我在“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")
       

另外,我删除了诸如“float”和“int”之类的定义(即松散类型)。这使用“松散”类型检查性能。

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

对于标准 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))

此代码在 CPython 2.7.18(Python 用 C 实现)或 Jython 2.7.2(Java 实现)中执行。对于 Python 3.8.3(“Python3”),将“xrange”替换为“range”。

我也在 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}"

这里是使用 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) )

我在DataMelt 编辑器中运行了“MonteCarloPI.java”、“MonteCarloPI.groovy”、“MonteCarloPI.py”、“MonteCarloPI.bsh”和 MonteCarloPI.rb。 使用本地安装的 julia-1.5.0/bin 处理 julia 代码。

这是在 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

如您所见,Java 和 Groovy 的计算时间大致相同(大约 2 秒)。在 Groovy 中使用松散类型时,执行速度会慢 9 倍。 Python 比 Java 和 Groovy 慢 12 倍。 Python3 甚至更慢。 JRuby 和 Python 一样慢。 PyPy 相当快(但比 Java/Groovy 慢)。 但是 BeanShell 根本无法进行这种计算(需要很长时间,而且我的计算机永远不会停止处理这个文件)。

这方面有什么智慧吗?

【问题讨论】:

  • 我猜 Jython 解释器没有使用任何原始值类型,只使用对象实例。
  • 我还怀疑 BeanShell 和 Jython 只对对象进行操作。为了完整性,我还添加了标准 Python(非常慢)

标签: java python groovy jruby jython


【解决方案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 更快。

希望这会有所帮助。

【讨论】:

  • 谢谢!我怀疑可以通过从 Jython 调用一些 Java 库来完成类似的技巧。顺便说一句,这个讨论已经触发了 reddit 线程reddit.com/r/Python/comments/aiu0ak 我了解到,使用松散类型的变量(与上面的代码不同),Groovy 可能会慢 3 倍。但是,pypy 与 Groovy 和 Java 一样快(大约 3 秒)
  • Jython 开发人员建议使用 xrange() 代替 range()。这对 CPython 没有影响。但这加速了我们的 Jython。
【解决方案2】:

对于 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/

【讨论】:

  • 嗨,一月。我已经更改了没有“BenchmarkTools”的代码,我确认执行时间为 0.74 秒。因此,它比该线程中的任何其他代码示例都快。
  • 在 Groovy 中使用 r.nextFloat() 会将 Groovy 的基准值从 3 秒更改为 2 秒。 Julia 仍然快 2 倍。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-02-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-09-23
  • 2017-12-30
  • 2023-03-13
相关资源
最近更新 更多