Темы, Коро, Любая путаница

Я относительно новичок в Perl и даже новичок в потоках в Perl. У меня есть Perl-скрипт, который принимает данные из 3 разных источников. (2 запроса LDAP и файл, который не всегда существует) Поскольку некоторые части могут занимать больше времени, чем другие, поэтому я решил использовать потоки и очереди. Во время разработки тестирование отдельных компонентов скрипта работало очень хорошо, но после их объединения производительность, похоже, снижается.

Базовая структура состоит из 2 потоков:(чтение файла или чтение записей AD) -> Очередь 1 -> 2 потока:(очистка данных) -> Очередь 2 -> 3-4 потока (сравнение с существующими локальными записями LDAP). Несколько потоков сообщают статистику обратно основному сценарию, и как только все потоки завершены, отправляется электронное письмо со всей статистикой и статусом этого запуска.

Я использую dequeue_nb, и я подумал, что это поможет, но не повезло.

Хит производительности, кажется, находится в очередях. В поисках советов по повышению производительности я наткнулся на несколько статей о том, что потоки perl бесполезны и что они используют coro, POE, Anyevent, IO:async и т. Д.

Это не похоже на проблему "события", так что я не думал, что AnyEvent или POE будут подходить к тому, что я вижу, coros, кажется, использует только один процессор за раз, поэтому я не уверен это будет работать либо. Я думал об использовании их комбинации, но затем моя голова начала болеть. У кого-нибудь есть какие-либо предложения о том, как исправить или устранить неполадки в моем сценарии, или какие-либо предложения о том, как реализовать один из других модулей?

2 ответа

Решение

Проблема с параллелизмом - это синхронизация. Это убийца производительности, это плохо, его следует избегать, если это возможно.

ОП архитектура

Давайте посмотрим на вашу архитектуру:

+--------------+--------------+
|   Input 1    |   Input 2    |
+--------------+--------------+
|           QUEUE A           |
+--------------+--------------+
|   Scrub 1    |   Scrub 2    |
+--------------+--------------+
|           QUEUE B           |
+---------+---------+---------+
| Compare | Compare | Compare |
+---------+---------+---------+

обсуждение

Очередь А должна быть синхронизирована по четырем потокам; Очередь Б через 5-6. Только один поток может получить доступ к очереди в любое время, поэтому большую часть времени ваши потоки будут ждать, а не работать!

Параллельная конвейерная архитектура

Несколько другая архитектура может выглядеть так:

+-----------+    +-----------+
|  Input 1  |    |  Input 2  |
+-----------+    +-----------+
| QUEUE  1A |    | QUEUE  2A |
+-----------+    +-----------+
|  Scrub 1  |    |  Scrub 2  |
+-----------+    +-----------+
| QUEUE  1B |    | QUEUE  2B |
+-----+-----+    +-----+-----+
| Cmp | Cmp |    | Cmp | Cmp |
+-----+-----+    +-----+-----+

обсуждение

Здесь очереди A связаны только с двумя потоками (-> меньше ожидающих), а очереди B - только с тремя. Эта архитектура должна работать быстрее для аналогичного размера / сложности ввода. Если вход 2 был значительно короче, весь конвейер 2 работал бы до того, как конвейер 1 хотя бы наполовину закончил. Однако это намного лучше, чем использование одного процесса для каждого конвейера.

Архитектура спринклерной лужайки

концепция

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

Каждая запись очереди должна идти в другую очередь:

  +-----------+   +-----------+
  |  Input 1  |   |  Input 2  |
  +-----------+   +-----------+
        |      \ /      |
  +-----------+   +-----------+
  | QUEUE  1A |   | QUEUE  2A |
  +-----------+   +-----------+
  |  Scrub 1  |   |  Scrub 2  |
  +-----------+   +-----------+
        / | \ \   / / | \      

+-------+-------+-------+-------+
| Q. 1B | Q. 2B | Q. 3B | Q. 4B |
+-------+-------+-------+-------+
|  Cmp  |  Cmp  |  Cmp  |  Cmp  |
+-------+-------+-------+-------+

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

обсуждение

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

Так что, если эта архитектура имеет смысл, зависит от ваших точных требований.

Он медленнее для входов одинакового размера, но лучше работает на нерегулярных входах.

Приложения

По реализации:

Какая структура используется, является вторичной по отношению к архитектуре. Если вы передаете только текстовые строки, я настоятельно рекомендую использовать каналы. Если вам нужно передать типы данных или объекты Perl, вам, вероятно, придется принять на себя дополнительные издержки использования реальной очереди: при добавлении непересекающейся переменной в очередь необходимо дополнительно сделать глубокую копию (см. Ответ @Leon Timmermans) на все накладные расходы синхронизации.

По масштабируемости:

Архитектура 1 и 3 не фиксирована по количеству потоков. Я настоятельно рекомендую использовать эту гибкость для сравнения различных композиций. Практическое правило заключается в том, что вы должны использовать от n до 2n потоков, где n - количество процессоров (или аппаратных потоков). Это можно рассматривать как максимально разумное число для нитей одной стадии. Кроме того, вы получаете только штраф памяти и без ускорения. Точка насыщения производительности может быть достигнута раньше, когда каскад может обрабатывать ввод быстрее, чем он подается.

Какие данные вы помещаете в очереди? Простые данные AFAIK дешевле, чем сложные структуры, поскольку их необходимо клонировать и копировать как минимум дважды. Я планировал написать более быструю реализацию очереди (большая часть работы уже выполнена на самом деле), но еще не опубликовал это.

Другие вопросы по тегам