私は当初、「if-then-else」を使用する場合と Haskell のガードを使用する場合で速度に違いがないことを確認する目的で、素数を計算するこの (強引で非効率的な) メソッドを書きました (違いはありません!)。しかし、比較のために C プログラムを作成することにしたところ、次の結果が得られました (Haskell は 25% 以上遅くなりました)。
(次の投稿からコンパイラ呼び出しで mod の代わりに rem を使用し、O3 オプションも使用するというアイデアを得たことに注意してください。
Haskell : Forum.hs
divisibleRec :: Int -> Int -> Bool
divisibleRec i j
| j == 1 = False
| i `rem` j == 0 = True
| otherwise = divisibleRec i (j-1)
divisible::Int -> Bool
divisible i = divisibleRec i (i-1)
r = [ x | x <- [2..200000], divisible x == False]
main :: IO()
main = print(length(r))
C : main.cpp
#include <stdio.h>
bool divisibleRec(int i, int j){
if(j==1){ return false; }
else if(i%j==0){ return true; }
else{ return divisibleRec(i,j-1); }
}
bool divisible(int i){ return divisibleRec(i, i-1); }
int main(void){
int i, count =0;
for(i=2; i<200000; ++i){
if(divisible(i)==false){
count = count+1;
}
}
printf("number of primes = %d\n",count);
return 0;
}
私が得た結果は次のとおりです。
コンパイル時間
time (ghc -O3 -o runProg Forum.hs)
real 0m0.355s
user 0m0.252s
sys 0m0.040s
time (gcc -O3 -o runProg main.cpp)
real 0m0.070s
user 0m0.036s
sys 0m0.008s
および次の実行時間 :
Ubuntu 32 ビットでの実行時間
Haskell
17984
real 0m54.498s
user 0m51.363s
sys 0m0.140s
C++
number of primes = 17984
real 0m41.739s
user 0m39.642s
sys 0m0.080s
Haskell の実行時間には非常に感銘を受けました。ただし、私の質問は次のとおりです。haskellプログラムを高速化するために何かできることはありますか:
- 基礎となるアルゴリズムの変更 (アルゴリズムを変更することで大幅なスピードアップが得られることは明らかですが、パフォーマンスを向上させるために言語/コンパイラ側で何ができるかを理解したいだけです)
- llvm コンパイラを呼び出します (これがインストールされていないため)
[編集:メモリ使用量]
Alan のコメントの後、Haskell プログラムはメモリ サイズがゆっくりと大きくなるのに対し、C プログラムは一定量のメモリを使用することに気付きました。最初は、これは再帰に関係していると思っていましたが、gspr は、なぜこれが起こっているのかを以下に説明し、解決策を提供します。Will Ness は、(gspr のソリューションと同様に) メモリが静的なままであることを保証する代替ソリューションを提供します。
[編集:より大きなランのまとめ]
テストされた最大数: 200,000:
(54.498s/41.739s) = Haskell 30.5% 遅い
テストされた最大数: 400,000:
3m31.372s/2m45.076s = 211.37s/165s = Haskell 28.1% 遅い
テストされた最大数: 800,000:
14m3.266s/11m6.024s = 843.27s/666.02s = Haskell 26.6% 遅い
[編集: アランのコード]
これは、再帰を持たず、 200,000 でテストした以前に書いたコードです。
#include <stdio.h>
bool divisibleRec(int i, int j){
while(j>0){
if(j==1){ return false; }
else if(i%j==0){ return true; }
else{ j -= 1;}
}
}
bool divisible(int i){ return divisibleRec(i, i-1); }
int main(void){
int i, count =0;
for(i=2; i<8000000; ++i){
if(divisible(i)==false){
count = count+1;
}
}
printf("number of primes = %d\n",count);
return 0;
}
再帰がある場合とない場合の C コードの結果は次のとおりです (800,000 の場合)。
再帰あり: 11m6.024s
再帰なし: 11m5.328s
実行可能ファイルは、最大数に関係なく (システム モニターで見られるように) 60kb を使用しているように見えるため、コンパイラがこの再帰を検出していると思われることに注意してください。