14

Haskell コンパイラと C++ コンパイラが同じ方法で関数呼び出しを最適化できるかどうかお尋ねしたいと思います。次のコードを見てください。次の例では、Haskell は C++ よりも大幅に高速です。

Haskell は LLVM にコンパイルでき、LLVM パスによって最適化できると聞いたことがあります。さらに、Haskell には内部でいくつかの大幅な最適化が行われていると聞いています。ただし、次の例は同じパフォーマンスで機能するはずです。私は尋ねたい:

  1. C++ のサンプル ベンチマークが Haskell のベンチマークよりも遅いのはなぜですか?
  2. コードをさらに最適化することは可能ですか?

(LLVM-3.2 と GHC-7.6 を使用しています)。

C++ コード:

#include <cstdio>
#include <cstdlib>

int b(const int x){
    return x+5;
}

int c(const int x){
    return b(x)+1;
}

int d(const int x){
    return b(x)-1;
}

int a(const int x){
    return c(x) + d(x);
}

int main(int argc, char* argv[]){
    printf("Starting...\n");
    long int iternum = atol(argv[1]);
    long long int out = 0;
    for(long int i=1; i<=iternum;i++){
        out += a(iternum-i);
    }
    printf("%lld\n",out);
    printf("Done.\n");
}

でコンパイルclang++ -O3 main.cpp

ハスケルコード:

module Main where
import qualified Data.Vector as V
import System.Environment
b :: Int -> Int
b x = x + 5
c x = b x + 1
d x = b x - 1
a x = c x + d x
main = do
   putStrLn "Starting..."
   args <- getArgs
   let iternum = read (head args) :: Int in do
      putStrLn $ show $ V.foldl' (+) 0 $ V.map (\i -> a (iternum-i))
         $ V.enumFromTo 1 iternum
      putStrLn "Done."

でコンパイルghc -O3 --make -fforce-recomp -fllvm ghc-test.hs

速度の結果:

ここに画像の説明を入力

Running testcase for program 'cpp/a.out'
-------------------
cpp/a.out 100000000      0.0%   avg time: 105.05 ms 
cpp/a.out 200000000      11.11% avg time: 207.49 ms 
cpp/a.out 300000000      22.22% avg time: 309.22 ms 
cpp/a.out 400000000      33.33% avg time: 411.7 ms 
cpp/a.out 500000000      44.44% avg time: 514.07 ms 
cpp/a.out 600000000      55.56% avg time: 616.7 ms 
cpp/a.out 700000000      66.67% avg time: 718.69 ms
cpp/a.out 800000000      77.78% avg time: 821.32 ms 
cpp/a.out 900000000      88.89% avg time: 923.18 ms 
cpp/a.out 1000000000     100.0% avg time: 1025.43 ms


Running testcase for program 'hs/main'
-------------------
hs/main 100000000    0.0%   avg time: 70.97 ms (diff: 34.08)
hs/main 200000000    11.11% avg time: 138.95 ms (diff: 68.54)
hs/main 300000000    22.22% avg time: 206.3 ms (diff: 102.92)
hs/main 400000000    33.33% avg time: 274.31 ms (diff: 137.39)
hs/main 500000000    44.44% avg time: 342.34 ms (diff: 171.73)
hs/main 600000000    55.56% avg time: 410.65 ms (diff: 206.05)
hs/main 700000000    66.67% avg time: 478.25 ms (diff: 240.44)
hs/main 800000000    77.78% avg time: 546.39 ms (diff: 274.93)
hs/main 900000000    88.89% avg time: 614.12 ms (diff: 309.06)
hs/main 1000000000   100.0% avg time: 682.32 ms (diff: 343.11)

EDIT もちろん、言語の速度を比較することはできませんが、実装の速度を比較します。

しかし、Ghc と C++ のコンパイラが関数呼び出しを同じように最適化できるかどうか、私は興味があります

あなたの助けに基づいて、新しいベンチマークとコードで質問を編集しました:)

4

3 に答える 3

10

ghc はリストを融合しません (どんな犠牲を払っても成功を避けますか?)

stream-fusionパッケージを使用するバージョンは次のとおりです。

module Main where

import Prelude hiding (map, foldl)
import Data.List.Stream
import Data.Stream (enumFromToInt, unstream)
import Text.Printf
import Control.Exception
import System.CPUTime

b :: Int -> Int
b x = x + 5
c x = b x + 1
d x = b x - 1

a x = c x + d x

main = do
    putStrLn "Starting..."
    putStrLn $ show $ foldl (+) 0 $ map (\z -> a z) $ unstream $ enumFromToInt 1 100000000 
    putStrLn "Done."

結果と比較するためにllvmをインストールしていませんが、バージョンよりも10倍高速です(llvmなしでコンパイル)。

ベクトル フュージョンはさらに高速に実行する必要があると思います。

于 2013-06-27T14:04:20.183 に答える