私は人工的なベンチマークが悪であることを知っています。非常に特定の狭い状況でのみ結果を表示できます。あるばかげたベンチのために、ある言語が他の言語より優れているとは思いません。しかし、なぜ結果がそんなに違うのだろうか。下の質問をご覧ください。
ベンチマークは、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)
テーブル内:計算時間(秒)実行中:Factor以外はすべてVirtualBox(Debian不安定AMD64ゲスト、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/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"
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は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))))))
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")
因子
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
大まかな答え:
(2...n).all?
のis-prime?
は、Ruby(EDIT:これは実際に当てはまります。詳細については、ジュリアンの回答を参照してください...)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は0.635秒で10kを完了します(つまり、リストで2番目に速く、Scalaを破ります)
PS場合によってはベンチマーク内にコードを印刷することに注意してください-特にprint
のような関数を初めて使用すると初期化が発生する場合、結果が歪むので良い考えではありませんIOサブシステムまたはそのようなもの!
同じ基本アルゴリズムを使用した高速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の新しいreducersライブラリを活用するバージョンがあります(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倍高速に実行されます。私のマシンでは、1.5秒で100kです。
#2だけをお答えします。これはリモートで言うことができる唯一のものだからです。しかし、Pythonコードの場合、is_prime
で中間リストを作成しています。 Rubyのall
で.map
を再使用しているだけです。
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で実行すると10Kで.3秒、100Kで21秒になります。
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コンパイラーは末尾再帰を認識し、それをJVMが繰り返しループに変換します多かれ少なかれ最適なマシンコードに変わる可能性があるので、Cバージョンからそれほど遠くないはずです。
ここに私の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の提案によると、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)))
_
上記の編集には非常に短い時間がかかりました。さらに一歩進めて、素数テストで素数除数のみを試し、テストする数の平方根までだけにすることにしました。私がやった方法は、数字を順番に確認する場合にのみ機能するため、すべての素数を累積することができます。しかし、この問題はすでに番号を順番にチェックしていたので、それで問題ありませんでした。
私のラップトップ(特別なものはありません。プロセッサーは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ではさらに苦痛になります。
このバージョンの番号を他のバージョンの番号と比較することはできません。他のバージョンを書き換えて、同様のトリックを使用してください。ここで何かを証明しようとはしていません。この問題は楽しいものだと思ったので、どのような簡単なパフォーマンスの改善が得られるのかを知りたかったのです。
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
Cバージョンの最も明らかな最適化のいくつかを実行することに抵抗できませんでした。これにより、マシンで100kテストが0.3秒かかりました(どちらもMSVC 2010/Oxでコンパイルされた、問題のCバージョンより5倍高速です) 。
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バージョンとほぼ同じ速度で実行されます。クライアントまたはサーバーVMは、JITトレーニングがサーバーを支援するためにVM少し(〜3%)ですが、クライアントVMにはほとんど効果がありません。Javaの出力は、 Cの場合。出力が両方のバージョンで静的カウンタに置き換えられた場合、JavaバージョンはCバージョンよりも少し速く実行されます。
これらは、10万回実行の私の時間です。
および1M実行(結果16386):
これは実際にはあなたの質問に答えるわけではありませんが、小さな調整がパフォーマンスに顕著な影響を与える可能性があることを示しています。したがって、言語を実際に比較できるようにするには、アルゴリズムの違いを可能な限りすべて回避する必要があります。
また、Scalaがかなり速いように見える理由も示します。Java =VMパフォーマンス。
ただの楽しみのために、ここにパラレル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を提供するように見えますが、デフォルトの場合は素晴らしいパフォーマンスが向上しますが、真のマルチスレッドはJRubyがスレッド化された場合に50%高速になります。 JRuby 1.6は健全な17%を獲得しました!
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バージョンと同じ速さで実行されました。
Asus u81aを使用 Intel Core 2 Duo T6500 2.1GHz、2MB L2キャッシュ、800MHz FSB。 4GB RAM
100kバージョン:_C: 2.723s
_ _Go: 2.743s
_
1000000の場合(100Kではなく1M):_C: 3m35.458s
_ _Go: 3m36.259s
_
しかし、Goの組み込みマルチスレッド機能を使用して、そのバージョンを通常のCバージョン(マルチスレッドなし)と比較するのは公平だと思います。Goでマルチスレッドを実行するのはあまりにも簡単だからです。
更新: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秒で完了しました。 1.5 Mb未満のRAMを使用しました。
奇妙なことに、デュアルコアkubuntu 64bitが両方のコアでピークに達することはありませんでした。 Goは1つのコアのみを使用しているように見えました。 runtime.GOMAXPROCS(4)
の呼び出しで修正
更新:100万個までの並列バージョンを実行しました。 私のCPUコアの1つは常に100%でしたが、もう1つはまったく使用されませんでした(奇数)。 Cおよび通常のGoバージョンよりも1分以上かかりました。 :(
1000000(100Kではなく1M):
_C: 3m35.458s
_ _Go: 3m36.259s
_ _Go using goroutines:
_3分27秒137_2m16.125s
_
100kバージョン:
_C: 2.723s
_ _Go: 2.743s
_ _Go using goroutines: 1.706s
_
x4u's answer に基づいて、scala再帰を使用してバージョンを作成し、プライムチェック関数のx/2の代わりにsqrtのみを使用して改善しました。100kで最大250ms、1Mで最大600msを取得し、6秒で10Mに進みました。
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で1秒、10Mで12秒かかります。残念ながら、ここでは末尾再帰を使用できなかったため、ループに戻す必要がありました。
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")
あなたの質問#1に対する答えは、はい、JVMは非常に高速であり、はい静的型付けが役立つということです。
JVMは、長期的にはCよりも高速であり、「通常」のアセンブリ言語よりも高速である必要があります-もちろん、手動のランタイムプロファイリングを実行し、CPUごとに別個のバージョンを作成することで、いつでもアセンブリを最適化して手作業で最適化できます驚くほど良い知識を持っている必要があります。
Javaの速度の理由は次のとおりです。
JVMは実行中にコードを分析し、手作業で最適化できます。たとえば、コンパイル時に静的に分析できるメソッドがあり、真の関数であり、JVMが頻繁に同じメソッドで呼び出していることに気付いた場合パラメータ、それは実際に呼び出しを完全に排除し、最後の呼び出しから結果を注入するだけです(Javaは実際にこれを正確に行いますが、このようなものは多くありません) 。
静的型付けにより、JVMはコンパイル時にコードについて多くのことを知ることができます。これにより、かなり多くのものを事前に最適化できます。また、コンパイラは、別のクラスがどのようにそれを使用することを計画しているかを知らなくても、各クラスを個別に最適化できます。また、Javaにはメモリ位置への任意のポインタがありません。メモリ内の値が変更される場合と変更されない場合があり、それに応じて最適化できます。
ヒープ割り当てはCよりもはるかに効率的です。Javaのヒープ割り当ては、Cのスタック割り当てに似ていますが、より汎用的です。ここで使用されているさまざまなアルゴリズムに多くの時間が費やされています。これは芸術です。たとえば、寿命の短いオブジェクト(Cのスタック変数など)はすべて「既知の」空き場所に割り当てられます(空きスポットの検索はありません)十分なスペースがあります)、すべてが単一のステップ(スタックポップなど)で一緒に解放されます。
JVMはCPUアーキテクチャに関する癖を知り、特定のCPU専用のマシンコードを生成できます。
JVMは、コードを出荷してからずっと後にコードを高速化できます。プログラムを新しいCPUに移動すると速度が上がるのと同じように、新しいバージョンのJVMに移動すると、コードを最初にコンパイルしたときにさえ存在しなかったCPUに合わせた巨大な速度パフォーマンスが得られます。再確認なしで行います。
ところで、Java速度はJVMをロードするための長い起動時間に起因します(誰かがJVMをOSに組み込み、これはなくなります!)多くの開発者がGUIコード(特にスレッド化)を書くのが本当に苦手であるため、Java GUIが応答しなくなり、不具合が生じることがあります。JavaおよびVBは、平均的なプログラマの能力がより複雑な言語よりも低くなる傾向があるという事実によって、その欠点を増幅させています。