Укажите причину замерзания 200 мс в цикле, критичном ко времени

Новое описание проблемы:

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

Оригинальный вопрос:

В нашем программном обеспечении для сбора данных у нас есть два потока, которые обеспечивают основную функциональность. Один поток отвечает за сбор данных с разных датчиков, а второй поток сохраняет данные на диск большими блоками. Данные собираются в двойной буфер. Обычно он содержит 100000 байт на элемент и собирает до 300 элементов в секунду. Один буфер используется для записи в поток сбора данных, а один буфер используется для чтения данных и сохранения их на диск во втором потоке. Если все данные были прочитаны, буферы переключаются. Переключение буферов, похоже, является серьезной проблемой производительности. Каждый раз, когда буфер переключается, поток сбора данных блокируется примерно на 200 мс, что слишком долго. Однако время от времени случается так, что переключение происходит намного быстрее, почти не занимая время. (Тестовый ПК: 64-битная Windows 7, процессор i5-4570 @ 3,2 ГГц (4 ядра), 16 ГБ DDR3 (800 МГц)).

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

У меня вопрос: есть ли метод перемещения данных из одного потока в другой, который не мешает потоку сбора?

редактировать: Двойной буфер реализован как два std :: векторы, которые используются в качестве кольцевых буферов. Переменная bool (int) используется, чтобы указать, какой буфер является активным буфером записи. При каждом обращении к двойному буферу проверяется значение bool, чтобы узнать, какой вектор следует использовать. Переключение буферов в двойной буфер означает переключение этого значения bool. Конечно, во время переключения все чтение и запись блокируются мьютексом. Я не думаю, что этот мьютекс может блокироваться в течение 200 мс. Кстати, 200 мс очень воспроизводимы для каждого события переключения.

0

Решение

Блокировка и освобождение мьютекса только для переключения одной переменной bool не займет 200 мс.

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

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

2

Другие решения

Поскольку вы работаете на Windows, может быть, вы используете Visual Studio? если да, я бы прибегнул к профилировщику VS, который в таких случаях неплох (IMHO), если вам не нужно проверять кэши данных / инструкций (тогда Intel vTune является естественным выбором). По моему опыту VS достаточно хорош, чтобы улавливать проблемы с конкуренцией, а также узкие места процессора. Вы можете запустить его непосредственно из VS или как отдельный инструмент. вам не нужна установленная VS на вашем тестовом компьютере, вы можете просто скопировать инструмент и запустить его локально.

VSPerfCmd.exe / start: SAMPLE / attach: 12345 / output: samples — подключиться к процессу 12345 и собрать данные выборки ЦП
VSPerfCmd.exe / detach: 12345 — отсоединиться от процесса
VSPerfCmd.exe / shutdown — отключить профилировщик, в файле samples.vsp записано (см. Первую строку)

Затем вы можете открыть файл и проверить его в Visual Studio. если вы не видите, что ваш процессор занят работой, переключитесь на профилирование конкуренции — просто измените аргумент «start» с «SAMPLE» на «CONCURRENCY»

Инструмент находится в папке% YourVSInstallDir% \ Team Tools \ Performance Tools \, AFAIR доступен с VS2010.
Удачи

1

После обсуждения проблемы в чате выяснилось, что анализатор производительности Windows является подходящим инструментом для использования. Программное обеспечение является частью Windows SDK и может быть открыто с помощью команды wprui в командном окне. (Алоис Краус разместил эту полезную ссылку: http://geekswithblogs.net/akraus1/archive/2014/04/30/156156.aspx в чате). Следующие шаги показали, что ожидает программное обеспечение:

  • Запишите информацию с помощью WPR, используя настройки по умолчанию, и загрузите сохраненный файл в WPA.
  • Определите соответствующую тему. В этом случае поток записи и поток сохранения, очевидно, имели самую высокую загрузку процессора. Сохранение потока может быть легко идентифицировано. Так как он сохраняет данные на диск, это тот, который имеет доступ к файлу. (Посмотрите на память-> Жесткие неисправности)
  • Проверьте Computing-> CPU CPU (Precise) и выберите Utilization by Process, Thread. Выберите процесс, который вы анализируете. Лучше всего отображать столбцы в следующем порядке: NewProcess, ReadyingProcess, ReadyingThreadId, NewThreadID, [желтая полоса], сумма Ready (мкс), сумма Wait (мкс), Count …
  • В ReadyingProcess я искал процесс с наибольшим временем ожидания (мкс), так как ожидал, что этот процесс будет ответственен за задержки.
  • В разделе ReadyingThreadID я проверял каждую строку, относящуюся к потоку, с задержками в столбце NewThreadId. После короткого поиска я нашел поток, который показывал частые ожидания около 100 мс, которые всегда отображались в паре. В столбце ReadyingThreadID я смог прочитать идентификатор потока, который ожидал цикл записи.
  • В зависимости от использования процессора этот поток практически ничего не делал. В нашем особом случае это привело меня к предположению, что команда io последовательного порта может вызвать это ожидание. После их отключения задержка исчезла. Важным открытием стало то, что задержка 200 мс фактически состояла из двух задержек по 100 мс.

Дальнейший анализ показал, что команда fetch data через пару виртуальных последовательных портов иногда теряется. Это может быть связано с очень высокой загрузкой ЦП в цикле сохранения и сжатия данных. Если команда извлечения теряется, данные не принимаются, и первая, а также вторая попытка получить данные имеют тайм-аут с временем ожидания 100 мс.

0
По вопросам рекламы [email protected]