Вопрос

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

Однако мой вопрос таков - какова хорошая точка отсечения для подобных потоков ввода-вывода?Я знаю, что это была бы лишь приблизительная оценка, но разве мы говорим о сотнях?тысячи?


Редактировать:

Спасибо всем вам за ваши ответы, похоже, мне просто нужно будет протестировать это, чтобы узнать свой потолок количества потоков.Однако вопрос в том, что:откуда мне знать, что я достиг этого потолка?Что именно я должен измерить?

Это было полезно?

Решение

Некоторые люди сказали бы, что два тем слишком много - я не совсем в этом лагере :-)

Вот мой совет: измеряй, а не гадай. Одно из предложений состоит в том, чтобы сделать его настраиваемым и изначально установить значение 100, затем выпустить свое программное обеспечение на волю и следить за тем, что происходит.

Если использование вашего потока достигает максимума в 3, то 100 - это слишком много.Если большую часть дня он остается на уровне 100, увеличьте его до 200 и посмотрите, что произойдет.

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


Для уточнения и доработки:

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

Я написал код для объединения потоков и подключений к базе данных, и у них есть следующие функции (которые, я считаю, необходимы для повышения производительности):

  • минимальное количество активных потоков.
  • максимальное количество потоков.
  • завершение работы потоков, которые некоторое время не использовались.

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

Вам нужно сбалансировать использование ресурсов из-за наличия неиспользуемых потоков (A) с использованием ресурсов из-за нехватки потоков для выполнения работы (B).

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

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

Первый - это количество доступных подключений к базе данных.Это может быть жестким ограничением, если только вы не можете увеличить его в СУБД - я собираюсь предположить, что в этом случае ваша СУБД может принимать неограниченное количество подключений (хотя в идеале вы должны измерять и это).

Затем количество потоков, которые у вас должны быть, зависит от вашего исторического использования.Минимум, который вы должны запустить, - это минимальное число, которое у вас когда-либо работало + A%, с абсолютным минимумом (например, и настраивайте его точно так же, как A) 5.

Максимальное количество потоков должно быть вашим историческим максимумом + B%.

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


В ответ на: "что именно я должен измерить?" вопрос:

Что вам следует измерить конкретно, так это максимальное количество потоков, одновременно используемых (например, ожидающих возврата из вызова базы данных) под нагрузкой.Затем добавьте коэффициент запаса прочности в размере 10% для пример (подчеркнуто, поскольку другие плакаты, похоже, воспринимают мои примеры как фиксированные рекомендации).

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

Другие советы

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

  1. Размер стека потоков:В Linux размер стека потоков по умолчанию составляет 8 МБ (вы можете использовать ulimit -a, чтобы узнать это).
  2. Максимальный объем виртуальной памяти, поддерживаемый данным вариантом ОС.Ядро Linux 2.4 поддерживает адресное пространство памяти размером 2 ГБ.с ядром 2.6, у меня немного больше (3 ГБ)
  3. [1] показаны расчеты максимального количества потоков на каждую поддерживаемую максимальную виртуальную машину.Для 2.4 получается около 255 потоков.для 2.6 число немного больше.
  4. Что за планировщик ядра у тебя?Сравнивая планировщик ядра Linux 2.4 с 2.6, последний дает вам планирование O(1) без зависимости от количества задач, существующих в системе, тогда как первый планировщик больше похож на O(n).Таким образом, возможности SMP расписания ядра также играют хорошую роль в максимальном количестве устойчивых потоков в системе.

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

Обратите внимание, что по своему желанию можно создавать тысячи потоков, но когда в Linux заканчивается виртуальная машина, он просто случайным образом начинает убивать процессы (то есть потоки).Это сделано для того, чтобы профиль полезности не был исчерпан.(Функция полезности рассказывает об общесистемной полезности для данного количества ресурсов.При постоянных ресурсах (в данном случае циклов ЦП и памяти) кривая полезности выравнивается при увеличении количества задач).

Я уверен, что планировщик ядра Windows также делает что-то подобное, чтобы справиться с чрезмерным использованием ресурсов.

[1] http://adywicaksono.wordpress.com/2007/07/10/i-can-not-create-more-than-255-threads-on-linux-what-is-the-solutions/

Если ваши потоки выполняют какую-либо ресурсоемкую работу (ЦП/Диск), вы редко увидите преимущества, кроме одного или двух, а слишком большое количество очень быстро приведет к снижению производительности.

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

Хорошим решением является размещение запросов в пуле, которые затем отправляются рабочим потокам из пула потоков (и да, предотвращение непрерывного создания/уничтожения потоков — отличный первый шаг).

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

Вы должны иметь в виду одну вещь: Python (по крайней мере, версия на основе C) использует так называемый глобальная блокировка интерпретатора это может оказать огромное влияние на производительность на многоядерных машинах.

Если вам действительно нужна максимальная отдача от многопоточного Python, возможно, вы захотите использовать Jython или что-то в этом роде.

Как справедливо сказал Пакс: измеряй, не угадывай.Это то, ради чего я сделал DNSwitness и результаты были неожиданными:идеальное количество потоков было намного выше, чем я думал, около 15 000 потоков, чтобы получить самые быстрые результаты.

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

Полные меры (только на французском языке) в Combien de fils d'execution?.

Я написал ряд сильно многопоточных приложений.Обычно я разрешаю указывать количество потенциальных потоков в файле конфигурации.Когда я настраивался на конкретных клиентов, я установил число достаточно высокое, чтобы загрузка всех ядер ЦП была довольно высокой, но не настолько высокой, чтобы у меня возникли проблемы с памятью (на тот момент это были 32-битные операционные системы). время).

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

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

Ответ «большого железа» — это, как правило, один поток на каждый ограниченный ресурс — процессор (привязка к ЦП), рука (привязка к вводу-выводу) и т. д. — но это работает только в том случае, если вы можете направить работу в правильный поток для ресурса. быть доступны.

Если это невозможно, учтите, что у вас есть взаимозаменяемые ресурсы (ЦП) и невзаимозаменяемые ресурсы (оружие).Для процессоров не критично назначать каждый поток конкретному процессору (хотя это помогает с управлением кешем), но для рук, если вы не можете назначить поток руке, вы попадаете в теорию очередей и то, какое оптимальное количество для хранения рук занятый.Обычно я думаю, что если вы не можете маршрутизировать запросы в зависимости от используемой руки, то наличие 2-3 потоков на каждую руку будет вполне правильным.

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

Конечно, вы должны сопоставить все это с «весом» потока.К сожалению, большинство систем имеют очень тяжелые потоки (а то, что они называют «легкими потоками», часто вообще не являются потоками), поэтому лучше ошибиться в сторону меньшего количества.

На практике я видел, что очень тонкие различия могут иметь огромное значение в оптимальном количестве потоков.В частности, проблемы с кэшем и конфликты блокировок могут значительно ограничить объем практического параллелизма.

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

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

ryeguy, я сейчас разрабатываю аналогичное приложение, и количество моих потоков установлено на 15.К сожалению, если я увеличу его до 20, произойдет сбой.Итак, да, я думаю, что лучший способ справиться с этим — измерить, допускает ли ваша текущая конфигурация больше или меньше X потоков.

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

Более подробную информацию о том, как это должно работать, вы можете найти здесь: http://en.wikipedia.org/wiki/Thread_pool_pattern

Я очень часто слышал, что потоков столько же, сколько ядер ЦП.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top