# 在C，Clojure，Python，Ruby，Scala等中解释基准

## 结果表

` ` 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/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 (optimized) 0.32 0.79 1.46 12.01` `

[* 1] – 我恐怕想象要花多less时间

## 代码清单

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 "#{(ba)*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((ba).toString + " mils")` `

` `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 (- ba) "mils")))` `

Clojure优化`is-prime?`

` `(defn ^:static is-prime? [^long n] (loop [i (long 2)] (if (= (rem ni) 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((ba)) + " mils")` `

` `MEMO:: prime? ( n -- ? ) n 1 - 2 [a,b] [ n swap mod 0 > ] all? ; MEMO: sexyprimes ( nn -- rr ) [a,b] [ prime? ] filter [ 6 + ] map [ prime? ] filter dup [ 6 - ] map ; 5 10 1000 * sexyprimes . .` `

` `#!/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. 为什么斯卡拉这么快？ 是因为静态打字吗？ 或者它只是非常有效地使用JVM？
2. 为什么Ruby和Python有如此巨大的差异？ 我认为这两者没有什么不同。 也许我的代码是错误的。 请赐教！ 谢谢。 UPD是的，这是我的代码中的错误。 Python和Ruby 1.9是相当的。
3. Ruby版本之间的生产力真的让人印象深刻。
4. 我可以通过添加types声明来优化Clojure代码吗？ 它会帮助吗？

### 13 Solutions collect form web for “在C，Clojure，Python，Ruby，Scala等中解释基准”

1. Scala的静态types在这里帮助很大 – 这意味着它可以非常高效地使用JVM，而不需要太多额外的工作。
2. 我不太确定Ruby / Python的区别，但我怀疑`(2...n).all?` 在函数中`is-prime?` 很可能在Ruby中相当优化（编辑：这听起来确实如此，请参阅Julian的更详细的答案…）
3. Ruby 1.9.3只是更好的优化
4. Clojure代码当然可以加速很多！ 虽然Clojure默认是dynamic的，但在很多情况下，您可以使用types提示，原始math等来接近Scala /纯Java的速度。

Clojure代码中最重要的优化是在`is-prime?`内使用键入的原始math运算符`is-prime?` ， 就像是：

` `(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 ni)) false (if (>= (inc i) n) true (recur (inc i))))))` `

PS请注意，在某些情况下，您在基准testing程序中打印了代码 – 这不是一个好主意，因为它会扭曲testing结果，特别是如果首次使用像`print`这样的函数会引起IO子系统初始化或类似情况！

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

` `import time def is_prime(n): return all(n % j 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((ba)) + " mils")` `

` `(set! *unchecked-math* true) (defn is-prime? [^long n] (loop [i 2] (if (zero? (unchecked-remainder-int ni)) false (if (>= (inc i) n) true (recur (inc i)))))) (defn sexy-primes [m] (for [x (range 11 (inc m)) :when (and (is-prime? x) (is-prime? (- x 6)))] [(- x 6) x]))` `

` `(require '[clojure.core.reducers :as r]) ;' (defn sexy-primes [m] (->> (vec (range 11 (inc m))) (r/filter #(and (is-prime? %) (is-prime? (- % 6)))) (r/map #(list (- % 6) %)) (r/fold (fn ([] []) ([ab] (into ab))) conj)))` `

` ` def isPrime(n: Int, i: Int = 2): Boolean = if (i == n) true else if (n % i != 0) isPrime(n, i + 1) else false` `

` `object SexyPrimes { def isPrime(n: Int): Boolean = (2 to math.sqrt(n).toInt).forall{ n%_ != 0 } def isSexyPrime(n: Int): Boolean = isPrime(n) && isPrime(n-6) def findPrimesPar(n: Int) { for(k <- (11 to n).par) if(isSexyPrime(k)) printf("%d %d\n",k-6,k) } def findPrimes(n: Int) { for(k <- 11 to n) if(isSexyPrime(k)) printf("%d %d\n",k-6,k) } def timeOf(call : =>Unit) { val start = System.currentTimeMillis call val end = System.currentTimeMillis println((end-start)+" mils") } def main(args: Array[String]) { timeOf(findPrimes(100*1000)) println("------------------------") timeOf(findPrimesPar(100*1000)) } }` `

` `object SexyPrimes { def isPrime(n: Int): Boolean = (2 to math.sqrt(n).toInt).forall{ n%_ != 0 } def isSexyPrime(n: Int): Boolean = isPrime(n) && isPrime(n-6) def findPrimesPar(n: Int) { for(k <- (11 to n).par) if(isSexyPrime(k)) ()//printf("%d %d\n",k-6,k) } def findPrimes(n: Int) { for(k <- 11 to n) if(isSexyPrime(k)) ()//printf("%d %d\n",k-6,k) } def timeOf(call : =>Unit): Long = { val start = System.currentTimeMillis call val end = System.currentTimeMillis end - start } def main(args: Array[String]) { val xs = timeOf(findPrimes(1000*1000))::timeOf(findPrimesPar(1000*1000)):: timeOf(findPrimes(1000*1000))::timeOf(findPrimesPar(1000*1000)):: timeOf(findPrimes(1000*1000))::timeOf(findPrimesPar(1000*1000)):: timeOf(findPrimes(1000*1000))::timeOf(findPrimesPar(1000*1000))::Nil println(xs) } }` `

` `from functools import lru_cache import time as time_ @lru_cache() def is_prime(n): return n%2 and all(n%i for i in range(3, n, 2)) def primes_below(x): return [(i-6, i) for i in range(9, x+1, 2) if is_prime(i) and is_prime(i-6)] correct100 = [(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)] assert(primes_below(100) == correct100) a = time_.time() print(primes_below(30*1000)) b = time_.time() elapsed = b - a print("{} msec".format(round(elapsed * 1000)))` `

` `from functools import lru_cache import math import time as time_ known_primes = set([2, 3, 5, 7]) @lru_cache(maxsize=128) def is_prime(n): last = math.ceil(math.sqrt(n)) flag = n%2 and all(n%x for x in known_primes if x <= last) if flag: known_primes.add(n) return flag def primes_below(x): return [(i-6, i) for i in range(9, x+1, 2) if is_prime(i) and is_prime(i-6)] correct100 = [(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)] assert(primes_below(100) == correct100) a = time_.time() print(primes_below(100*1000)) b = time_.time() elapsed = b - a print("{} msec".format(round(elapsed * 1000)))` `

` `logical function isprime(n) IMPLICIT NONE ! integer :: n,i do i=2,n if(mod(n,i).eq.0)) return .false. enddo return .true. end subroutine findprimes(m) IMPLICIT NONE ! integer :: m,i logical, external :: isprime do i=11,m if(isprime(i) .and. isprime(i-6))then write(*,*) i-6,i endif enddo end program main findprimes(10*1000) end` `

` `int isprime( int x ) { int i, n; for( i = 3, n = x >> 1; i <= n; i += 2 ) if( x % i == 0 ) return 0; return 1; } void findprimes( int m ) { int i, s = 3; // s is bitmask of primes in last 3 odd numbers for( i = 11; i < m; i += 2, s >>= 1 ) { if( isprime( i ) ) { if( s & 1 ) printf( "%d %d\n", i - 6, i ); s |= 1 << 3; } } } main() { findprimes( 10 * 1000 ); }` `

` `public class prime { private static boolean isprime( final int x ) { for( int i = 3, n = x >> 1; i <= n; i += 2 ) if( x % i == 0 ) return false; return true; } private static void findprimes( final int m ) { int s = 3; // s is bitmask of primes in last 3 odd numbers for( int i = 11; i < m; i += 2, s >>= 1 ) { if( isprime( i ) ) { if( ( s & 1 ) != 0 ) print( i ); s |= 1 << 3; } } } private static void print( int i ) { System.out.println( ( i - 6 ) + " " + i ); } public static void main( String[] args ) { // findprimes( 300 * 1000 ); // for some JIT training long time = System.nanoTime(); findprimes( 10 * 1000 ); time = System.nanoTime() - time; System.err.println( "time: " + ( time / 10000 ) / 100.0 + "ms" ); } }` `

Java 1.7.0_04的运行速度几乎和C版一样快。 客户机或服务器虚拟机并没有显示出太大的差别，只是JIT培训似乎有助于服务器虚拟机（〜3％），而对客户机虚拟机几乎没有影响。 Java中的输出似乎比C中的输出慢。如果两个版本中的输出被静态计数器replace，则Java版本运行速度比C版本要快一些。

• 319ms C用/ Ox编译并输出到> NIL：
• 使用/ Ox和静态计数器编译312ms C
• 324ms Java客户端虚拟机，输出为> NIL：
• 299毫秒的Java客户端虚拟机与静态计数器

• 用/ Ox和静态计数器编译24.95s
• 具有静态计数器的25.08s Java客户端VM
• 具有静态计数器的24.86s Java服务器虚拟机

Tuple2专门用于Int，Long和Double，这意味着它不必对这些原始数据types进行装箱/取消装箱。 Tuple2来源 。 列表不是专门的。 列出来源 。

` `package main import ( "fmt" ) func main(){ findprimes(10*1000) } func isprime(x int) bool { for i := 2; i < x; i++ { if x%i == 0 { return false } } return true } func findprimes(m int){ for i := 11; i < m; i++ { if isprime(i) && isprime(i-6) { fmt.Printf("%d %d\n", i-6, i) } } }` `

100k版本： `C: 2.723s` `Go: 2.743s`

` `package main import ( "fmt" "runtime" ) func main(){ runtime.GOMAXPROCS(4) printer := make(chan string) printer2 := make(chan string) printer3 := make(chan string) printer4 := make(chan string) finished := make(chan int) var buffer, buffer2, buffer3 string running := 4 go findprimes(11, 30000, printer, finished) go findprimes(30001, 60000, printer2, finished) go findprimes(60001, 85000, printer3, finished) go findprimes(85001, 100000, printer4, finished) for { select { case i := <-printer: // batch of sexy primes received from printer channel 1, print them fmt.Printf(i) case i := <-printer2: // sexy prime list received from channel, store it buffer = i case i := <-printer3: // sexy prime list received from channel, store it buffer2 = i case i := <-printer4: // sexy prime list received from channel, store it buffer3 = i case <-finished: running-- if running == 0 { // all goroutines ended // dump buffer to stdout fmt.Printf(buffer) fmt.Printf(buffer2) fmt.Printf(buffer3) return } } } } func isprime(x int) bool { for i := 2; i < x; i++ { if x%i == 0 { return false } } return true } func findprimes(from int, to int, printer chan string, finished chan int){ str := "" for i := from; i <= to; i++ { if isprime(i) && isprime(i-6) { str = str + fmt.Sprintf("%d %d\n", i-6, i) } } printer <- str //fmt.Printf("Finished %d to %d\n", from, to) finished <- 1 }` `

`C: 3m35.458s` `Go: 3m36.259s` `Go using goroutines:` `2m16.125s` `Go using goroutines:` 3m27.137s `2m16.125s`

100k版本：

`C: 2.723s` `Go: 2.743s` `Go using goroutines: 1.706s`

` `import scala.annotation.tailrec var count = 0; def print(i:Int) = { println((i - 6) + " " + i) count += 1 } @tailrec def isPrime(n:Int, i:Int = 3):Boolean = { if(n % i == 0) return false; else if(i * i > n) return true; else isPrime(n = n, i = i + 2) } @tailrec def findPrimes(max:Int, bitMask:Int = 3, i:Int = 11):Unit = { if (isPrime(i)) { if((bitMask & 1) != 0) print(i) if(i + 2 < max) findPrimes(max = max, bitMask = (bitMask | (1 << 3)) >> 1, i = i + 2) } else if(i + 2 < max) { findPrimes(max = max, bitMask = bitMask >> 1, i = i + 2) } } val a = System.currentTimeMillis() findPrimes(max=10000000) println(count) val b = System.currentTimeMillis() println((b - a).toString + " mils")` `

` `count = 0; print = (i) -> console.log("#{i - 6} #{i}") count += 1 return isPrime = (n) -> i = 3 while i * i < n if n % i == 0 return false i += 2 return true findPrimes = (max) -> bitMask = 3 for i in [11..max] by 2 prime = isPrime(i) if prime if (bitMask & 1) != 0 print(i) bitMask |= (1 << 3) bitMask >>= 1 return a = new Date() findPrimes(1000000) console.log(count) b = new Date() console.log((b - a) + " ms")` `

` `require 'benchmark' num = ARGV[0].to_i def is_prime?(n) (2...n).all?{|m| n%m != 0 } end def sexy_primes_default(x) (9..x).map do |i| [i-6, i] end.select do |j| j.all?{|j| is_prime? j} end end def sexy_primes_threads(x) partition = (9..x).map do |i| [i-6, i] end.group_by do |x| x[0].to_s[-1] end threads = Array.new partition.each_key do |k| threads << Thread.new do partition[k].select do |j| j.all?{|j| is_prime? j} end end end threads.each {|t| t.join} threads.map{|t| t.value}.reject{|x| x.empty?} end puts "Running up to num #{num}" Benchmark.bm(10) do |x| x.report("default") {a = sexy_primes_default(num)} x.report("threads") {a = sexy_primes_threads(num)} end` `

` `# Ruby 1.9.3 \$ ./sexyprimes.rb 100000 Running up to num 100000 user system total real default 68.840000 0.060000 68.900000 ( 68.922703) threads 71.730000 0.090000 71.820000 ( 71.847346) # JRuby 1.6.7.2 on JVM 1.7.0_05 \$ jruby --1.9 --server sexyprimes.rb 100000 Running up to num 100000 user system total real default 56.709000 0.000000 56.709000 ( 56.708000) threads 36.396000 0.000000 36.396000 ( 36.396000) # JRuby 1.7.0.preview1 on JVM 1.7.0_05 \$ jruby --server sexyprimes.rb 100000 Running up to num 100000 user system total real default 52.640000 0.270000 52.910000 ( 51.393000) threads 105.700000 0.290000 105.990000 ( 30.298000)` `

Java的速度的原因是：

JVM可以在运行时对代码进行分析并对其进行手动优化 – 例如，如果您有一个可以在编译时静态分析的方法是一个真正的函数，并且JVM注意到您经常使用相同的方法调用它参数，它可能实际上完全消除了调用，只是注入了最后一次调用的结果（我不确定Java是否真的这样做，但它做了很多这样的东西）。

The JVM can know quirks about your CPU architecture and generate machine code specifically for a given CPU.

The JVM can speed your code long after you shipped it. Much like moving a program to a new CPU can speed it up, moving it to a new version of the JVM can also give you huge speed performances taylored to CPUs that didn't even exist when you initially compiled your code, something c physically cannot do without a recomiple.

By the way, most of the bad rep for java speed comes from the long startup time to load the JVM (Someday someone will build the JVM into the OS and this will go away!) and the fact that many developers are really bad at writing GUI code (especially threaded) which caused Java GUIs to often become unresponsive and glitchy. Simple to use languages like Java and VB have their faults amplified by the fact that the capibilities of the average programmer tends to be lower than more complicated languages.

• 什么是Scala中的“上下文绑定”？
• 适用于Android的Scala编程
• 什么是Scala中的函数字面量？
• 使用Scala IDE的SBT
• 什么types用于存储在Scala中的内存中的可变数据表？
• 为什么追加到列表不好？
• Scala中的`def` vs`val` vs`lazy val`评估
• 在任意scala代码位置放入解释器
• 揭开斯卡拉神话
• 一个范围可以在Scala中匹配吗？
• JVM需要很长时间才能parsinglocalhost的IP地址