如何强制每秒进行X次评估?

时间:2019-05-17 17:16:51

标签: haskell

我需要一个实时事件循环,该循环应执行以下操作:

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值。这是我应该担心的事情吗?如果是,什么是更好的方法?

预先感谢

1 个答案:

答案 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暂停所引起的潜在问题。