Я иду с другого направления: начал использовать pthreads
в своем приложении, которое я недавно заменил на C ++ 11 std::thread
. Теперь я играю с высокоуровневыми конструкциями, такими как псевдоусиливающий пул потоков и, еще более абстрактно, Intel Threading Building Blocks . Я бы посчитал, что GCD будет на уровне или даже выше чем TBB.
Несколько комментариев:
- imho, pthread не сложнее , чем GCD: в своем основном ядре pthread на самом деле содержит очень мало команд (всего лишь несколько: использование только тех, которые упомянуты в OP, даст вам 95% + от функциональность, которая вам когда-либо понадобится). Как и любая библиотека более низкого уровня, именно то, как вы их собираете и как вы используете, дает вам ее силу. Не забывайте, что в конечном итоге библиотеки, такие как GCD и TBB, будут вызывать библиотеку потоков, такую как
pthreads
или std::thread
.
- иногда, это не , что вы используете, а то, как вы его используете , который определяет успех против неудачи. Как сторонники библиотеки, TBB или GCD расскажут вам обо всех преимуществах использования их библиотек, но пока вы не попробуете их в реальном контексте приложения, все это имеет теоретическое преимущество . Например, когда я прочитал о том, как легко было использовать мелкозернистый параллель_ для , я сразу же использовал его в задаче, для которой, по моему мнению, может быть полезен параллелизм. Естественно, меня тоже привлек тот факт, что TBB будет обрабатывать все детали оптимального распределения нагрузки и распределения потоков. Результат? TBB занял в пять раз больше времени, чем однопоточная версия! Но я не виню TBB: в ретроспективе это, очевидно, случай неправильного использования parallel_for: когда я читал мелкий шрифт, я обнаружил накладные расходы, связанные с использованием parallel_for и предполагающие, что в моем случае затраты на переключение контекста и вызовы дополнительных функций перевешивают преимущества использования нескольких потоков. Таким образом, вы должны профилировать ваш случай, чтобы увидеть, какой из них будет работать быстрее. Возможно, вам придется реорганизовать свой алгоритм, чтобы использовать меньшие накладные расходы.
- почему это происходит? Как pthread или никакие потоки не могут быть быстрее, чем GCD или TBB? Когда дизайнер разрабатывает GCD или TBB, он должен сделать предположения о среде, в которой будут выполняться задачи. На самом деле, библиотека должна быть достаточно общей, чтобы она могла обрабатывать странные, непредвиденные варианты использования разработчиком. Эти общие реализации не придут бесплатно. С другой стороны, библиотека будет запрашивать оборудование и текущую рабочую среду, чтобы лучше распределить нагрузку. Будет ли это работать на вашу пользу? Единственный способ узнать это - попробовать.
- есть ли какая-либо польза от изучения библиотек более низкого уровня, таких как
std::thread
, когда библиотеки более высокого уровня доступны? Ответ - громкое ДА . Преимущество использования библиотек более высокого уровня заключается в абстрагировании от деталей реализации . недостаток в использовании библиотек более высокого уровня также является абстракцией от деталей реализации . При использовании pthreads
я в высшей степени осведомлен об общем состоянии и времени жизни объектов, потому что, если я ослаблю свою охрану, особенно в средних и крупных проектах, я очень легко смогу получить условия гонки или ошибки памяти . Эти проблемы исчезают, когда я использую библиотеку более высокого уровня? На самом деле, нет. Кажется, что мне не нужно думать о них, но на самом деле, если я получу небрежные детали, реализация библиотеки также потерпит крах. Таким образом, вы обнаружите, что если вы понимаете конструкции нижнего уровня, все эти библиотеки действительно имеют смысл, потому что в какой-то момент вы будете думать о их реализации самостоятельно, если будете использовать вызовы более низкого уровня. Конечно, в этот момент обычно лучше использовать проверенный временем и отлаженный вызов библиотеки.
Итак, давайте разберем возможные реализации:
- TBB / GCD библиотечные вызовы: наибольшее преимущество для начинающих потоков. Они имеют более низкие барьеры для входа по сравнению с изучением библиотек более низкого уровня. Однако они также игнорируют / скрывают некоторые ловушки использования многопоточности. Динамическая балансировка нагрузки сделает ваше приложение более переносимым без дополнительного кодирования с вашей стороны.
pthread
и std::thread
вызовы: на самом деле очень мало вызовов для изучения, но для их правильного использования требуется внимание к деталям и глубокое понимание работы вашего приложения. Если вы сможете понимать потоки на этом уровне, API библиотек более высокого уровня, безусловно, будет иметь больше смысла.
- однопоточный алгоритм : давайте не будем забывать о преимуществах простого однопоточного сегмента. Для большинства приложений однопоточность проще для понимания и намного менее подвержена ошибкам, чем многопоточность. На самом деле, во многих случаях это может быть подходящим выбором дизайна. В том-то и дело, что реальное приложение проходит через различные многопоточности и однопоточности: может быть, многопоточность не нужна постоянно.
Какой из них самый быстрый? Удивительная правда в том, что это может быть любой из трех вышеперечисленных. Чтобы получить преимущества многопоточности в скорости, вам может потребоваться коренная реорганизация ваших алгоритмов. Перевешивают ли выгоды затраты, зависит от конкретного случая.
Да, и ОП спросил о случаях, когда thread_pool не подходит. Простой случай: если у вас плотный цикл, для которого не требуется много циклов на цикл, использование thread_pool может стоить дороже, чем преимущества без серьезной переработки. Также следует помнить о накладных расходах вызовов функций, таких как лямбда, через пулы потоков по сравнению с использованием одного узкого цикла.
Для большинства приложений многопоточность является своего рода оптимизацией, поэтому делайте это в нужное время и в нужных местах.