用 C、Clojure、Python、Ruby、Scala 等解释基准 [英] Interpreting a benchmark in C, Clojure, Python, Ruby, Scala and others

查看:35
本文介绍了用 C、Clojure、Python、Ruby、Scala 等解释基准的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我知道人工基准是邪恶的.它们只能显示非常具体的狭窄情况的结果.我不认为一种语言比另一种更好,因为一些愚蠢的板凳.但是我想知道为什么结果如此不同.请在底部查看我的问题.

I know that artificial benchmarks are evil. They can show results only for very specific narrow situation. I don't assume that one language is better than the other because of the some stupid bench. However I wonder why results is so different. Please see my questions at the bottom.

基准是简单的数学计算,用于找到相差 6 的素数对(所谓的性感素数)例如.低于 100 的性感素数将是:(5 11) (7 13) (11 17) (13 19) (17 23) (23 29) (31 37) (37 43) (41 47) (47 53)(53 59) (61 67) (67 73) (73 79) (83 89) (97 103)

Benchmark is simple math calculations to find pairs of prime numbers which differs by 6 (so called sexy primes) E.g. sexy primes below 100 would be: (5 11) (7 13) (11 17) (13 19) (17 23) (23 29) (31 37) (37 43) (41 47) (47 53) (53 59) (61 67) (67 73) (73 79) (83 89) (97 103)

表中:计算时间以秒为单位运行:除了 Factor 之外的所有都在 VirtualBox 中运行(Debian 不稳定 amd64 来宾,Windows 7 x64 主机)CPU:AMD A4-3305M

In table: calculation time in seconds Running: all except Factor was running in VirtualBox (Debian unstable amd64 guest, Windows 7 x64 host) CPU: AMD A4-3305M

  Sexy primes up to:        10k      20k      30k      100k               

  Bash                    58.00   200.00     [*1]      [*1]

  C                        0.20     0.65     1.42     15.00

  Clojure1.4               4.12     8.32    16.00    137.93

  Clojure1.4 (optimized)   0.95     1.82     2.30     16.00

  Factor                    n/a      n/a    15.00    180.00

  Python2.7                1.49     5.20    11.00       119     

  Ruby1.8                  5.10    18.32    40.48    377.00

  Ruby1.9.3                1.36     5.73    10.48    106.00

  Scala2.9.2               0.93     1.41     2.73     20.84

  Scala2.9.2 (optimized)   0.32     0.79     1.46     12.01

[*1] - 我不敢想象需要多少时间

[*1] - I'm afraid to imagine how much time will it take

C:

int isprime(int x) {
  int i;
  for (i = 2; i < x; ++i)
    if (x%i == 0) return 0;
  return 1;
}

void findprimes(int m) {
  int i;
  for ( i = 11; i < m; ++i)
    if (isprime(i) && isprime(i-6))
      printf("%d %d
", i-6, i);
}

main() {
    findprimes(10*1000);
}

红宝石:

def is_prime?(n)
  (2...n).all?{|m| n%m != 0 }
end

def sexy_primes(x)
  (9..x).map do |i|
    [i-6, i]
  end.select do |j|
    j.all?{|j| is_prime? j}
  end
end

a = Time.now
p sexy_primes(10*1000)
b = Time.now
puts "#{(b-a)*1000} mils"

斯卡拉:

def isPrime(n: Int) =
  (2 until n) forall { n % _ != 0 }

def sexyPrimes(n: Int) = 
  (11 to n) map { i => List(i-6, i) } filter { _ forall(isPrime(_)) }

val a = System.currentTimeMillis()
println(sexyPrimes(100*1000))
val b = System.currentTimeMillis()
println((b-a).toString + " mils")

Scala 优化 isPrime(与 Clojure 优化中的想法相同):

Scala opimized isPrime (the same idea like in Clojure optimization):

import scala.annotation.tailrec

@tailrec // Not required, but will warn if optimization doesn't work
def isPrime(n: Int, i: Int = 2): Boolean = 
  if (i == n) true 
  else if (n % i != 0) isPrime(n, i + 1)
  else false

Clojure:

(defn is-prime? [n]
  (every? #(> (mod n %) 0)
    (range 2 n)))

(defn sexy-primes [m]
  (for [x (range 11 (inc m))
        :let [z (list (- x 6) x)]
        :when (every? #(is-prime? %) z)]
      z))

(let [a (System/currentTimeMillis)]
  (println (sexy-primes (* 10 1000)))
  (let [b (System/currentTimeMillis)]
    (println (- b a) "mils")))

Clojure 优化 is-prime?:

Clojure optimized is-prime?:

(defn ^:static is-prime? [^long n]
  (loop [i (long 2)] 
    (if (= (rem n i) 0)
      false
      (if (>= (inc i) n) true (recur (inc i))))))

蟒蛇

import time as time_

def is_prime(n):
  return all((n%j > 0) for j in xrange(2, n))

def primes_below(x):
  return [[j-6, j] for j in xrange(9, x+1) if is_prime(j) and is_prime(j-6)]

a = int(round(time_.time() * 1000))
print(primes_below(10*1000))
b = int(round(time_.time() * 1000))
print(str((b-a)) + " mils")

因素

MEMO:: prime? ( n -- ? )
n 1 - 2 [a,b] [ n swap mod 0 > ] all? ;

MEMO: sexyprimes ( n n -- r r )
[a,b] [ prime? ] filter [ 6 + ] map [ prime? ] filter dup [ 6 - ] map ;

5 10 1000 * sexyprimes . .

Bash(zsh):

#!/usr/bin/zsh
function prime {
  for (( i = 2; i < $1; i++ )); do
    if [[ $[$1%i] == 0 ]]; then
      echo 1
      exit
    fi
  done
  echo 0
}

function sexy-primes {
  for (( i = 9; i <= $1; i++ )); do
    j=$[i-6]
    if [[ $(prime $i) == 0 && $(prime $j) == 0 ]]; then
      echo $j $i
    fi
  done
}

sexy-primes 10000

问题

  1. 为什么 Scala 这么快?是不是因为静态类型?或者它只是非常有效地使用 JVM?
  2. 为什么 Ruby 和 Python 之间有如此巨大的差异?我认为这两者并没有完全不同.也许我的代码是错误的.请赐教!谢谢. UPD 是的,这是我的代码中的错误.Python 和 Ruby 1.9 相当.
  3. Ruby 版本之间的生产力飞跃确实令人印象深刻.
  4. 我可以通过添加类型声明来优化 Clojure 代码吗?会有帮助吗?
  1. Why Scala is so fast? Is it because of static typing? Or it is just using JVM very efficiently?
  2. Why such a huge difference between Ruby and Python? I thought these two are not somewhat totally different. Maybe my code is wrong. Please enlighten me! Thanks. UPD Yes, that was error in my code. Python and Ruby 1.9 are pretty equal.
  3. Really impressive jump in productivity between Ruby versions.
  4. Can I optimize Clojure code by adding type declarations? Will it help?

推荐答案

粗略回答:

  1. Scala 的静态类型在这方面有很大帮助 - 这意味着它可以非常有效地使用 JVM,而无需付出太多额外的努力.
  2. 我不太确定 Ruby/Python 的区别,但我怀疑 (2...n).all? 在函数 is-prime? 中> 可能会在 Ruby 中得到很好的优化(听起来确实如此,请参阅 Julian 的回答以了解更多详细信息...)
  3. Ruby 1.9.3 优化得更好
  4. Clojure 代码当然可以大大加速!虽然 Clojure 默认情况下是动态的,但在许多情况下,您可以使用类型提示、原始数学等来接近 Scala/纯 Java 的速度.
  1. Scala's static typing is helping it quite a bit here - this means that it uses the JVM pretty efficiently without too much extra effort.
  2. I'm not exactly sure on the Ruby/Python difference, but I suspect that (2...n).all? in the function is-prime? is likely to be quite well optimised in Ruby ( sounds like this is indeed the case, see Julian's answer for more detail...)
  3. Ruby 1.9.3 is just much better optimised
  4. Clojure code can certainly be accelerated a lot! While Clojure is dynamic by default, you can use type hints, primitive maths etc. to get close to Scala / pure Java speed in many cases when you need to.

Clojure 代码中最重要的优化是在 is-prime? 中使用类型化的原始数学,例如:

Most important optimisation in the Clojure code would be to use typed primitive maths within is-prime?, something like:

(set! *unchecked-math* true) ;; at top of file to avoid using BigIntegers

(defn ^:static is-prime? [^long n]
  (loop [i (long 2)] 
    (if (zero? (mod n i))
      false
      (if (>= (inc i) n) true (recur (inc i))))))

通过这项改进,我让 Clojure 在 0.635 秒内完成了 10k(即你的列表中第二快,击败了 Scala)

With this improvement, I get Clojure completing 10k in 0.635 secs (i.e. the second fastest on your list, beating Scala)

PS 请注意,在某些情况下,您的基准测试中有打印代码 - 不是一个好主意,因为它会扭曲结果,尤其是在使用 print 之类的函数时第一次导致 IO 子系统的初始化或类似的事情!

P.S. note that you have printing code inside your benchmark in some cases - not a good idea as it will distort the results, especially if using a function like print for the first time causes initialisation of IO subsystems or something like that!

这篇关于用 C、Clojure、Python、Ruby、Scala 等解释基准的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆