go
worker tail-recursive loop patternは、純粋なコードを書くために非常にうまくいくようです。 ST
モナドのためのそのようなループを書くのと同等の方法は何でしょうか?より具体的には、私はループの反復で新しいヒープ割り当てを避けたい。私の推測では、CPS transformation
またはfixST
のいずれかが含まれているため、ループ全体で変化するすべての値が各繰り返しにわたって渡されるため、レジスタ位置(または流出の場合はスタック)を反復。私はgo
ワーカーパターンを持っていますが、変化する状態値はアキュムレータ引数を通さないfindSnakes
という関数を含む下記の単純化された例を(実行しようとしません - セグメンテーションフォールトでクラッシュする可能性があります):STモナドのための効率的な反復ループの作成
{-# LANGUAGE BangPatterns #-}
module Test where
import Data.Vector.Unboxed.Mutable as MU
import Data.Vector.Unboxed as U hiding (mapM_)
import Control.Monad.ST as ST
import Control.Monad.Primitive (PrimState)
import Control.Monad as CM (when,forM_)
import Data.Int
type MVI1 s = MVector (PrimState (ST s)) Int
-- function to find previous y
findYP :: MVI1 s -> Int -> Int -> ST s Int
findYP fp k offset = do
y0 <- MU.unsafeRead fp (k+offset-1) >>= \x -> return $ 1+x
y1 <- MU.unsafeRead fp (k+offset+1)
if y0 > y1 then return y0
else return y1
{-#INLINE findYP #-}
findSnakes :: Vector Int32 -> MVI1 s -> Int -> Int -> (Int -> Int -> Int) -> ST s()
findSnakes a fp !k !ct !op = go 0 k
where
offset=1+U.length a
go x k'
| x < ct = do
yp <- findYP fp k' offset
MU.unsafeWrite fp (k'+offset) (yp + k')
go (x+1) (op k' 1)
| otherwise = return()
{-#INLINE findSnakes #-}
(cmm
の私の限られた知識で - 私はそれが間違って得た場合、私を修正してください)ghc 7.6.1
にcmm
出力を見ると、私は、各反復でヒープの割り当てやヒープチェックを起こしs1tb_info
のループ(と、コールフローのこの種を見ます):
findSnakes_info -> a1_r1qd_info -> $wa_r1qc_info (new stack allocation, SpLim check)
-> s1sy_info -> s1sj_info: if arg > 1 then s1w8_info else R1 (can't figure out
what that register points to)
-- I am guessing this one below is for go loop
s1w8_info -> s1w7_info (big heap allocation, HpLim check) -> s1tb_info: if arg >= 1
then s1td_info else R1
s1td_info (big heap allocation, HpLim check) -> if arg >= 1 then s1tb_info
(a loop) else s1tb_info (after executing a different block of code)
cmm
コード内のarg >= 1
という形式のチェックは、go
ループが終了したかどうかを判断することです。それが正しいとすれば、ループがループ全体にyp
を渡すように書き換えられない限り、新しい値のためにループ全体でヒープ割り当てが行われます(yp
がそのヒープ割り当てを引き起こしていると推測しています)。上の例でループをgo
と書くと効率的な方法は何でしょうか?私はyp
がgo
ループの引数として、または同等の方法でfixST
またはCPS
の変換で渡されなければならないと思います。私はヒープ割り当てを削除する上記のループgo
を書き直す良い方法を考えることができず、それに助けていただければ幸いです。