【问题标题】:Adding random number generation to the STM monad in Haskell向 Haskell 中的 STM monad 添加随机数生成
【发布时间】:2015-07-10 16:06:15
【问题描述】:

我目前正在 Haskell 中进行一些事务性内存基准测试,并希望能够在事务中使用随机数。我目前正在使用来自here 的 Random monad/monad 转换器。在下面的示例中,我有一个包含整数的 tvar 数组和一个在数组中随机选择 10 个 tvar 进行递增的事务,例如:

tvars :: STM (TArray Int Int)
tvars = newArray (0, numTVars) 0

write :: Int -> RandT StdGen STM [Int]
write 0 = return []
write i = do
    tvars <- lift tvars
    rn <- getRandomR (0, numTVars)
    temp <- lift $ readArray tvars rn
    lift $ writeArray tvars rn (temp + 1)
    rands <- write (i-1)
    lift $ return $ rn : rands

我想我的问题是“这是最好的方法吗?”似乎反过来会更自然/更有效,即将随机单子提升到 STM 单子中。每个事务进行大量的 STM 操作,很少进行随机操作。我会假设每个lift 都会增加一些开销。仅lift 随机计算而不单独处理 STM 计算不是更有效吗?这甚至安全吗?似乎定义一个 STM monad 转换器会破坏我们使用 STM monad 获得的良好的静态分离属性(即,我们可以将 IO 提升到 STM monad,但是如果事务中止并呈现问题的数量)。我对 monad 转换器的了解非常有限。非常感谢您对使用变压器的性能和相对开销进行简要说明。

【问题讨论】:

    标签: haskell random transactions transactional-memory


    【解决方案1】:

    STM 是一个基础 monad,如果我们有 STMT,想想 atomically,目前是 STM a -&gt; IO a 应该是什么样子。

    对于您的特定问题,我没有几个解决方案。更简单的可能是重新排列代码:

    write :: Int -> RandT StdGen STM [Int]
    write n = do
       -- random list of indexes, so you don't need to interleave random and stm code at all
       rn <- getRandomRs (0, numTVars) 
       lift $ go rn
       where go []     = return []
             go (i:is) = do tvars <- tvars -- this is redundant, could be taken out of the loop
                            temp <-  readArray tvars i
                            writeArray tvars i (temp + 1)
                            rands <- go is
                            return $ i : rands
    

    然而RandT 本质上是StateTlift

    instance MonadTrans (StateT s) where
        lift m = StateT $ \ s -> do
            a <- m
            return (a, s)
    

    所以表单的代码:

    do x <- lift baseAction1
       y <- lift baseAction2
       return $ f x y
    

    do x <- StateT $ \s -> do { a <- baseAction1; return (a, s) }
       y <- StateT $ \s -> do { a <- baseAction2; return (a, s) }
       return $ f x y
    

    脱糖后的do符号

    StateT (\s -> do { a <- baseAction1; return (a, s) }) >>= \ x ->
    StateT (\s -> do { a <- baseAction2; return (a, s) }) >>= \ y ->
    return $ f x y
    

    首先内联&gt;&gt;=

    StateT $ \s -> do
      ~(a, s') <- runStateT (StateT (\s -> do { a <- baseAction1; return (a, s) })) s
      runStateT ((\ x -> StateT (\s -> do { a <- baseAction2; return (a, s) }) >>= \ y -> return $ f x y) a) s'
    

    StateTrunStateT 取消:

    StateT $ \s -> do
      ~(x, s') <- do { a <- baseAction1; return (a, s) }))
      runStateT ((\ x -> StateT (\s -> do { a <- baseAction2; return (a, s) }) >>= \ y -> return $ f x y) x) s'
    

    经过几个内联/缩减步骤:

    StateT $ \s -> do
      ~(x, s') <- do { a <- baseAction1; return (a, s) }))
      ~(y, s'') <- do { a <- baseAction2; return (a, s') }))
      return (f x y, s'')
    

    可能 GHC 足够聪明,可以进一步减少这种情况,因此状态只是通过而不创建中间对(但我不确定,应该使用单子定律来证明这一点):

    StateT $ \s -> do
       x <- baseAction1
       y <- baseAction2
       return (f x y, s)
    

    你从中得到什么

    lift do x <- baseAction1
            y <- baseAction2
            return $ f x y
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多