在C,Clojure,Python,Ruby,Scala等中解释基准
放弃
我知道人造基准是邪恶的。 他们只能针对非常狭窄的情况显示结果。 我不认为一种语言比另一种语言好,因为一些愚蠢的板凳。 不过,我想知道为什么结果如此不同。 请在底部查看我的问题。
math基准描述
基准是简单的math计算,以find相差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 unstable amd64 guest,Windows 7 x64主机) 中运行的所有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/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")
斯卡拉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 (- 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))))))
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((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 . .
巴什(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
问题
- 为什么斯卡拉这么快? 是因为静态打字吗? 或者它只是非常有效地使用JVM?
-
为什么Ruby和Python有如此巨大的差异?我认为这两者没有什么不同。也许我的代码是错误的。请赐教!谢谢。UPD是的,这是我的代码中的错误。 Python和Ruby 1.9是相当的。 - Ruby版本之间的生产力真的让人印象深刻。
- 我可以通过添加types声明来优化Clojure代码吗? 它会帮助吗?
粗糙的答案:
- Scala的静态types在这里帮助很大 – 这意味着它可以非常高效地使用JVM,而不需要太多额外的工作。
- 我不太确定Ruby / Python的区别,但我怀疑
(2...n).all?
在函数中is-prime?
很可能在Ruby中相当优化(编辑:这听起来确实如此,请参阅Julian的更详细的答案…) - Ruby 1.9.3只是更好的优化
- 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))))))
通过这个改进,我得到了Clojure在0.635秒内完成了10K(即在你的名单上第二快,击败了Scala)
PS请注意,在某些情况下,您在基准testing程序中打印了代码 – 这不是一个好主意,因为它会扭曲testing结果,特别是如果首次使用像print
这样的函数会引起IO子系统初始化或类似情况!
我只会回答#2,因为它是唯一一个可以远程智能说的东西,但是对于你的Python代码,你在is_prime
创build了一个中间列表,而在你all
中使用.map
Ruby只是迭代。
如果您将您的is_prime
更改为:
def is_prime(n): return all((n%j > 0) for j in range(2, n))
他们是平等的。
我可以进一步优化Python,但是我的Ruby还不够好,无法知道何时可以获得更多的优势(例如,使用xrange
使得Python在我的机器上获胜,但我不记得您使用的Ruby范围在内存中创build一个完整的范围或不)。
编辑:不要太愚蠢,使得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((ba)) + " mils")
这个变化没有太大的变化,对于我来说是1.5s,而且,如果用更多的愚蠢的话,用PyPy运行它可以使得它在10K时为0.3s,在100K时为21s。
这是一个快速的Clojure版本,使用相同的基本algorithm:
(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]))
它比我的机器上原来的速度快了20倍。 下面是一个利用1.5中新的reducer库的版本(需要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 ([] []) ([ab] (into ab))) conj)))
这比你的原始速度快了40倍。 在我的机器上,这在1.5秒内是100k。
你可以通过修改你的isPrime
方法来使Scala更快
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编译器识别尾recursion并将其变成一个while循环,JVM可以变成更多或更less的最佳机器代码,所以它不应该太远C版本。
另请参阅: 如何优化在Scala中的理解和循环?
下面是我的Scala版本,并行和不平行,只是为了好玩:(在我的双核计算中,并行版本需要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)) } }
编辑:据Emil H的build议,我已经改变了我的代码,以避免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) } }
没关系的基准; 问题让我感兴趣,我做了一些快速的调整。 这使用lru_cache
装饰器,它记忆一个函数; 所以当我们打电话给is_prime(i-6)
我们基本上可以免费获得这个主要检查。 这一改变大致将工作削减了一半。 另外,我们可以使range()
调用步骤通过奇数,大致再减半。
http://en.wikipedia.org/wiki/Memoization
http://docs.python.org/dev/library/functools.html
这需要Python 3.2或更高版本才能获得lru_cache
,但如果安装提供lru_cache
的Python配方,则可以使用较旧的Python。 如果你使用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)))
上面只花了很短的时间来编辑。 我决定更进一步,使素数testing只尝试素因数,只能达到被测数字的平方根。 我这样做的方式只有在你检查数字时才有用,所以它可以积累所有素数。 但这个问题已经在检查数字,所以没问题。
在我的笔记本电脑(没有什么特别的;处理器是1.5 GHz的AMD Turion 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,Ruby等书写,但在C中会更痛苦。
你不能把这个版本的数字与其他版本的数字进行比较,而不用重写其他版本来使用类似的技巧。 我并不想在这里certificate任何东西。 我只是觉得这个问题很有趣,我想看看我可以收集哪些简单的性能改进。
别忘了Fortran! (大部分是在开玩笑,但我期望C的performance类似)。 感叹号的声明是可选的,但风格很好。 ( !
是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
我忍不住要为C版本做一些最显而易见的优化,这使得100ktesting现在在我的机器上花费了0.3s(比问题中的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中的相同实现:
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版本要快一些。
这是我10万次运行的时代:
- 319ms C用/ Ox编译并输出到> NIL:
- 使用/ Ox和静态计数器编译312ms C
- 324ms Java客户端虚拟机,输出为> NIL:
- 299毫秒的Java客户端虚拟机与静态计数器
和1M跑(16386结果):
- 用/ Ox和静态计数器编译24.95s
- 具有静态计数器的25.08s Java客户端VM
- 具有静态计数器的24.86s Java服务器虚拟机
虽然这并不能真正回答您的问题,但它表明小的调整可能会对性能产生显着的影响。 所以为了能够真正地比较语言,你应该尽量避免所有的algorithm差异。
它也提供了一个暗示,为什么斯卡拉似乎相当快。 它运行在Java虚拟机上,从而获得了令人印象深刻的性能。
在斯卡拉尝试使用Tuple2而不是List,它应该会更快。 因为(x,y)是一个Tuple2,所以删除单词“List”。
Tuple2专门用于Int,Long和Double,这意味着它不必对这些原始数据types进行装箱/取消装箱。 Tuple2来源 。 列表不是专门的。 列出来源 。
这里是Go(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英特尔酷睿2双核T6500 2.1GHz,2MB二级caching,800MHz前端总线。 4GB内存
100k版本: C: 2.723s
Go: 2.743s
用1000000(1M而不是100K): C: 3m35.458s
Go: 3m36.259s
但是我认为使用Go内置的multithreadingfunction并且将该版本与常规C版本(不使用multithreading)进行比较是公平的,仅仅因为使用Go进行multithreading就太简单了。
更新:我在Go中使用Goroutines做了一个并行版本:
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秒完成。 它使用less于1.5 Mb的RAM。
一件奇怪的事情是:我的双核kubuntu 64bit在两个内核中都没有达到峰值。 看起来Go只用了一个核心。 通过调用runtime.GOMAXPROCS(4)
更新:我运行了1M以上的并行版本。 我的一个CPU核心一直是100%,而另一个根本不使用(奇数)。 比C和常规的Go版本花费了更多的时间。 🙁
用1000000(1M而不是100K):
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
基于x4u的回答 ,我写了一个使用recursion的scala版本,并且我只改进了sqrt而不是x / 2来检查主要检查函数。 10万〜250ms,1M〜600ms。 我继续前进,在6秒内跑到10米。
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)版本,通过使用一个计数器(忽略I / O),可以得到100k的时间约为15ms,1M的时间为250ms,10M的时间为6s。 如果打开输出,100k需要150ms,1M需要1s,10M需要12s。 不幸的是,在这里不能使用尾recursion,所以我不得不把它转换回循环。
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")
为了它的乐趣,这里是一个平行的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 Core 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在默认情况下给了Ruby一个很好的性能提升,而真正的multithreading有助于JRuby在线程情况下执行速度提高了50%。 更有趣的是JRuby 1.7将JRuby 1.6的分数提高了17%!
你的问题#1的答案是,是的,JVM速度非常快,是静态打字帮助。
从长远来看,JVM应该比C更快,甚至可能比“普通”汇编语言更快 – 当然,您可以通过手动运行时间分析和每个CPU创build单独的版本来手动优化程序集,必须是惊人的好,知识渊博。
Java的速度的原因是:
JVM可以在运行时对代码进行分析并对其进行手动优化 – 例如,如果您有一个可以在编译时静态分析的方法是一个真正的函数,并且JVM注意到您经常使用相同的方法调用它参数,它可能实际上完全消除了调用,只是注入了最后一次调用的结果(我不确定Java是否真的这样做,但它做了很多这样的东西)。
由于静态types的input,JVM可以在编译时知道很多关于你的代码的信息,这使得它可以预先优化相当多的东西。 它还使编译器能够单独优化每个类,而无需知道另一个类是如何计划使用它的。 另外Java没有任何指向内存位置的指针,它知道内存中的哪些值可能也可能不会被改变,并且可以相应地进行优化。
堆的分配比C更有效率,Java的堆分配更像C的堆栈分配速度 – 但更多function。 A lot of time has gone into the different algroithims used here, it's an art–for instance, all the objects with a short lifespan (like C's stack variables) are allocated to a "known" free location (no searching for a free spot with enough space) and are all freed together in a single step (like a stack pop).
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.