我需要一个实时事件循环,该循环应执行以下操作:
n
我了解eventloop软件包,但它似乎更适合Web应用程序,而我需要的是实时信号处理。假设采样率为100 Hz。采样率不需要非常精确。如果偶尔只有98 Hz,那没什么大不了的。但是我永远不希望它偏离5%以上。目前,我们仅谈论GHC执行+/- 5%。忽略由于操作系统而导致的计时错误。
我的问题是: 1)Haskell可以做这种事情吗?我有点担心+/- 5%的部分。 2)睡眠部分效率低下吗?还是在myEventLoop执行睡眠部分时其他Haskell线程可以占用相同的OS线程? 3)假设睡眠是一个好主意,硬编码睡眠时间很可能不是。由于程序在x86架构上运行,因此我可以读取时间戳计数器(TSC),但是如果GHC决定将程序划分为微线程,将其中的一些发送到1个CPU内核,而将其他的发送到另一个CPU内核,则该方法可能会出现问题。 ,因为不同的核心可以具有不同的TSC值。这是我应该担心的事情吗?如果是,什么是更好的方法?
预先感谢
答案 0 :(得分:4)
您可以尝试,但我并不乐观。 GC和RTS会对您产生重大的负面影响,但我无法说出您是否会在意。
Eventloop很大...个人而言,我会做一些自定义且重量轻的事情。
不要使用“先睡后逻辑”设置,因为“逻辑”部分不是免费的,您最终会得到比期望更长的暂停时间。我的“先入叉逻辑然后入睡”测试显示出类似的不良行为,因为sleep
直到RTS再次调度父线程才开始。
使用绝对时间来避免漂移的自定义解决方案
自定义解决方案如下所示:
import Control.Concurrent
import Control.Monad
import Data.Time
periodic :: NominalDiffTime -> UTCTime -> IO () -> IO ()
periodic delayTime base operation =
do start <- getCurrentTime
let end = addUTCTime delayTime base
microSeconds = floor $ (1000000 :: Double) * realToFrac (diffUTCTime end start)
threadDelay microSeconds
void $ forkIO operation
periodic delayTime end operation
main :: IO ()
main =
do start <- getCurrentTime
forkIO $ periodic (1/98) start (getCurrentTime >>= print)
threadDelay 10000000 -- 10 seconds
这很简单,除非您需要取消事件之类的时髦功能,否则它使您真正接近任何框架将提供的功能。您还将接近所需的时间段:
import Control.Concurrent
import Control.Monad
import Data.Time
periodic :: NominalDiffTime -> UTCTime -> IO () -> IO ()
periodic delayTime base operation =
do start <- getCurrentTime
let end = addUTCTime delayTime base
microSeconds = floor $ (1000000 :: Double) * realToFrac (diffUTCTime end start)
threadDelay microSeconds
void $ forkIO operation
periodic delayTime end operation
main :: IO ()
main =
do start <- getCurrentTime
forkIO $ periodic (1/98) start (getCurrentTime >>= print)
threadDelay 10000000 -- 10 seconds
结果:
% ./y | wc -l
980
您可以分析打印时间来获得置信区间,但是随着负载变化,堆增长以及GC需要做更多的暂停,情况会变得更糟。
实际答案
编辑,因为您有实际问题。
1)这是Haskell可以做的事情吗?
Haskell并不是真正的目的-GHC RTS甚至不适合音频等软实时工作。
2)睡眠部分效率低下吗?还是在myEventLoop执行睡眠部分时其他Haskell线程可以占用相同的OS线程?
多个Haskell线程可以占用相同的OS线程,没有问题。
3)假设睡眠是一个好主意,硬编码睡眠时间很可能不是。由于程序在x86架构上运行,因此我可以读取时间戳计数器(TSC),但是如果GHC决定将程序划分为微线程,将其中的一些发送到1个CPU内核,而将其他的发送到另一个CPU内核,则该方法可能会出现问题。 ,因为不同的核心可以具有不同的TSC值。这是我应该担心的事情吗?如果是,什么是更好的方法?
是的,您应该担心让Haskell线程从一个核心移到另一个核心,以及从OS线程移到OS线程。以上是我尝试自家种植的方法。十一年前,我写了control-event
(关于黑客问题)为我解决了同样的问题,但是我的时间限制不再是一个问题,需要有所不同。这些天,您可以直接使用GHC事件管理器,尽管由于API的级别较低,我会避免使用它,并且它无法解决GC暂停所引起的潜在问题。