Сравнительный анализ Java, Groovy, Jython и Python - PullRequest
0 голосов
/ 21 января 2019

Я пытался сравнить вычисление метания дротиков в PI (3.14159) по методу Монте-Карло. Я реализовал свой код на Java, Groovy, BeanShell, Jython и Python (Python2 реализован на C)

Вот мой оригинальный код 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}"

Я запустил «MonteCarloPI.java», «MonteCarloPI.groovy», «MonteCarloPI.py», «MonteCarloPI.bsh» и MonteCarloPI.rb внутри DataMelt редактора.

Вот результаты тестов на моем компьютере i7 x64 (Linux Mint) с 2048 МБ, выделенными для JDK9 при выполнении кода Groovy, Jython, BeanShell:

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 в 10 раз медленнее, чем Java и Groovy. JRuby такой же медленный, как Python. PyPy довольно быстрый (так же быстро, как Java / Groovy). Но Jython и BeanShell вообще не могут выполнить этот расчет (это занимает вечно, и мой компьютер никогда не прекращает обработку этого файла).

В последнем примере использовался Jython версии 2.7.2a. Я повторил выполнение кода Jython вне DataMelt, используя сценарий "jython.sh", поставляемый с самим Jython, и получил те же результаты (т.е. вычисление Jython занимает вечность).

Я знал, что Jython полезен для манипуляций с высокоуровневыми классами Java, но я не ожидал, что он не сможет многое сделать для такого простого числового случая, когда используется «range» вместо «xrange». Python также удивительно медленный по сравнению со скриптами в Groovy.

Есть ли в этом какая-то мудрость?

1 Ответ

0 голосов
/ 23 января 2019

Отличная работа. Интересное сравнение у тебя там есть. Как разработчик Python, я хотел бы добавить некоторые дополнительные представления о Python.

Я предполагаю, что это медленнее в основном из-за динамической типизации. Другая причина заключается в том, что вы вычисляете скалярные значения (т.е. используете для цикла и вычисляете по одному числу за раз). Одним из преимуществ 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.

Надеюсь, это поможет.

...