在C,Clojure,Python,Ruby,Scala和其他人解释基准 [英] Interpreting a benchmark in C, Clojure, Python, Ruby, Scala and others

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

问题描述

免责声明



我知道人工基准是邪恶的。它们只能针对非常特定的狭窄情况显示结果。我不认为一种语言比另一种语言更好,因为一些愚蠢的长凳。然而我不知道为什么结果是如此不同。



数学基准描述



基准是一个简单的数学计算,不同6的素数(所谓的性感素材
例如性感素质低于100将是:(5 11)(7 13)(11 17)(13 19)(17 23)(23 29)(31 37)(37 43) 47 53)(53 59)(61 67)(67 73)(73 79)(83 89)(97 103)



表>

在表格中:计算时间(以秒计)
运行:除了Factor之外的所有操作都在VirtualBox中运行(Debian unstable amd64 guest,Windows 7 x64主机)
CPU:AMD A4-3305M

 性感素材达: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(优化)0.95 1.82 2.30 16.00

因素n / an / 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优化)0.32 0.79 1.46 12.01

[* 1] - 恐怕会想到多少时间



代码清单



C:

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

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

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

Ruby:

  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#{ (ba)* 1000} mils

Scala:


$ b b

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

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

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


b $ b

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

  import scala.annotation.tailrec 

@tailrec //不需要,但会在优化不起作用时发出警告
def isPrime(n:Int,i:Int = 2):Boolean =
if(i == n)true
else if(n%i!= 0)isPrime(n,i + 1)
else false

Clojure:

 (b)(b)(b)(b)(b)(b)(b)(b)(b)(b) -p rimes [m] 
(for [x(range 11(inc m))
:let [z(list( - x 6)x)]
:when (*))
z)]
$ b(let [a(System / currentTimeMillis)]
(println(sexy-primes(* 10 1000)))
(let [b(System / currentTimeMillis)]
(println( - ba)mils)))

Clojure优化 is-prime?

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

b
$ b

  import time as time_ 

def is_prime(n):
return all((n%j> 0)对于xrange(2,n)中的j)

def primes_below(x):
return [[j-6,j]如果is_prime(j)和is_prime(j-6)]

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

因素

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

备忘:sexyprimes(n n - r r)
[a,b] ] filter [6 +] map [ ] 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]];然后
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]];那么
echo $ j $ i
fi
done
}

sexy-primes 10000



问题




  1. 为什么Scala这么快?是因为静态打字?或者它只是非常有效地使用JVM?

  2. 为什么Ruby和Python之间有巨大的区别?我认为这两个不是有点完全不同。也许我的代码是错误的。请指教我!感谢。 UPD 是的,这是我的代码中的错误。

  3. 我可以通过添加类型声明来优化Clojure代码吗?它会有帮助吗?


解决方案

粗糙的答案:


  1. Scala的静态类型在这里非常有用 - 这意味着它可以高效地使用JVM,而不需要太多的额外工作。

  2. 对于Ruby / Python的区别并不完全确定,但我怀疑函数中的(2 ... n).all? is- ?很可能在Ruby中被相当好地优化(编辑:听起来像这样,请参阅朱利安的答案更多细节...)

  3. Ruby 1.9.3只是更好的优化

  4. Clojure代码肯定会加速很多!默认情况下,Clojure是动态的,你可以使用类型提示,原始数学等在很多情况下接近Scala /纯Java速度。

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

 (set!* unchecked-math * true);;在文件顶部避免使用BigIntegers 

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



有了这个改进,我得到Clojure完成10k在0.635秒(即你的名单上第二快,击败Scala)



PS 注意,在某些情况下,您在基准测试中有打印代码 - 这不是一个好主意,因为它会扭曲结果,特别是如果使用 print 第一次导致初始化IO子系统或类似的东西!


Disclaimer

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.

Math benchmark description

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)

Results table

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] - I'm afraid to imagine how much time will it take

Code listings

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\n", i-6, i);
}

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

Ruby:

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"

Scala:

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

Python

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

Factor

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

Questions

  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?

解决方案

Rough answers:

  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 (EDIT: 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.

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

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

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天全站免登陆