Обработка данных захвата прибора

У меня есть инструмент, который производит поток данных; мой код получает доступ к этим данным через обратный вызов onDataAcquisitionEvent(const InstrumentOutput &data). Алгоритм обработки данных потенциально намного медленнее, чем скорость поступления данных, поэтому я не могу надеяться обработать каждый отдельный фрагмент данных (и мне это не нужно), но я хотел бы обработать как можно больше. Благодарю прибор как датчик окружающей среды со скоростью сбора данных, которую я не контролирую. InstrumentOutput может быть, например, классом, который содержит три одновременных измерения давления в разных местах.

Мне также нужно вести небольшую историю данных. Предположим, например, что я могу разумно надеяться обрабатывать выборку данных каждые 200 мс или около того. В большинстве случаев я был бы счастлив обработать только один последний образец, но иногда мне нужно было бы просмотреть данные за пару секунд, которые поступили до этого последнего образца, в зависимости от того, присутствуют ли аномальные показания в последнем образце.

Другое требование - как можно скорее выйти из onDataAcquisitionEvent() обратного вызова, чтобы избежать потери данных в датчике.

Библиотека сбора данных (сторонняя) собирает данные прибора в отдельном потоке.

Я подумал о следующем дизайне; иметь очередь одного производителя / одного потребителя и помещать токены данных в синхронизированную очередь в обратном вызове onDataAcquisitionEvent ().
На принимающей стороне существует цикл, который выталкивает данные из очереди. Цикл почти никогда не засыпает из-за высокой скорости поступления данных. На каждой итерации происходит следующее:

  1. Вытащить все доступные данные из очереди,
  2. Извлеченные данные копируются в кольцевой буфер (я использовал кольцевой буфер повышения), таким образом всегда доступна некоторая история,
  3. Обработать последний элемент в буфере (и, возможно, посмотреть на предыдущие),
  4. Повторить петлю.

Вопросов:

  1. Насколько хорош этот дизайн и каковы подводные камни? а также
  2. Что может быть лучше?

Изменить: одна проблема, о которой я подумал, - это когда размер кольцевого буфера недостаточно велик для хранения необходимой истории; в настоящее время я просто перераспределяю кольцевой буфер, удваивая его размер. Надеюсь, мне нужно будет сделать это всего один или два раза.


person Cattus    schedule 22.07.2012    source источник
comment
Пожалуйста, будьте более конкретны: когда вы пишете о данных, вы имеете в виду реальные образцы PCM или какие-то события, например, MIDI-события?   -  person Frunsi    schedule 22.07.2012
comment
Спасибо, инструмент не имеет отношения к музыке, это датчик окружающей среды. Обновлю исходный пост.   -  person Cattus    schedule 22.07.2012


Ответы (3)


У меня есть небольшой опыт сбора данных, и я могу сказать вам, что у многих разработчиков есть проблемы с преждевременным выходом из строя функций. Поскольку просто записать данные из прибора в журнал кажется простым, люди, как правило, добавляют в систему несущественные компоненты, прежде чем проверять надежность регистрации. Это большая ошибка.

Другое требование - как можно скорее выйти из обратного вызова onDataAcquisitionEvent(), чтобы избежать потери данных в датчике.

Это единственное требование до тех пор, пока эта часть продукта не будет работать на 110% во всех полевых условиях.


В большинстве случаев я был бы счастлив обработать только один последний образец, но иногда мне нужно было бы просмотреть данные за пару секунд, которые были получены до этого последнего образца, в зависимости от того, присутствуют ли аномальные показания в последнем образце.

«В большинстве случаев» не имеет значения. Кодируйте наихудший случай, потому что onDataAcquisitionEvent() не может тратить время на размышления о непредвиденных обстоятельствах.

Похоже, вы попадаете в ловушку, проектируя его для работы с лучшими данными, которые могут быть доступны, и оставляете открытым то, что может произойти, если он недоступен или если предоставление лучших данных на монитор в конечном итоге слишком дорого.

Уничтожьте данные в источнике. Укажите, сколько выборок потребуется для обработки аномального случая, и попытайтесь предоставить это количество с постоянной частотой выборки плюс запас в 20%.

Конечно, не должно быть петель, которые никогда не спят. Круговой буфер - это нормально, но просто заполните его тем минимумом, который вам нужен, и анализируйте его только по мере необходимости.

Качество системы определяется ее стабильностью и детерминизмом, а не попытками сделать все возможное и предоставить как можно больше.

person Potatoswatter    schedule 22.07.2012
comment
Спасибо. Хороший момент в том, что не нужно предоставлять слишком много данных; очень возможно, что скорость сбора данных станет настолько высокой, что я буду тратить большую часть времени на размещение уже не относящихся к делу данных в буфер. - person Cattus; 22.07.2012

Ваш производственный / потребительский дизайн - это именно тот дизайн. В системах реального времени мы также часто назначаем различные приоритеты времени выполнения потребляющим потокам, не уверен, что это применимо в вашем случае.

Используйте структуру данных, которая в основном является двусвязным списком, чтобы при ее росте вам не нужно было перераспределять все, и у вас также есть доступ O (1) к нужным вам образцам.

Если ваша память недостаточно велика для хранения данных за несколько секунд (а это должно быть - одна выборка каждые 200 мс? 5 выборок в секунду), вам нужно посмотреть, можете ли вы выдержать чтение из вспомогательной памяти, но это пропускная способность и в вашем случае это не имеет ничего общего с вашим дизайном и требованием «как можно скорее прекратить обратный вызов».

Рассмотрим реализацию очереди, которая не требует блокировки (помните: только один читатель и один писатель!), Чтобы ваш обратный вызов не остановился.

Если ваш обратный вызов действительно быстрый, подумайте о том, чтобы отключить прерывания / назначить ему высокий приоритет. Может не понадобиться, если он никогда не может блокироваться и имеет правильный приоритет.

person Nitzan Shaked    schedule 22.07.2012
comment
Спасибо. Небольшая поправка - алгоритму обработки требуется не более 200 мс для работы в системе, которая не перегружена, но скорость поступления данных намного выше (что означает, что мне придется пропускать выборки). Мне не обязательно обрабатывать все образцы, но на всякий случай нужно иметь историю. - person Cattus; 22.07.2012
comment
Без проблем. Однако мой ответ все еще остается в силе. Похоже, вы можете разместить все нужные вам сэмплы в памяти, и правильный дизайн по-прежнему будет добавлен в конец связанного списка, а другой поток будет обрабатывать сэмплы. (В этом потоке вы можете делать все, что захотите, включая создание большего количества потоков и т. Д.) - person Nitzan Shaked; 22.07.2012

Вопросы: (1) насколько хорош этот дизайн и каковы подводные камни, и (2) какой дизайн может быть лучше. Спасибо.

Да, это нормально. Но из соображений производительности вы должны разработать код так, чтобы он обрабатывал массив входных выборок на каждом этапе обработки, а не только по одной выборке на каждой. Это приводит к гораздо более оптимальному коду для современных процессоров.

Длина такого массива (= фрагмент данных) либо фиксированная (более простой код), либо переменная (гибкая, но некоторая обработка может усложняться).

В качестве второго варианта дизайна вам, вероятно, следует игнорировать историю на этом архитектурном уровне и отбросить эту функцию ...

В большинстве случаев я был бы счастлив обработать только один последний образец, но иногда мне нужно было бы просмотреть данные за пару секунд [...]

Возможно, отслеживание истории стоит реализовать именно в той особой части кода, которая иногда требует доступа к ней. Возможно, это не должно быть частью «общей архитектуры». Если да, то это вообще упрощает обработку.

person Frunsi    schedule 22.07.2012