22

この記事によると、

GHCに関する限り、列挙型は単一のコンストラクター型としてカウントされないため、厳密なコンストラクターフィールドまたは厳密な関数引数として使用された場合、列挙型はアンパックの恩恵を受けません。これは GHC の欠陥ですが、回避できます。

代わりに、newtypes の使用が推奨されます。ただし、次のコードではこれを確認できません。

{-# LANGUAGE MagicHash,BangPatterns #-}
{-# OPTIONS_GHC  -O2 -funbox-strict-fields -rtsopts -fllvm -optlc --x86-asm-syntax=intel #-}
module Main(main,f,g)
where       
import GHC.Base  
import Criterion.Main

data D = A | B | C
newtype E = E Int deriving(Eq)

f :: D -> Int#
f z | z `seq` False = 3422#
f z = case z of
  A -> 1234#
  B -> 5678#
  C -> 9012#

g :: E -> Int#
g z | z `seq` False = 7432#
g z = case z of
  (E 0) -> 2345#
  (E 1) -> 6789#
  (E 2) -> 3535#

f' x = I# (f x)
g' x = I# (g x)

main :: IO ()
main = defaultMain [ bench "f" (whnf f' A) 
                   , bench "g" (whnf g' (E 0)) 
                   ]

アセンブリを見ると、列挙型 D の各コンストラクターのタグは実際にはアンパックされ、命令に直接ハードコーディングされています。さらに、この関数fにはエラー処理コードがなく、より 10% 以上高速ですg。より現実的なケースでは、列挙型を newtype に変換した後にスローダウンも経験しました。誰かが私にこれについての洞察を与えることができますか? ありがとう。

4

2 に答える 2

18

ユースケースによって異なります。あなたが持っている関数については、列挙のパフォーマンスが向上することが期待されます。基本的に、 の 3 つのコンストラクターはそれぞれsにDなります。s 厳密性解析でそれが許可され、引数が 3 つの値のうちの 1 つしか持てないことが静的にチェックされていることを GHC が認識している場合。したがって、 のエラー処理コードを挿入する必要はありません。の場合、3 つの値のうちの 1 つだけが可能であるという静的な保証が与えられていないため、 のエラー処理コードを追加する必要があり、処理が大幅に遅くなります。最後のケースになるようにの定義を変更するとIntInt#0#, 1#, 2#fEgg

E _ -> 3535#

違いは完全に、またはほぼ完全に消えます (fまだ 1% ~ 2% 優れたベンチマークが得られますが、それが実際の違いなのか、ベンチマークの成果物なのかを確認するのに十分なテストを行っていません)。

しかし、これは wiki ページが話している使用例ではありません。それが話しているのは、型が他のデータのコンポーネントである場合、コンストラクターを他のコンストラクターにアンパックすることです。

data FooD = FD !D !D !D

data FooE = FE !E !E !E

次に、 でコンパイルすると-funbox-strict-fields、3 つInt#の s を のコンストラクターにアンパックできるFooEため、基本的には と同等のものを取得できます

struct FooE {
    long x, y, z;
};

のフィールドはFooDマルチコンストラクター型であり、コンストラクター(1)Dにアンパックできないため、基本的にはFD

struct FooD {
    long *px, *py, *pz;
}

それは明らかに大きな影響を与える可能性があります。

単一コンストラクター関数の引数のケースについてはわかりません。これには、タプルなどのデータが含まれる型には明らかな利点がありますが、単純な列挙型にそれがどのように適用されるかはわかりませんcase。ワーカーとラッパーを分割するだけでは意味がありません(私には)。

とにかく、worker/wrapper 変換は単一のコンストラクターではなく、コンストラクターの特殊化により、コンストラクターの少ない型に同じ利点を与えることができます。(作成されるコンストラクターの特殊化の数は、の値によって異なります-fspec-constr-count。)


(1)それは変わったかもしれないが、私はそうは思わない. 確認していないので、ページが古くなっている可能性があります。

于 2012-10-12T21:03:30.107 に答える
5

そのページが2008年に最後に更新されて以来、GHCはかなり変更されたと思います。また、LLVMバックエンドを使用しているため、パフォーマンスにも何らかの影響がある可能性があります. -O2GHC は からすべてのエラー処理コードを取り除くfことができます (そして、あなたが を使ったのでそうします) f。についても同じことが言えませんgf分岐条件で他に何も使用されていないことが簡単にわかるため、 のコンストラクター タグをアンパックするのは LLVM バックエンドだと思います。しかし、私はそれについて確信が持てません。

于 2012-10-12T20:48:39.377 に答える