Сколько накладных расходов при создании потока? - PullRequest
34 голосов
/ 14 октября 2010

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

Ответы [ 9 ]

36 голосов
/ 04 января 2015

Чтобы воскресить этот старый поток, я просто выполнил простой тестовый код:

#include <thread>

int main(int argc, char** argv)
{
  for (volatile int i = 0; i < 500000; i++)
    std::thread([](){}).detach();
  return 0;
}

Я скомпилировал это с g++ test.cpp -std=c++11 -lpthread -O3 -o test. Затем я запускал его три раза подряд на старом (ядро 2.6.18) сильно загруженном (перестраивание базы данных) медленном ноутбуке (Intel Core i5-2540M). Результаты трех последовательных прогонов: 5,647 с, 5,515 с и 5,561 с. Таким образом, мы наблюдаем чуть более 10 микросекунд на поток на этой машине, возможно, намного меньше на вашей.

Это совсем немного, учитывая, что максимальный последовательный порт составляет около 1 бита на 10 микросекунд. Теперь, конечно, существуют различные дополнительные потери потоков, которые можно получить, используя переданные / захваченные аргументы (хотя сами вызовы функций могут навязывать некоторые), замедление кэширования между ядрами (если несколько потоков на разных ядрах сражаются за одну и ту же память одновременно), и т. д. Но в целом я очень сомневаюсь, что представленный вами сценарий использования окажет негативное влияние на производительность (и может дать преимущества, в зависимости от), несмотря на то, что вы уже превентивно назвали концепцию «действительно ужасным кодом», даже не зная, сколько времени требуется для запустить поток.

Хорошая идея или нет, во многом зависит от деталей вашей ситуации. За что еще отвечает вызывающий поток? Что именно участвует в подготовке и выписывании пакетов? Как часто они выписываются (с каким распределением: равномерным, кластерным и т. Д.?) И какова их структура? Сколько ядер у системы? И т.д. В зависимости от деталей, оптимальное решение может быть где угодно: от «вообще нет потоков» до «общего пула потоков» до «потока для каждого пакета».

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

11 голосов
/ 14 октября 2010

Вы определенно не хотите этого делать. Создайте отдельный поток или пул потоков и просто сигнализируйте о доступности сообщений. После получения сигнала поток может выполнить любую необходимую обработку сообщений.

С точки зрения накладных расходов, создание / удаление потоков, особенно в Windows, довольно дорого. Где-то порядка десятков микросекунд, если быть точным. По большей части это следует делать только в начале / конце приложения, за возможным исключением динамически изменяемых пулов потоков.

11 голосов
/ 14 октября 2010

Мне всегда говорили, что создание потоков обходится дешево, особенно по сравнению с альтернативой создания процесса. Если в программе, о которой вы говорите, нет большого количества операций, которые должны выполняться одновременно, то многопоточность может не потребоваться, и, судя по тому, что вы написали, это вполне может быть. Некоторая литература, чтобы поддержать меня:

http://www.personal.kent.edu/~rmuhamma/OpSystems/Myos/threads.htm

Нитки дешевы в том смысле, что

  1. Им нужны только стек и хранилище для регистров , поэтому создавать потоки дешево.

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

  3. Переключение контекста происходит быстро при работе с потоками. Причина в что нам нужно только сохранить и / или восстановить ПК, ИП и регистры.

Больше того же здесь .

В Концепции операционной системы 8-е издание (стр. 155) авторы пишут о преимуществах многопоточности:

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

8 голосов
/ 14 октября 2010

Существуют некоторые издержки при создании потока, но, сравнивая его с обычно медленной скоростью передачи данных последовательного порта (19200 бит / с, является наиболее распространенным), это просто не имеет значения.

7 голосов
/ 14 октября 2010

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

Это сильно зависит от системы. Например, в прошлый раз, когда я использовал VMS, потоки были кошмарно медленными (были годы, но из памяти один поток мог создавать что-то вроде еще 10 в секунду (и если вы продолжали это в течение нескольких секунд, не выходя из потоков, вы бы стали ядром)) тогда как в Linux вы, вероятно, можете создавать тысячи. Если вы хотите точно знать, сравните его в своей системе. Но не очень полезно просто знать, что, не зная больше о сообщениях: имеют ли они в среднем 5 байтов или 100 КБ, отправляются ли они непрерывно, или между ними простаивает строка, и каковы требования к задержке для приложения - все они актуальны к уместности использования потока кода в качестве абсолютного измерения затрат на создание потока. И производительность, возможно, не должна была доминировать при проектировании.

4 голосов
/ 28 января 2014

Для сравнения взгляните на OSX: Ссылка

  • Структуры данных ядра: примерно 1 КБ, пространство стека: 512 КБ. (вторичные потоки): 8 МБ (основной поток OS X), 1 МБ (основной iOS) нить)

  • Время создания: приблизительно 90 микросекунд

Создание нити posix также должно быть вокруг этого (я думаю, не слишком далекого).

3 голосов
/ 14 октября 2010

Создание потока и вычисление в потоке довольно дорого. Все структуры данных должны быть настроены, должен быть зарегистрирован поток, зарегистрированный в ядре, и переключение потока, чтобы новый поток действительно выполнялся (в неуказанное и непредсказуемое время). Выполнение thread.start не означает, что основная функция потока вызывается немедленно. Как отмечается в статье (упоминается при опечатывании), создание потока обходится дешевле, чем создание процесса. В целом, это довольно дорого.

Я бы никогда не использовал поток

  • для коротких вычислений
  • вычисление, где мне нужен результат в моем потоке кода (что значит, я запускаю тему и подождите пока он вернет результат это вычисление

В вашем примере имеет смысл (как уже было указано) создать поток, который обрабатывает все последовательные соединения и является вечным.

НТН

Mario

2 голосов
/ 07 марта 2017

Я использовал вышеупомянутый «ужасный» дизайн в приложении VOIP, которое я сделал.Это работало очень хорошо ... абсолютно без задержек или пропущенных / пропущенных пакетов для локально подключенных компьютеров.Каждый раз, когда поступал пакет данных, создавался поток, который передавал эти данные для обработки на устройства вывода.Конечно, пакеты были большими, поэтому это не вызвало узких мест.Тем временем основной поток может вернуться назад, чтобы ждать и получать другой входящий пакет.

Я пробовал другие проекты, где нужные мне потоки создаются заранее, но это создает свои собственные проблемы.Сначала вам нужно правильно спроектировать свой код для потоков, чтобы получать входящие пакеты и обрабатывать их детерминистическим образом.Если вы используете несколько (предварительно распределенных) потоков, возможно, что пакеты будут обработаны «не по порядку».Если вы используете один (предварительно выделенный) поток для зацикливания и получения входящих пакетов, есть вероятность, что поток может столкнуться с проблемой и прекратит работу, не оставляя потоков для обработки каких-либо данных.

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

2 голосов
/ 24 марта 2011

В любой разумной реализации стоимость создания потока должна быть пропорциональна количеству системных вызовов, которые она включает, и на том же уровне, что и привычные системные вызовы, такие как open и read. Некоторые случайные измерения в моей системе показали, что pthread_create занимает примерно вдвое больше времени, чем open("/dev/null", O_RDWR), что очень дорого по сравнению с чистыми вычислениями, но очень дешево по сравнению с любыми операциями ввода-вывода или другими операциями, которые включают переключение между пользователем и пространством ядра.

...