4

我目前正在 Haskell 中进行一些事务内存基准测试,并希望能够在事务中使用随机数。我目前正在使用此处的 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 转换器的了解非常有限。非常感谢有关使用变压器的性能和相对开销的简要说明。

4

1 回答 1

2

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

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

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本质StateT上是lift

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

这是在脱糖后做符号

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

先内联>>=

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
于 2015-07-11T09:13:18.417 回答