Больше потоков, лучшая производительность? - PullRequest
5 голосов
/ 21 апреля 2009

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

На мой взгляд, существует три основных подхода (если у вас есть какие-либо другие настройки, пожалуйста, поделитесь):

  1. Обработка всех сообщений в одном потоке.
  2. Наличие отдельных потоков для отдельных типов сообщений (общие, пользовательский интерфейс, сеть и т. Д.)
  3. Наличие нескольких потоков, которые совместно используют и обрабатывают одну очередь сообщений.

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

И еще один вопрос: вы бы порекомендовали внедрить такую ​​систему в стандартную систему обмена сообщениями Windows или внедрить отдельный механизм очереди и почему?

Ответы [ 10 ]

8 голосов
/ 21 апреля 2009

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

  1. сообщения приходят часто
  2. сообщения независимы и не слишком сильно зависят от общих ресурсов
  3. желательно ответить на поступающее сообщение как можно быстрее
  4. вы хотите, чтобы приложение хорошо масштабировалось на архитектурах обработки (т. Е. Многоядерные / многопроцессорные системы)
  5. масштабируемость является ключевым требованием к дизайну (например, больше сообщений с более высокой скоростью)
  6. желательна устойчивость к потере потока / длительным операциям

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

# 1 Один поток обрабатывает все сообщения => пул потоков только с одним потоком

# 2 Поток на N типов сообщений => пул потоков с N потоками, каждый поток просматривает очередь, чтобы найти соответствующие типы сообщений

# 3 Несколько потоков для всех сообщений => пул потоков с несколькими потоками

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

Для большинства платформ доступно множество хороших библиотек пула потоков, включая .NET, C ++ / STL, Java и т. Д.

Что касается вашего второго вопроса, использовать ли стандартный механизм отправки сообщений Windows. Этот механизм имеет значительные накладные расходы и на самом деле предназначен только для прокачки сообщений через цикл пользовательского интерфейса Windows-приложения. Если это не та проблема, которую вы пытаетесь решить, я бы не советовал использовать ее в качестве общего решения для рассылки сообщений. Кроме того, сообщения Windows несут очень мало данных - это не объектно-ориентированная модель. Каждое сообщение Windows имеет код и 32-битный параметр. Этого может быть недостаточно для построения чистой модели обмена сообщениями. Наконец, очередь сообщений Windows не предназначена для обработки таких случаев, как насыщение очереди, нехватка потоков или повторная постановка сообщений в очередь; это случаи, которые часто возникают при реализации достойного решения для очереди сообщений.

3 голосов
/ 21 апреля 2009

Все зависит.

Например:

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

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

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

3 голосов
/ 21 апреля 2009

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

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

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

Убедитесь, что вы используете потоковую структуру данных для очереди.

1 голос
/ 21 апреля 2009

Обратите внимание, что существуют две разные цели производительности, и вы еще не указали, на что вы нацелены: пропускная способность и скорость реагирования.

Если вы пишете приложение с графическим интерфейсом, пользовательский интерфейс должен быть отзывчивым. Вам не важно, сколько кликов в секунду вы можете обработать, но вам важно показать ответ в течение одной десятой секунды или около того (в идеале меньше). Это одна из причин, по которой лучше иметь отдельный поток, посвященный обработке графического интерфейса пользователя (другие причины были упомянуты в других ответах). Поток GUI должен в основном конвертировать сообщения Windows в рабочие элементы и позволить вашей рабочей очереди справиться с тяжелой работой. Как только рабочий будет сделан, он уведомляет поток GUI, который затем обновляет отображение, чтобы отразить любые изменения. Это делает такие вещи, как рисование окна, но не рендеринг данных для отображения. Это дает приложению быструю «привязанность», чего хотят большинство пользователей, когда они говорят о производительности. Их не волнует, что для выполнения чего-то сложного требуется 15 секунд, если они нажимают кнопку или меню, они реагируют мгновенно.

Другая характеристика производительности - пропускная способность. Это количество заданий, которые вы можете обработать за определенное время. Обычно этот тип настройки производительности необходим только для приложений серверного типа или другой сложной обработки. Он измеряет, сколько веб-страниц может быть обработано за час, или сколько времени занимает рендеринг DVD. Для такого рода заданий вы хотите иметь 1 активный поток на процессор. Меньше, чем это, и вы будете тратить время простоя часов. Более того, и потоки будут конкурировать за процессорное время и спотыкаясь друг на друга. Взгляните на второй график в этой статье DDJ Articles для компромисса, с которым вы имеете дело. Обратите внимание, что идеальное число потоков больше, чем количество доступных процессоров, из-за таких вещей, как блокировка и блокировка. Ключом является число активных потоков.

1 голос
/ 21 апреля 2009

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

Я бы выбрал вариант 3, используя любые абстракции, которые предлагает мой язык по выбору.

0 голосов
/ 22 июня 2009

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

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

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

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

0 голосов
/ 21 апреля 2009

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

(1) представляет собой узкое место для обработки сообщений
(3) вводит конфликт блокировки, потому что вам нужно синхронизировать доступ к вашей общей очереди.

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

Если вам нравится вариант №2, звучит так, как если бы вы были заинтересованы в реализации архитектуры SEDA . Потребуется некоторое чтение, чтобы понять, что происходит, но я думаю, что архитектура хорошо вписывается в ваш образ мышления.

Кстати, Выход - хорошая гибридная реализация на C ++ / Python.

0 голосов
/ 21 апреля 2009

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

Использование среды обмена сообщениями Windows для обработки запросов зависит от рабочей нагрузки каждого потока. Я думаю, что Windows ограничивает нет. сообщений, которые могут быть поставлены в очередь максимально до 10000. В большинстве случаев это не должно быть проблемой. Но если у вас много сообщений в очереди, это может быть что-то, что следует принять во внимание.

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

0 голосов
/ 21 апреля 2009

Я думаю, это зависит от того, как долго будет работать каждый поток. Каждое сообщение занимает одинаковое количество времени для обработки? Или некоторые сообщения займут, например, несколько секунд. Если бы я знал, что для завершения Сообщения А потребуется 10 секунд, я бы определенно использовал новый поток, потому что зачем мне задерживать очередь для долго работающего потока ...

Мои 2 цента.

0 голосов
/ 21 апреля 2009

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

Продуманный ответ на этот вопрос приведет вас к лучшему ответу на следующий вопрос: «Как мне использовать несколько потоков в моем приложении?»

И это должно быть последующим вопросом; не основной вопрос. Первый вопрос должен быть почему, а не как.

...