Сколько потоков требуется для создания их плохим выбором?

>>> from datetime import date
>>>
>>> repr(date.today())        # calls date.today().__repr__()
'datetime.date(2009, 1, 16)'
>>> eval(_)                   # _ is the output of the last command
datetime.date(2009, 1, 16)

вывод является строкой, которая может быть проанализирована интерпретатором Python и результатами в равном объекте.

, Если это не возможно, это должно возвратить строку в форме <...some useful description...>.

12
задан Peter Mortensen 17 October 2009 в 09:17
поделиться

14 ответов

According to Amdahl's law that was discussed by Herb Sutter in his article:

Some amount of a program's processing is fully "O(N)" parallelizable (call this portion p), and only that portion can scale directly on machines having more and more processor cores. The rest of the program's work is "O(1)" sequential (s). [1,2] Assuming perfect use of all available cores and no parallelization overhead, Amdahl's Law says that the best possible speedup of that program workload on a machine with N cores is given by
formula image

In your case I/O operations could take most of the time, as well as synchronization issues. You could count time that will be spend in blocking(?) slow I/O operations and approximately find number of threads that will be suitable for your task.


Full list of concurrency related articles by Herb Sutter could be found here.

13
ответ дан 2 December 2019 в 02:55
поделиться

Ориентировочно вам, вероятно, следует поддерживать количество потоков от 10 до 100, чтобы минимизировать конфликты блокировок и накладные расходы на переключение контекста.

1
ответ дан 2 December 2019 в 02:55
поделиться

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

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

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

Просто начните с потоками и беспокоиться об этом позже :)

1
ответ дан 2 December 2019 в 02:55
поделиться

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

Если у вас загруженность ЦП, просто продолжайте добавлять потоки, пока загрузка ЦП не превышает 100%. Когда вы ограничены вводом-выводом , перегрузка диска может в какой-то момент помешать большему количеству потоков повысить скорость. Это вам придется выяснить самому.

Вы видели Intel Threading Building Blocks ? Обратите внимание, что я не могу комментировать, нужно ли это вам. Я сделал всего лишь небольшой игрушечный проект на Windows, и это было несколько лет назад. (Он был чем-то похож на ваш, BTW: он рекурсивно просматривает иерархию папок и считает строки в файлах исходного кода, которые он находит.)

2
ответ дан 2 December 2019 в 02:55
поделиться

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

3
ответ дан 2 December 2019 в 02:55
поделиться

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

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

3
ответ дан 2 December 2019 в 02:55
поделиться

Звучит не банально, но вы используете столько потоков, сколько вам нужно.

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

Первый график, в частности, поможет вам определить, где находится узкое место в мощности процессора. В какой-то момент вы станете либо I / O ограниченным (что означает, что диск не может загружать данные достаточно быстро), либо количество потоков станет настолько большим, что повлияет на производительность машины.

Второе случается. Я видел один фрагмент кода, который в итоге создал более 30 000 потоков. В конечном итоге это оказалось быстрее, так как ограничение было ограничено 1000.

Другой способ взглянуть на это: насколько быстро достаточно быстро? Точка, в которой ввод-вывод становится узким местом, - это одно, но вы можете достичь точки раньше, когда он «достаточно быстр».

4
ответ дан 2 December 2019 в 02:55
поделиться

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

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

Как предлагают другие плакаты, сначала профиль!

5
ответ дан 2 December 2019 в 02:55
поделиться

Чтобы уточнить, это действительно зависит от

IO boundedness of the problem
    how big are the files
    how contiguous are the files
    in what order must they be processed
    can you determine the disk placement
how much concurrency you can get in the "global structure insert"
    can you "silo" the data structure with a consolidation wrapper
the actual CPU cost of the "global structure insert" 

. Например, если ваши файлы находятся в массиве флэш-памяти 3 терабайта, то решение отличается от решения, если они находятся на одном диске (где, если " "вставка глобальной структуры" занимает меньше времени, чем чтение. Проблема ограничена вводом / выводом , и с таким же успехом можно использовать двухступенчатый конвейер с 2 потоками - этап чтения, питающий этап вставки.)

Но в обоих случаях архитектура, вероятно, будет представлять собой двухступенчатый вертикальный конвейер. n потоков для чтения и m для записи потоков, где n и m определяются «естественным параллелизмом» для этапа.

Создание потока для каждого файла, вероятно, приведет к перегрузке диска. Точно так же, как вы адаптируете количество потоков процесса, связанного с ЦП, к естественно достижимому параллелизму ЦП (и вышеизложенное, что создает накладные расходы на переключение контекста, известное как разрушение), то же самое верно и на стороне ввода-вывода - в некотором смысле вы можете думать о перегрузка диска как «переключение контекста на диске».

6
ответ дан 2 December 2019 в 02:55
поделиться

Я не уверен насчет HP / UX, но в мире Windows мы используем пулы потоков для решения такого рода проблем. Раймонд Чен писал об этом некоторое время назад, на самом деле ...

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

11
ответ дан 2 December 2019 в 02:55
поделиться

Ответ в некоторой степени зависит от того, насколько интенсивно загружается процессор при обработке каждого файла.

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

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

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

4
ответ дан 2 December 2019 в 02:55
поделиться

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

4
ответ дан 2 December 2019 в 02:55
поделиться

More threads will not necessarily give you higher throughput. Threads have a non-trivial cost, both to create (in terms of CPU time and OS resources) and to run (in terms of memory and scheduling). And the more threads you have, the more potential for contention with other threads. Adding threads can sometimes even slow down execution. Each problem is subtly different, and you are best off writing a nice, flexible solution and experimenting with the parameters to see what works best.

Your example code, spawning a thread for each file, would almost immediately swamp the system for values of max_threads beyond around 10. As others have suggested, a thread pool with a work queue is what you probably want. The fact that each file is independent is nice, as that makes it almost embarrassingly parallel (save for the aggregation at the end of each unit of work).

Some factors that will affect your throughput:

  • Number of CPU cores
  • The number of disk channels (spindles, RAID devices, etc)
  • The processing algorithm, and whether the problem is CPU or I/O bound
  • Contention for the master statistics structure

Last year I wrote an application that does essentially the same as you describe. I ended up using Python and the pprocess library. It used a multi-process model with a pool of worker processes, communicating via pipes (rather than threads). A master process would read the work queue, chop up the input into chunks, and send chunk info to the workers. A worker would crunch the data, collecting stats, and when it was done send the results to back the master. The master would combine the results with the global totals and send another chunk to the worker. I found it scaled almost linearly up to 8 worker threads (on an 8-core box, which is pretty good), and beyond that it degraded.

Some things to consider:

  • Use a thread pool with a work queue, where the number of threads is likely around the number of cores in your system
  • Alternatively, use a multi-process setup, which communicates via pipes
  • Evaluate using mmap() (or equivalent) to memory map the input files, but only after you've profiled the baseline case
  • Read data in multiples of the block size (eg. 4kB), and chop up into lines in memory
  • Build in detailed logging from the start, to aid debugging
  • Keep an eye on contention when updating the master statistics, although it will likely be swamped by the processing and read times of the data
  • Don't make assumptions - test, and measure
  • Set up a local dev environment that is as close as possible to the deployment system
  • Use a database (such as SQLite) for state data, processing results, etc
  • The database can keep track of which files have been processed, which lines had errors, warnings, etc
  • Only give your app read-only access to the original directory and files, and write your results elsewhere
  • Be careful not to try to process files that are open by another process (there's a few tricks here)
  • Careful you don't hit OS limits of the number of files per directory
  • Profile everything, but be sure to only change one thing at a time, and keep detailed records. Performance optimization is hard.
  • Set up scripts so you can consistently re-run tests. Having a DB helps here, as you can delete the records that flag a file as having been processed and re-run against the same data.

When you have a significant number of files in the one directory as you describe, aside from potentially hitting filesystem limits, the time to stat the directory and figure out which files you've already processed and which you still need to goes up significantly. Consider breaking up the files into subdirectories by date, for example.

One more word on performance profiling: be careful when extrapolating performance from small test data sets to super-huge data sets. You can't. I found out the hard way that you can reach a certain point where regular assumptions about resources that we make every day in programming just don't hold any more. For example, I only found out the statement buffer in MySQL is 16MB when my app went way over it! And keeping 8 cores busy can take a lot of memory, but you can easily chew up 2GB of RAM if you're not careful! At some point you have to test on real data on the production system, but give yourself a safe test sandbox to run in, so you don't munge production data or files.

Directly related to this discussion is a series of articles on Tim Bray's blog called the "Wide Finder" project. The problem was simply to parse logfiles and generate some simple statistics, but in the fastest manner possible on a multicore system. Many people contributed solutions, in a variety of languages. It is definitely worth reading.

2
ответ дан 2 December 2019 в 02:55
поделиться

Вы сказали, что все файлы находятся в одном каталоге. Означает ли это, что все они находятся на одном физическом диске?

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

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

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

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

6
ответ дан 2 December 2019 в 02:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: