Еще о многопоточности
Три недели в углу лежал незакрытый гештальт, надо закрывать.
Если кто помнит, то в прошлой серии мы дошли до того, что Qt-шная система signal-slot плохо масштабируется в многопоточном случае и нужно для передачи данных между потоками использовать что-то еще. Ну, к примеру, lock-free очередь (из TBB или вот эту, такой уж большой разницы я пока не обнаружил, хотя и должна быть).
Ну что ж, берем делаем приложение:
- пачка потоков (1...32) делает какую-то простую работу (копирует строку)
- складывает результат в очередь
- и выгребатор, в одной очереди, результаты выгребает.
Достаточно ожидаемо упираемся в выгребатор (а так же в malloc):
Да, с ростом количества потоков становится незначительно быстрее (40% при переходе от 1 рабочего потока к 32).
В-общем и так понятно, что если единичная задача очень маленькая, то оверхед по раскидыванию задач по потокам и сбору обратно - велик и делать так не надо.
Поэтому перейдем к реальной задаче. В качестве реальной задачи возьмем SHA512, но в качестве данных будем передавать конечно указатель (и размер), а не сами данные, иначе мы (100%) упремся в копирование туда-сюда.
Итак у нас есть:
- lock-free очередь заданий
- Рабочие threads которые из нее берут и работают
- Маленький (относительно задачи) результат, который мы складываем в выходную очередь
- И еще один поток-выгребатор, который разбирает выходную очередь.
При вменяемом размере единичной задачи (я пробовал два размера, 10 и 100 килобайт, один поток их обрабатывает со скоростью ~22000 и 2200 заданий в секунду, соответственно) получается картинка близкая к идеальной:
- Линейный рост до 16 параллельных задач (физических CPU cores у меня как раз 16). На 16-ти ядрах ускорение практически ровно в 16 раз для большой (100кб) задачи и в 15.15 раз для маленькой.
- И медленный рост (еще процентов 20 от 16 до 32 потоков) за счет hyper-threading.
Понятно что к Qt данная конструкция не имеет никакого отношения (ну я потоки запускал через QThread::start) и если нужно взаимодействие с Qt-шной программой, то его нужно делать уже отдельно каким-то способом (вариантов просматривается более одного), кроме того отдельный прикол - это завершение работы worker threads по команде из Qt же, но все эти задачи представляются решаемыми.