Снова про потоки

Простейшие вопросы в области инженерной разработки
Ответить
Sergey Puzanov
advanced
advanced
Сообщения: 150
Зарегистрирован: 05 ноя 2020, 08:26
Версия LabVIEW: 18, 20.0f1
Благодарил (а): 26 раз
Поблагодарили: 5 раз
Контактная информация:

Снова про потоки

Сообщение Sergey Puzanov »

Добрый день. Пишу программу сервера на основе тулкита Workers. Есть 6 этих самых worker'ов (классов), из которых рассмотрим 4 - UI, Acquisition, Registration и Network. Все классы наследуются от UI. В штатном режиме Acquisition опрашивает приборы и получает массивы данных (на это уходит 10мс), которые записывает в Shared Variable. Далее он же посылает события:
1. На отображение этих данных в UI
2. На запись их в Registration
Network же сам с частотой 2 Гц обращается к Shared Variable и отправляет данные дальше на отображение клиентами.
С помощью тех же Shared Variable записываю время на каждое из этих событий (время чтения с приборов, время между отправки по сети, время записи одного массива данных), и отображаю это время + данные в UI. Основные VI этих классов имеют тот же поток, что и UI. Конкретные VI по сбору, отправке и прочему я распределил на разные потоки, вызовы различных dll функций тоже вынес в отдельный поток. Но всё равно при переключении вкладок программы или перетаскивании окна все эти времена начинают сильно скакать. В чём ошибка и где эта зависимость осталась? Спасибо.
VI отображения данных
VI отображения данных
Время штатное
Время штатное
Время сразу после переноса окна
Время сразу после переноса окна
Аватара пользователя
dadreamer

Activity Professionalism Автор
professor
professor
Сообщения: 3962
Зарегистрирован: 17 фев 2013, 16:33
Награды: 4
Версия LabVIEW: 2.5 — 2024
Благодарил (а): 13 раз
Поблагодарили: 138 раз
Контактная информация:

Re: Снова про потоки

Сообщение dadreamer »

Sergey Puzanov писал(а): 08 июл 2024, 15:22Но всё равно при переключении вкладок программы или перетаскивании окна все эти времена начинают сильно скакать. В чём ошибка и где эта зависимость осталась?
Рискну предположить (сильно санными тряпками не кидайтесь), что дело в свойстве Value: его вызов требует переключения UI-потока, который, как известно, один на всё приложение. Таскание за заголовок и аналогичные графические манипуляции также занимают UI-поток, отсюда и задержки в работе программы. Используйте локалки, если возможно, а если нет, то индексы (Get / Set Control Values by Index).
Вложения
Block UI.vi
lv2020
(8.11 КБ) 29 скачиваний
Последний раз редактировалось dadreamer 08 июл 2024, 17:06, всего редактировалось 1 раз.
Sergey Puzanov
advanced
advanced
Сообщения: 150
Зарегистрирован: 05 ноя 2020, 08:26
Версия LabVIEW: 18, 20.0f1
Благодарил (а): 26 раз
Поблагодарили: 5 раз
Контактная информация:

Re: Снова про потоки

Сообщение Sergey Puzanov »

dadreamer писал(а): 08 июл 2024, 16:15 Таскание за заголовок и аналогичные графические манипуляции также занимают UI-поток
Согласен, но задержки происходят в других потоках ведь. Та же запись в файл находится в other 1. Или UI по умолчанию имеет приоритет над всем остальным, и пока там всё не выполнится, остальные потоки ждут? Прикреплённый вами файл не смог открыть - говорит, что 2024 версия.
AndreyDmitriev

Activity Professionalism Tutorials Gold Black
VIP
VIP
Сообщения: 1408
Зарегистрирован: 03 фев 2010, 00:42
Награды: 8
Версия LabVIEW: 6.1 - 2025
Откуда: Германия
Благодарил (а): 1 раз
Поблагодарили: 77 раз
Контактная информация:

Re: Снова про потоки

Сообщение AndreyDmitriev »

dadreamer писал(а): 08 июл 2024, 16:15
Sergey Puzanov писал(а): 08 июл 2024, 15:22Но всё равно при переключении вкладок программы или перетаскивании окна все эти времена начинают сильно скакать. В чём ошибка и где эта зависимость осталась?
Рискну предположить (сильно санными тряпками не кидайтесь), что дело в свойстве Value: его вызов требует переключения UI-потока, который, как известно, один на всё приложение.
Так и есть. Я в таких случаях просто вызываю Sleep из kernel32.dll в UI потоке, и смотрю на циклы, которые останавливаются. Ну вот, к примеру, на время блокировки UI потока оба цикла остановят обновление индикатора, но второй цикл будет всё равно бежать, а первый встанет колом:
Изображение
Это по значениям индикаторов хорошо видно:
Изображение
Аватара пользователя
dadreamer

Activity Professionalism Автор
professor
professor
Сообщения: 3962
Зарегистрирован: 17 фев 2013, 16:33
Награды: 4
Версия LabVIEW: 2.5 — 2024
Благодарил (а): 13 раз
Поблагодарили: 138 раз
Контактная информация:

Re: Снова про потоки

Сообщение dadreamer »

Sergey Puzanov писал(а): 08 июл 2024, 16:36Согласен, но задержки происходят в других потоках ведь. Та же запись в файл находится в other 1.
А как организованы потоки? Это отдельные циклы? Если речь о Preferred Execution System, то это не совсем подходящий способ распараллеливания кода. Вот тут подробно расписано: viewtopic.php?p=54155#p54155 И даже если выставить Other 1, например, и вызвать там Property Node, этот вызываемый узел также потребует, чтобы UI-поток был свободен. То есть, смена системы исполнения не избавляет от подобного.
Sergey Puzanov писал(а): 08 июл 2024, 16:36Прикреплённый вами файл не смог открыть - говорит, что 2024 версия.
Поторопился, залил не ту версию. Исправил. В принципе, Андрей там в теме выложил почти такой же пример.

Ещё, будьте готовы к тому, что на не RTOS до конца избавиться от графических эффектов не получится. Вот длинный тред, где уже обсуждали всё это: Странное торможение параллельных потоков.
Artem.spb

Activity Автор
professor
professor
Сообщения: 3564
Зарегистрирован: 31 июл 2011, 23:05
Награды: 2
Версия LabVIEW: 12-18
Благодарил (а): 57 раз
Поблагодарили: 191 раз
Контактная информация:

Re: Снова про потоки

Сообщение Artem.spb »

Sergey Puzanov писал(а): 08 июл 2024, 15:22 Все классы наследуются от UI.
Конкретные VI по сбору, отправке и прочему я распределил на разные потоки
Наследование и распределение по разным потокам противоречат друг другу. Как вы это сделали? Может, на самом деле нет?
Artem.spb

Activity Автор
professor
professor
Сообщения: 3564
Зарегистрирован: 31 июл 2011, 23:05
Награды: 2
Версия LabVIEW: 12-18
Благодарил (а): 57 раз
Поблагодарили: 191 раз
Контактная информация:

Re: Снова про потоки

Сообщение Artem.spb »

dadreamer писал(а): 08 июл 2024, 16:15 а если нет, то индексы (Get / Set Control Values by Index).
А разве это не те же самые property, которые в любом случае дёргают UI?
Аватара пользователя
dadreamer

Activity Professionalism Автор
professor
professor
Сообщения: 3962
Зарегистрирован: 17 фев 2013, 16:33
Награды: 4
Версия LabVIEW: 2.5 — 2024
Благодарил (а): 13 раз
Поблагодарили: 138 раз
Контактная информация:

Re: Снова про потоки

Сообщение dadreamer »

Artem.spb писал(а): 08 июл 2024, 17:28А разве это не те же самые property, которые в любом случае дёргают UI?
Не дёргают, посмотрите :vi: выше. Там пара внутренних функций :labview: вызывается: ReadDCOTransferData и WriteDCOTransferData. Конечно, при вызовах есть небольшие накладные расходы, поэтому получается немного медленнее, чем при обращении к локальной переменной. При большом желании можно эти две функции "вручную" вызвать с тем же результатом: https://lavag.org/topic/21394-direct-re ... -possible/
Sergey Puzanov
advanced
advanced
Сообщения: 150
Зарегистрирован: 05 ноя 2020, 08:26
Версия LabVIEW: 18, 20.0f1
Благодарил (а): 26 раз
Поблагодарили: 5 раз
Контактная информация:

Re: Снова про потоки

Сообщение Sergey Puzanov »

Artem.spb писал(а): 08 июл 2024, 17:28
Sergey Puzanov писал(а): 08 июл 2024, 15:22 Все классы наследуются от UI.
Конкретные VI по сбору, отправке и прочему я распределил на разные потоки
Наследование и распределение по разным потокам противоречат друг другу. Как вы это сделали? Может, на самом деле нет?
Тоже есть подозрение, что оно не работает. Но так да - это параллельные циклы со своими структурами case. Сама VI с циклом в потоке same as caller, а те что в кейсах - те в других потоках, по крайней мере по настройкам. Буду завтра экспериментировать и отпишусь
Artem.spb

Activity Автор
professor
professor
Сообщения: 3564
Зарегистрирован: 31 июл 2011, 23:05
Награды: 2
Версия LabVIEW: 12-18
Благодарил (а): 57 раз
Поблагодарили: 191 раз
Контактная информация:

Re: Снова про потоки

Сообщение Artem.spb »

dadreamer писал(а): 08 июл 2024, 17:35
Artem.spb писал(а): 08 июл 2024, 17:28А разве это не те же самые property, которые в любом случае дёргают UI?
Не дёргают, посмотрите :vi: выше.
Снимок.JPG
Даже не знаю, что проще. Делать event для пересылки значений, или дёргать по индексам. С индексами легко промахнуться, но можно сначала их искать по именам. Но тут снова приходим к текстовому стилю программирования. Но штука интересная.
Artem.spb

Activity Автор
professor
professor
Сообщения: 3564
Зарегистрирован: 31 июл 2011, 23:05
Награды: 2
Версия LabVIEW: 12-18
Благодарил (а): 57 раз
Поблагодарили: 191 раз
Контактная информация:

Re: Снова про потоки

Сообщение Artem.spb »

Sergey Puzanov писал(а): 08 июл 2024, 17:52 Сама VI с циклом в потоке same as caller, а те что в кейсах - те в других потоках, по крайней мере по настройкам. Буду завтра экспериментировать и отпишусь
Ну вот это вот фигня.
На передачу данных между потоками тратиться много времени. По крайней мере так было на заре цивилизации, и в моих тестах я обнаружил потерю производительности. В другом потоке должна быть вся "большая" функция, а не те кого она дёргает.
Sergey Puzanov
advanced
advanced
Сообщения: 150
Зарегистрирован: 05 ноя 2020, 08:26
Версия LabVIEW: 18, 20.0f1
Благодарил (а): 26 раз
Поблагодарили: 5 раз
Контактная информация:

Re: Снова про потоки

Сообщение Sergey Puzanov »

Отображение данных прямиком в индикаторы проблему не решило, следующий шаг будет всё таки удаление наследования, чтобы каждый цикл в своём потоке старался работать.
AndreyDmitriev

Activity Professionalism Tutorials Gold Black
VIP
VIP
Сообщения: 1408
Зарегистрирован: 03 фев 2010, 00:42
Награды: 8
Версия LabVIEW: 6.1 - 2025
Откуда: Германия
Благодарил (а): 1 раз
Поблагодарили: 77 раз
Контактная информация:

Re: Снова про потоки

Сообщение AndreyDmitriev »

Sergey Puzanov писал(а): 08 июл 2024, 16:36
dadreamer писал(а): 08 июл 2024, 16:15 Таскание за заголовок и аналогичные графические манипуляции также занимают UI-поток
Согласен, но задержки происходят в других потоках ведь. Та же запись в файл находится в other 1.
Как дополнение к ссылке, что выше приводилась, просто что б лучше понимать как оно работает.
standard, instrument I/O, data acquisition, other 1 и other 2 в общем ничем не отличаются. Чуть особняком стоит standard в том смысле, что если Top Level VI помечен как "same as caller", то он будет выполняться в standard.

По умолчанию LabVIEW выделяет вам двадцать четыре потока на каждую систему исполнения и каждый приоритет. Это легко показать.

Допустим я сделаю реентерантный инструмент, в который брошу Sleep из kernel32.dll с задержкой в секунду и потокобезопасным вызовом:
Изображение
Нвстройки этого подприбора:
Изображение
Вызову его вот так и замеряю время исполнения:
Изображение
Одна секунда, кто б сомневался.
Теперь смотрите, я положу двадцать четыре копии:
Изображение
По-прежнему одна секунда. Но стоит мне положить двадцать пятую копию, то ситуация изменится:
Изображение
Стало две секунды. Это потому, что мы израсходовали пул потоков. Теоретически можно внутри DLL вызвать GetThreadID и увидеть, что один из 25-и вернёт нам один из ID, используемых ранее.

Теперь допустим, что нам надо больше потоков. Я сделаю ещё пару таких же инструментов с задержкой, но попрошу один исполняться в other1, а второй в other2, при этом положу 24 копии каждого, вот теперь у меня 48 потоков:
Изображение
И время исполнения по-прежнему одна секунда. Стоит лишь мне добавить двадцать пятый (неважно other1 или other2), и снова станет две секунды (можете сами проверить).

Важно также понимать, что пул потоков выделяется не только на систему исполнения, но и на приоритет.
Проверяем - делаем два инструмента, оба будут в other1, но один будет иметь нормальный приоритет, а второй - повышенный, разница будет лишь вот тут:
Изображение
И вот у нас снова 48 потоков и время одна секунда, хотя оба в other1, но приоритет у них разный:
Изображение

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

Ещё такой момент - в принципе пул потков настраиваемый. Допустим, нам отчаянно не хватает тех, что выделены по умолчанию. Можно добавить в LabVIEW.ini следующие строки:

Код: Выделить всё

ESys.StdNParallel=-1
ESys.other1.Normal=1024
Первая строка включит нам пользовательский режим, а вторая скажет, что для инструментов, выполняющихся в other1 с нормальным приоритетом надо выделять 1024 потока. Теоретически там есть инструмент threadconfig.vi в C:\Program Files\National Instruments\LabVIEW 2024\vi.lib\Utility\sysinfo.llb:
Изображение
Но мне руками больше нравится.
Перезапускаем LabVIEW и проверяем (я отчаянно не ленивый сегодня):
Изображение
И вот - одна секунда на всё про всё:
Изображение
Task Manager тоже показывает больше тысячи потоков:
Изображение
Там есть ещё пара небольших тонкостей, скажем по умолчанию изначально вам выделяется столько потоков, сколько у вас ядер на процах, а потом оно довыделяется по необходимости (и вроде бы пачками по четыре потока), но не более 24-х потоков на систему и приоритет.
Поэтому при первом запуске инструмента, который требут 24-х потоков на 16-ти процессорной тачке я вижу не одну секунду, а чуть больше, где-то 1.2, двести миллисекунд — это накладные расходы на создание потоков по запросу, но это только один раз происходит, дальше эти созданные потоки так и висят в памяти, но это уже совсем тонкости. Если количество потоков задавать индивидуально, типа ESys.Normal=64, то потоки будут создаваться при загрузке VI, а если глобально через ESys.StdNParallel=64, то ри запуске (что даст задержку), но это неточно.

Полный список ключей для управления пулом потоков вот:

Код: Выделить всё

ESys.StdNParallel

ESys.Bgrnd
ESys.Normal
ESys.High
ESys.TCritical
ESys.Vhigh

ESys.DAQ.Bgrnd
ESys.DAQ.High
ESys.DAQ.Normal
ESys.DAQ.TCritical
ESys.DAQ.VHigh

ESys.instrument.Bgrnd
ESys.instrument.High
ESys.instrument.Normal
ESys.instrument.TCritical
ESys.instrument.VHigh

ESys.other1.Bgrnd
ESys.other1.High
ESys.other1.Normal
ESys.other1.TCritical
ESys.other1.VHigh

ESys.other2.Bgrnd
ESys.other2.High
ESys.other2.Normal
ESys.other2.TCritical
ESys.other2.VHigh
Когда очень много потоков используется, то в общем имеет смысл попробовать увеличить их количество тонкими настройками, в принципе это можно одной строчкой сделать, скажем добавив

Код: Выделить всё

ESys.StdNParallel=64
Это даст вам 64 потока на каждую систему исполнения и каждый приоритет, но в подавляющем большинстве случаев того, что даёт LabVIEW по умолчанию, более чем достаточно. Всё что выше справедливо для LabVIEW 2024x64.
Sergey Puzanov
advanced
advanced
Сообщения: 150
Зарегистрирован: 05 ноя 2020, 08:26
Версия LabVIEW: 18, 20.0f1
Благодарил (а): 26 раз
Поблагодарили: 5 раз
Контактная информация:

Re: Снова про потоки

Сообщение Sergey Puzanov »

Благодарю за такой развёрнутый ответ, стало намного понятнее. Так же самостоятельно провёл тест и убедился в том, что передача данных между потоками критично замедляет выполение функций, и, соответственно, разделять нужно независимые (или слабозависимые) части программы. Попробую скомбинировать все эти знания.
Sergey Puzanov
advanced
advanced
Сообщения: 150
Зарегистрирован: 05 ноя 2020, 08:26
Версия LabVIEW: 18, 20.0f1
Благодарил (а): 26 раз
Поблагодарили: 5 раз
Контактная информация:

Re: Снова про потоки

Сообщение Sergey Puzanov »

С учётом ваших ответов и небольшим перераспределением удалось добиться стабильных 30-50мс вместо 120-300мс+, всем спасибо!
Ответить

Вернуться в «Для чайников»