解释在C基准,Clojure中,Python和Ruby,Scala和其他(闭合)(Interpret

2019-06-26 12:46发布

放弃

我知道,人工基准是邪恶的。 他们可以只显示非常具体的狭窄情况的结果。 我不认为一种语言是优于其他,因为一些愚蠢的替补。 但是我不知道为什么结果是如此不同。 请参阅我的问题在底部。

数学基准描述

基准是简单的数学计算,以找到对,其通过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)

结果表

在表:以秒计算的时间运行:所有除因子在VirtualBox中(Debian的不稳定AMD64客人,Windows 7的64位主机)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] - 我不敢想象它会花费多少时间

代码清单

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

红宝石:

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

斯卡拉opimized isPrime (同样的想法,像Clojure的优化):

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?

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

巴什(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的代码? 有帮助吗?

Answer 1:

粗糙的答案:

  1. Scala的静态类型是颇有几分这里帮助它 - 这意味着它使用JVM非常有效地没有太多额外的努力。
  2. 我不是在Ruby / Python的差异完全确定,但我怀疑(2...n).all? 在功能上is-prime? 很可能是用Ruby很好的优化(编辑:听起来这的确是这样的,看朱利安的答案更详细......)
  3. 红宝石1.9.3只是更好的优化
  4. Clojure的代码肯定可以加速很多! 虽然Clojure是由默认的动态,你可以使用类型提示,原始数学等亲近在许多情况下斯卡拉/纯Java的速度,当您需要。

最重要的是优化Clojure的代码将是中使用输入原始数学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 n i))
      false
      (if (>= (inc i) n) true (recur (inc i))))))

通过这种改进,我得到的Clojure完成10K在0.635秒(即第二快的清单上,击败斯卡拉)

你有打印的代码在基准测试在某些情况下,PS注-不是一个好主意,因为它会扭曲的结果,尤其是在使用这样的函数print首度导致IO子系统的初始化或类似的东西!



Answer 2:

这里有一个快速Clojure的版本,使用相同的基本算法:

(set! *unchecked-math* true)

(defn is-prime? [^long n]
  (loop [i 2]
    (if (zero? (unchecked-remainder-int n i))
      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]))

它运行约20倍比我的机器上原来的速度更快。 下面是一个利用新减速机库在1.5版本(需要Java 7或JSR 166):

(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 ([] []) ([a b] (into a b))) conj)))

这种运行约40倍比你原来快。 在我的机器,这就是100K 1.5秒。



Answer 3:

我将回答#2,因为它是唯一一个我有任何远程智能地说,但对于Python代码,你创造了一个中间列表is_prime ,而你使用.map在你all的红宝石这是刚刚迭代。

如果你改变你的is_prime到:

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

他们看齐。

我可以进一步优化Python,但我的Ruby是不够好,知道什么时候我已经给更多的优势(例如,使用xrange让Python的我的机器上赢了,但我不记得是否Ruby的范围你使用在内存中创建或不是整个范围)。

编辑:没有被太傻,使得Python代码如下所示:

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((b-a)) + " mils")

不改变得多了,​​把它以1.5秒对我来说,并与被额外傻,与PyPy运行它把它在.3s为10K,和21S为100K。



Answer 4:

您可以通过修改使斯卡拉快了很多isPrime方法

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

不是很简洁,但该方案在40%的时间运行!

我们切出多余的Range和匿名Function对象,Scala编译器识别尾递归并把它变成一个while循环,使JVM可以变成或多或少的最佳机器代码,因此它不应该太遥远C版。

另请参见: 如何优化,内涵在Scala和循环?



Answer 5:

下面是在平行和不平行我的斯卡拉版本,只是为了好玩:(在我的双核计算,其水货版本需要335ms,而没有水货版本需要655ms)

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

编辑:据埃米尔H“的建议下,我改变了我的代码,以避免IO和JVM热身的效果:

结果表明:在我的计算:

名单(3432,1934年,3261,1716,3229,1654,3214,1700)

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


Answer 6:

没关系的基准; 这个问题让我感兴趣,我做了一些快速的调整。 这使用了lru_cache装饰,这memoizes的功能; 所以当我们调用is_prime(i-6)我们基本上可以免费获得这主要检查。 这种变化切割工作大约一半。 此外,我们可以使range()通话步只是奇数,再次将工件大约一半。

http://en.wikipedia.org/wiki/Memoization

http://docs.python.org/dev/library/functools.html

这需要Python 3.2或更新版本获得lru_cache ,但也有老的Python工作,如果你安装一个Python食谱,提供lru_cache 。 如果您在使用Python 2.x的应该要使用xrange()来代替range()

http://code.activestate.com/recipes/577479-simple-caching-decorator/

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

以上只用了很短的时间进行修改。 我决定把它一步,使素数测试只能尽量素因子,也是唯一多达数的平方根进行测试。 顺便我做了,如果你为了检查数字,因此其道理就可以积累所有的素数它只能; 但这个问题已经为了检查号码,这样很好。

在我的笔记本电脑(没有什么特别的处理器是1.5GHz的AMD炫龙II“K625”)这个版本产生了不到8秒,100K的答案。

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

上面的代码是很容易在Python,红宝石等写但会更C中的疼痛的

而无需重写他人使用类似的技巧,你不能比较在此版本与从另一个版本号的数字。 我并不想在这里证明什么; 我只是觉得这个问题很有趣,我想看看我能搜集什么样的方便性能改进。



Answer 7:

不要忘了Fortran语言! (大部分在开玩笑,但我希望类似的性能,以C)。 用感叹号的声明是可选的,但良好的作风。 ( !是Fortran 90的注释字符)

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


Answer 8:

我无法抗拒做一些最明显的优化对于这使得现在的100K测试取为0.3s我的机器上的C版(比问题C版本快5倍,均与MSVC 2010 / OX编译) 。

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

这是在Java中相同的FPGA实现:

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慢,Java版本运行速度比C版本快一点。

这是我对100K的运行时间:

  • 319ms下用/ Ox和输出编译为> NIL:
  • 与/ OX和静态编译柜台Ç312ms
  • 324ms Java客户端虚拟机与输出> NIL:
  • 299ms Java客户端虚拟机的静态反

和1M跑(16386个结果):

  • 24.95s下与/ OX和静态编译柜台
  • 25.08s Java客户端虚拟机的静态反
  • 24.86s Java服务器VM具有静态计数器

虽然这并没有真正回答你的问题,它表明,小的调整可能会对性能产生显着的影响。 因此,为了能够真正比较的语言,你应该尽量避免所有的算法差异尽可能。

这也给了点提示,为何斯卡拉似乎相当快。 它运行在Java虚拟机,因此从它的令人印象深刻的性能优势。



Answer 9:

在Scala中尝试使用Tuple2而不是列表,它应该走得更快。 只是删除,因为(X,Y)是一个Tuple2字“列表”。

Tuple2是专门为中等,长和双这意味着它不会有盒/拆箱那些原始数据类型。 Tuple2源 。 列表不专业。 列表源 。



Answer 10:

下面是围棋(golang.org)版本的代码:

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

它跑一样快,因为C版。

使用华硕u81a英特尔Core 2 Duo T6500 2.1GHz,拥有2MB二级缓存,800MHz前端总线。 4GB内存

100K的版本: C: 2.723s Go: 2.743s

随着1000000(1M,而不是100K): C: 3m35.458s Go: 3m36.259s

但我认为,这将是公平使用Go内置的多线程能力和比较,版本与普通C版(不带多线程),只是因为它几乎是太容易做与围棋多线程。

更新:我的确在旅途中使用够程并行版本:

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
}

平均2.743秒使用的并行版本,所使用的普通版完全相同的时间。

该并行版本1.706秒完成。 它使用小于1.5 MB RAM。

一个奇怪的事情:我的双核64位Kubuntu的两个核心从来没有达到顶峰。 它看起来像围棋中只使用一个核心。 与对呼叫固定runtime.GOMAXPROCS(4)

更新:我跑了paralellized版本升级到100万个的数字。 我的一个CPU核心为100%所有的时间,而另一份没有(奇)使用。 花了整整一分钟比C和经常去的版本更加。 :(

随着1000000(1M,而不是100K):

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

100K的版本:

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



Answer 11:

只是它的乐趣,这里是一个平行的Ruby版本。

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

在我的1.8GHz的酷睿i5的MacBook Air,表现效果:

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

它看起来像JVM的JIT是给红宝石在默认情况下,一个不错的性能提升,而真正的多线程处理有助于JRuby的在螺纹的情况下执行速度提高50%。 什么是更有趣的是JRuby的1.7一个健康的17%提高JRuby的1.6分!



Answer 12:

基于x4u的回答 ,我写了使用递归Scala的版本,并只去开方,而不是X / 2为主要检查功能我就可以改善。 我得到〜250毫秒为100K,并〜600毫秒为1M。 我继续去10M的〜6秒。

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

我还回去,并写了一个CoffeeScript的(V8 JavaScript的)版本,它得到15毫秒〜100K用于为1M 250毫秒,和6S为10M,通过使用计数器(忽略I / O)。 如果我打开输出需要150毫秒〜100K用于为1M 1S,和12S为10M。 无法在这里使用尾递归,不幸的是,所以我不得不将其转换回圈。

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


Answer 13:

在回答你的问题#1:是的,JVM是incredably快,是静态类型的帮助。

在JVM应该比C快,从长远来看,甚至可能快于“正常”汇编语言 - 当然,你总是可以手工优化装配由上工运行分析并创建一个单独的版本为每个CPU刚刚击败任何东西,你必须是好得令人吃惊和见地。

对于Java的速度的原因是:

而它运行和手工优化它的JVM可以分析你的代码 - 例如,如果你有可能被静态地分析,在编译的时候是一个真正的功能的方法和JVM注意到,您经常使用相同的调用它参数,它实际上完全消除了通话,只是注入从上次调用的结果(我不知道如果用Java代码实际上做这个了,但是那行的也有很多这样的东西)。

由于静态类型,JVM可以知道很多关于你的代码在编译时,这让它预优化了相当多的东西。 它也可以让编译器优化个别每一类无其他类计划如何使用它的知识。 此外Java没有任意内存指针的位置,它知道在什么样的内存值可以或不可以变化,可以相应地优化。

堆分配是比C更有效,Java的堆分配更像是在速度C的堆栈分配 - 但更灵活。 很多时候已经进入这里所使用的不同algroithims,它是一门艺术 - 例如,所有的寿命短(如C的堆栈变量)的对象被分配到一个“已知的”自由位置(不寻找一个免费点有足够的空间),并且在一个单一的步骤中的所有释放在一起(如堆栈弹出)。

该JVM可以知道你的CPU架构的怪癖和专门生成机器码为给定的CPU。

该JVM可以加快你的代码中长运后。 就像一个程序移动到一个新的CPU能够加快步伐,将其移动到JVM的一个新版本还可以给你巨大的速度性能taylored到CPU的,当你开始编译你的代码根本不存在的东西ç身体不能做没有recomiple。

顺便说一句,大多数坏名声对Java的速度来自于长的启动时间来加载JVM(总有一天,有人会建立JVM到OS,这将会消失!),事实上,许多开发人员在编写非常糟糕这引起的GUI的Java GUI代码(特别是带螺纹的)经常变得无响应和出问题。 简单的像Java和VB使用语言有其缺点在于,一般的程序员的capibilities往往比复杂的语言下被放大。



文章来源: Interpreting a benchmark in C, Clojure, Python, Ruby, Scala and others [closed]