Как заставить оценку X раз в секунду?

Мне нужен цикл событий в реальном времени, который делает что-то вроде этого:

myEventLoop = do
    businessLogic x0 x1 x2 ... xN
    sleep 0.01
    myEventLoop

Я знаю о пакете eventloop, но он, похоже, больше ориентирован на веб-приложения, и мне нужна обработка сигналов в реальном времени. Допустим, частота дискретизации составляет 100 Гц. Частота дискретизации не должна быть сверхточной; если это всего лишь 98 Гц, то это не имеет большого значения. Но я никогда не хочу отклоняться более чем на 5%. На данный момент мы говорим только о выполнении GHC +/- 5%. Игнорировать ошибки синхронизации из-за ОС.

Мои вопросы: 1) Это то, что может делать Haskell? Я немного обеспокоен +/- 5%. 2) Является ли часть сна неэффективной? Или другие потоки Haskell могут занимать тот же поток ОС, пока myEventLoop выполняет спящую часть? 3) Предполагая, что сон - это хорошая идея, жесткое кодирование времени сна, скорее всего, нет. Я могу прочитать счетчик меток времени (TSC), поскольку программа работает на архитектуре x86, но такой подход может быть проблематичным, если GHC решит разделить программу на микропотоки, отправив некоторые из них на одно ядро ​​процессора, а другие на другое ядро ​​процессора. , так как разные ядра могут иметь разные значения для TSC. Должен ли я беспокоиться об этом, и если да, то какой подход лучше?

заранее спасибо

Всего 1 ответ


Вы можете попробовать, но я не оптимистичен. GC и RTS окажут на вас существенное негативное влияние, но я не могу сказать, будете ли вы заботиться об этом.

Eventloop это ... огромный ... лично я бы сделал что-то обычное и легкий вес.

Не используйте настройку «Sleep затем logic», потому что часть «logic» не свободна, и вы получите более длинные паузы, чем хотелось бы. Мои тесты с "fork logic затем sleep" демонстрируют аналогично плохое поведение, потому что 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) Является ли часть сна неэффективной? Или другие потоки Haskell могут занимать тот же поток ОС, пока myEventLoop выполняет спящую часть?

Несколько потоков на Haskell могут занимать один и тот же поток ОС, никаких проблем.

3) Предполагая, что сон - это хорошая идея, жесткое кодирование времени сна, скорее всего, нет. Я могу прочитать счетчик меток времени (TSC), поскольку программа работает на архитектуре x86, но такой подход может быть проблематичным, если GHC решит разделить программу на микропотоки, отправив некоторые из них на одно ядро ​​процессора, а другие на другое ядро ​​процессора. , так как разные ядра могут иметь разные значения для TSC. Должен ли я беспокоиться об этом, и если да, то какой подход лучше?

Да, вам следует беспокоиться о перемещении потока Haskell из ядра в ядро ​​и потока ОС в поток ОС. Выше - моя попытка домашнего подхода. Одиннадцать лет назад я написал control-event (на хакерском языке), чтобы решить эту проблему для меня, но мои временные ограничения были менее важны и нуждаются в немного другом. В наши дни вы можете напрямую использовать менеджер событий GHC, хотя я бы избегал его из-за более низкого уровня API, и он не решает вашу основную проблему с паузами GC.


Есть идеи?

10000