Haskell / GHCでボックス化されていないタイプ(int#など)を使用している場合、どのようなものに注意する必要がありますか?
-
30-09-2019 - |
質問
Brainfuckコードを解析および実行する小さなスクリプトを作成し、最適化のGHCオプションを理解するために、コードを最適化して、少し速くなり、そこで何が起こっているのかを理解しようとしています。
部品の上には、BFコードの内部表現があり、これには特別なデータ型を使用します。これは、コンバージョンを実行している2つの関数を含むSourceCodeです。
data BFinstruction
= AdjustValue Int
| MovePointer Int
| GetChar
| PutChar
| Loop BFcode
deriving (Eq)
type BFcode = [BFinstruction]
unsafeCompileBrainfuck :: String -> BFcode
unsafeCompileBrainfuck = fst . parse [] where
-- arguments: input string, built code; output: output code, rest of input
parse :: BFcode -> String -> (BFcode,String)
parse c ('+':s) = parse (AdjustValue 1 :c) s
parse c ('-':s) = parse (AdjustValue (-1):c) s
parse c ('>':s) = parse (MovePointer 1 :c) s
parse c ('<':s) = parse (MovePointer (-1):c) s
parse c ('.':s) = parse (PutChar :c) s
parse c (',':s) = parse (GetChar :c) s
parse c (']':s) = (reverse c, s)
parse c ('[':s) = parse (Loop l :c) s' where (l,s') = parse [] s
parse c [] = (reverse c ,"")
parse c ( _ :s) = parse c s
simplifyBrainfuck :: BFcode -> BFcode
simplifyBrainfuck ((AdjustValue x):(AdjustValue y):zs) = if x + y /= 0
then simplifyBrainfuck (AdjustValue (x + y):zs)
else simplifyBrainfuck zs
simplifyBrainfuck ((MovePointer x):(MovePointer y):zs) = if x + y /= 0
then simplifyBrainfuck (MovePointer (x + y):zs)
else simplifyBrainfuck zs
simplifyBrainfuck (x :zs) = x: simplifyBrainfuck zs
simplifyBrainfuck [] = []
アイデアは、コードが何らかの入力(文字列)から読み取られ、上記のコードによって事前に分割され、単純化され、他の関数によって実行されるということです。 (入力が有効であると想定されています)。
この例を最適化するために、私はのintパラメーションのボックスを開けようとしました MovePointer
と AdjustValue
このようなドメトをすることでコンストラクター:
data BFinstruction -- BangPatterns
= AdjustValue {-# UNPACK #-} !Int
| MovePointer {-# UNPACK #-} !Int
| GetChar
| PutChar
| Loop BFcode
deriving (Eq)
これにより、箱入りが変わります Int
ボックス化されていない生に入力します Int#
タイプ。これはGHCの実装の詳細です。私が読んだように、このオプションはいくつかのケースでのみ良いので、私はこの種の最適化を実行したい場合、私が注意を払わなければならないことを尋ねたいです。私の目標は、Haskell -Laziness(私はアーカイブしたい、コードが必要に応じてメモリに保持される可能性がある)と簡単なことを使用してBFコードの実行を許可することです。
解決
これは本当に私にとって時期尚早の最適化のように見えます。開梱は、非常に多数がある場合にほとんど役立ちます BFInstruction
座っている。私はあなたがそれを価値のあるものにするのに十分なBrainf ** kコードを持っていることを疑います。私はジャンに同意します、それはテストするのに十分単純なはずですので、最初にそれを行います。
いずれにせよ、未梱包の値で注意すべきことは、コンパイラにそれらを再箱に入れなければならないことを望んでいないことです。数値opsでは問題ないはずですが、それ以外は、使用している関数を注意深く調べて、未張りの値を非厳格な引数として使用したかどうかを確認する必要があります。確実に確実にする唯一の方法は、コアまたはインターフェイスファイルを見ることで、どのパラメーターが厳格でないかを確認することです。いつものように、少なくとも「-o」でコンパイルしてください。
他のヒント
これは本当に必要ですか?このコードでパフォーマンスの問題に遭遇していますか?そうでない場合は、気にしないでください。
あなたがこれが事実であると信じているなら、 GHCマニュアルのこのページ 便利なリスト形式で必要な制限を提供しているようです。
主なポイントは、コンパイラによって拒否されない多型関数または名前と名前のないタイプとの間のあらゆる種類の相互作用が、依然として厄介なスペース漏れにつながる可能性があることです。また、試してみることなく、たとえばオーバーフローの場合には例外が投げられないと思うので、おそらく自分でこの種のことを検出する必要があります。簡単なテストでは、これが実際に当てはまるかどうかを確認できます。