Многопоточность: истинная скорость (09.06.2026). Печать
2026 - Июнь
09.06.2026 13:37
Save & Share
Обобщая все материалы по многопоточности: первый, второй, третий, четвёртый, пятый, шестой. Результаты:
- переписана однопоточная программа расчёта простых чисел на Visual Basic v.6.0, став многопоточной на Borland C++ Builder v.6.0 (размещена вместе с исходниками);
- переписана повторно - добавлены диагностические механизмы и понимание, почему прирост скорости только 3 раза, а не 11 (исходники были заменены);
- и вот сейчас, любопытство перевесило лень: а что будет, если убрать тормозящие нюансы - и вывести эту программу на загрузку процессора до усрачки (исходники потом будут добавлены)? Переписана ещё раз.

...теперь понимаю, почему ушёл из программирования. ...ушёл, бл.., как же...


Причины, тормозящие прирост скорости, - банальные и удобные (подача чисел на анализ порциями, с ожиданием завершения работы всех потоков, направленных на порцию):
- родительский поток тупо не успевал раздавать задачи дочерним - в случае, если потоков много, и частота их мала;
- один поток тупо тормозил все остальные, давно выполнившие свою задачу, - в случае работы с квадриллионными числами;
- недостатки компенсировались автоматической сортировкой полученных простых чисел как в пределах порции - так и всего массива полученных простых чисел. Сортировка Шелла, самая быстрая, обрабатывает 16млн чисел за ~10мин - изменение данной парадигмы работы с потоками сулило большие проблемы при работе с большим результатным массивом чисел. Особенный риск был при поиске простых чисел, начиная с 2: прирост скорости обнаружения чисел мог обернуться замедлением при сортировке.

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

Результаты:
- скорость нахождения простых чисел на i5-10400 в Windows 10 - улетела в стратосферу. Первые 16777215 простых чисел находились за 2:57 вместо 57:52 (особенно контрастно на фоне только что прочтённой книги по квантовой механике, ещё раз подтвердившей нефатальность мира). Это невозможно объяснить тем, что ЦП разогнался: у Intel с этим проблемы жульничества: разгоняется только первое ядро, которое родительское и в расчётах не участвует;
- дело не только в том, что ЦП теперь загружен на 100%. Родительский поток не просто перестал тормозить дочерние - он сам, как паразитный, стал выглядеть на их фоне гораздо менее жрущим ресурсы (код перенёсся в функцию потоков). Если ранее считалось, что он жрал ~23% при общей загрузке 50% (максимум, на который удавалось выходить) - теперь приходит понимание, что он жрал гораздо больше. Оставшиеся 27% размазывались по 11 дочерним потокам у i5-10400 (29% загрузки каждого) и ~4% по 23 у E5-2696 V2 (4.2% загрузки каждого). Плюс простой потоков, пока вся порция данных не обработается, - оказался недооценённым. А теперь, дочерние потоки вышли на максимум скорости и почти отсутствие задержек (только вход в критическую секцию) - вот и прирост получился в 19.6 раза;
- и загадка остаётся: как может быть прирост в 60 раз относительно однопоточного варианта, когда однопоточное грузит поток на 100%, а в ЦП логических процессоров всего 12;
- значит, у Borland C++ Builder v.6.0 нет проблем с работой с потоками, в контексте скорости, - остаются только нерабочими WaitFor, Suspend, Suspended;
- потребление памяти - не пострадало: она выделяется только в момент принудительного зануления массива результатов перед началом работы - так и осталось на уровне ~130МБ.

Расплата (хаос скорости работы потоков является хаотичным - двойной уровень вложенности, а не масло масляное);
- мало того, что частота потоков пляшет (аналоговая ТТХ ЦП: логические процессоры), так и сама ОС ещё перекидывает нагрузку с одного логического процессора на другой - даже если ЦП загружен на 100%;
- при загруженности 11 логических процессоров из 12 (12-й - родительский, крутящий ProcessMessages() и отображающий индикатор работы), - открытый заранее диспетчер задач просрался только через 50с (показав 100% загрузки ЦП), мышь иногда подвешивалась;
- скорость индикации новой "порции" простых чисел менялась. То мчится как сумасшедшая последовательно по 1000шт. То пропадёт, подумает - хрясь, сразу на миллион больше показывает;
- к граничной величине массива 16777216 чисел (максимум для Excel 2002) нужно прибавить 128 (максимальное число логических процессоров одного ЦП). Несмотря на то, что числа получаются более-менее отсортированными, - возникает ситуация: заказал 1000 чисел - приходит 1000-1005. И нельзя от крайних отказаться: если число 7933 закончится обрабатываться раньше 7919 - 7933 в результат попадёт, а 7919 - нет;
- отображение состояния работы потоков - начало глючить: часть потоков не показывает, что работа была завершена, - и остается гореть зелёным. Вообще, данный функционал потерял свой смысл: даже в ущербном диспетчере задач видно, что при расчётах - загружено буквально всё или буквально ничего.

В Excel 2002 сортировка полученных чисел не удалась: есть только её иллюзия - данные остаются на своих местах. Так сколько же времени отнимет сортировка именно в этом проекте? А вот хрен, а не реализация: сортировке Шелла (которую ещё и с VB6 переписывать) теперь противостоит поразрядная сортировка. Которая представлена на C++ в разных вариантах - но люди отмечают, что все они являются не оптимизированным по скорости говнокодом, - т.е. с кодом надо повторить то же вылизывание, что и в VB6 для Шелла, - проще застрелиться.
Обновлено ( 09.06.2026 16:33 )