我收集实时信号,计算派生信号并存储原始数据和派生数据 在一个循环缓冲区,所以我只持有最后一百万个样本。
有时我需要序列化所有信号的当前值。所以我需要这样的东西:
type D0 a = M.Map SignalType D1
data D1 a = D1
{ foo :: M.Map DoorType a
, bar :: D2 a
, baz :: a
}
data D2 = D2
{
quux :: a
, zoo :: a
}
data MyData = D0 SignalBuffer
data CurrentSignals = D0 SignalValue
SignalBuffer
是SignalValue
的序列。它可以是一个未装箱的浮动数组。 Haskell可以为我派生Functor
个实例,因此我可以使用fmap
从每个SignalValue
获取最后SignalBuffer
,并将结构传递给Aeson
以进行序列化。< / p>
如何为SignalBuffer
实现循环缓冲API,以便在新的滴答到达时将新值推送到所有缓冲区?我想节省内存,所以我想我必须使用未装箱的数组。使用可变的无盒数组(STUArray
?)是否有利,所以数组更新不会堆积在内存中?是否可以在此设置中使用可变数组?我已准备好将MyData
和CurrentSignals
更改为工作的任何内容。
我知道如何实现循环缓冲区,问题是如何优雅地将更新应用于MyData
。
我在考虑像
这样的东西type UpdateFunc a = MyData -> SignalValue -> Modifier SignalBuffer
updateAllBuffers :: D0 UpdateFunc -> Modifier MyData
某些信号是其他信号的“卷积”(不是真正的卷积,而是类似的处理)。要更新信号缓冲区,我需要访问其他信号的缓冲区 - 这就是为什么UpdateFunc接受MyData
和SignalValue
并返回缓冲区修改函数。
updateAllBuffers
然后“拉上”D0 UpdateFunc
和MyData
以获得新的MyData
。
当然我已经准备好使用Modifier
适合我的任务 - 它可以是一个函数,一个monadic值等。
答案 0 :(得分:0)
我不完全理解您要使用上面的代码做什么,但是您可以将Data.Vector.Unboxed.Mutable中的IOVector
用于高性能数组来制作循环缓冲区:
{-# LANGUAGE GADTs #-}
import Data.IORef (IORef, newIORef, readIORef, writeIORef)
import Data.Vector.Unboxed.Mutable (IOVector, Unbox)
import qualified Data.Vector.Unboxed.Mutable as V
data CircularBuffer a where
CircularBuffer :: Unbox a =>
{ start :: IORef Int -- index for getting an element
, end :: IORef Int -- index for putting an element
, array :: IOVector a
} -> CircularBuffer a
newCircularBuffer :: (Unbox a) => Int -> IO (CircularBuffer a)
newCircularBuffer size = CircularBuffer <$> newIORef 0 <*> newIORef 0 <*> V.new size
putCircularBuffer :: Unbox a => a -> CircularBuffer a -> IO ()
putCircularBuffer newEndValue (CircularBuffer _start end array) = do
endIndex <- readIORef end
V.write array endIndex newEndValue
writeIORef end $! (endIndex + 1) `mod` V.length array
getCircularBuffer :: Unbox a => CircularBuffer a -> IO a
getCircularBuffer (CircularBuffer start _end array) = do
startIndex <- readIORef start
startValue <- V.read array startIndex
writeIORef start $! (startIndex + 1) `mod` V.length array
pure startValue
然后,您可以制作一个类似函数的映射(尽管它是IO),将一个函数应用于CircularBuffer
数组中的每个项目。