2

私は Haskell をいじっていましたが、これら 2 つの関数のパフォーマンスに違いがあるかどうか疑問に思いました。

count :: (Eq a) => [a] -> a -> Int
count xs e = foldr countCheck 0 xs
    where countCheck x
            | x == e = (1+)
            | otherwise = (0+)

count' :: (Eq a) => [a] -> a -> Int
count' xs e = foldr countCheck 0 xs
    where countCheck x acc
            | x == e = acc + 1
            | otherwise = acc

ベンチマークとして次のコードを試してみました: main = print (count [1..10000] 1)、その結果、最初の (部分的に適用された を使用した+) 関数が平均してわずかに高速になりました。

私が主に疑問に思ったのは、私にとってcountは、count'. 「賢い」ということ以外に、もう 1 つのトレードオフは高速であることだと思いました。では、部分的に適用された関数を使用すると、コードの実行が速くなるでしょうか? もしそうなら、なぜですか?

4

2 に答える 2

5

さて、私は次のプログラムからコアを見ました

main = print (count [1..100000] 1)

main = print (count'  [1..100000] 1)

これらをコンパイルして結果のコアをきれいにすると、ほとんど同じファイルが得られます。それらは同一であるため、[1..10000]andについての面白くない部分を削除しました。print

カウント:

main_e = __integer 1
main7 = \ x -> x
main6 =
  \ a -> case a of _ { I# b -> I# (+# 1 b) }
main5 =
  \ x ->
    case eqInteger x main_e of _ {
      False -> main7;
      True -> main6
    }

カウント':

main_e = __integer 1
main5 =
  \ x acc ->
    case eqInteger x main_e of _ {
      False -> acc;
      True -> case acc of _ { I# x1 -> I# (+# x1 1) }
    }

したがって、実際にはほぼ同じコアをcount生成しますが、わずかに複雑なコアを生成します。ただし、ラムダ抽象化をインライン化して 1 つに持ち上げると、完全に同一のコアが得られますmain6main7

GHC はすべての最適化を実行できるので、コードのパフォーマンスの違いに非常に驚かされることでしょう。そして、それが賛成だった場合はさらに驚くcount.

Haskell の真髄は、クリーンなプログラムを作成し、コンパイラーに高速化を任せることです。非foldrバージョンの方が明確な場合は、それを書きます。foldしかし、数年間 s を読んだ後、foldrバージョンがより明確に見えるので、それを書きます。ただし、どちらも同じコードにコンパイルされるため、どちらを使用してもかまいません。

于 2013-10-30T02:19:56.330 に答える
1

十分に高いレベルの最適化でコンパイルされたプログラムで部分的に適用された関数を使用しても、速度はほとんど向上せず、遅くなるだけです。この場合、コンパイラに提供するインテントに関する情報が少ないためです。

例外として、部分的に適用された関数に名前とINLINEプラグマが指定されている場合 (いくつかの特殊なケース)。これはGHCの一種のヒントです。

于 2013-10-30T06:24:44.440 に答える