Распараллелены ли базовые утилиты POSIX?

В распространенном дистрибутиве Linux выполните такие утилиты, как rm, mv , ls, grep, wcи т. д. работают параллельно со своими аргументами?

Другими словами, если я выполню grepбольшой файл на 32-поточном ЦП, будет ли он работать быстрее, чем на двухъядерном ЦП?

19
26.06.2020, 19:37
3 ответа

Первое впечатление можно получить, проверив, связана ли утилита с библиотекой pthread. Любая динамически подключаемая программа, использующая потоки ОС, должна использовать библиотеку pthread.

ldd /bin/grep | grep -F libpthread.so

Так, например, в Ubuntu:

for x in $(dpkg -L coreutils grep findutils util-linux | grep /bin/); do if ldd $x | grep -q -F libpthread.so; then echo $x; fi; done

Однако это приводит к большому количеству ложных срабатываний из-за программ, связанных с библиотекой, которая сама связана с pthread. Например, /bin/mkdirв моей системе связан с PCRE (Я не знаю почему… ), который сам связан с pthread. Но mkdirникак не распараллеливается.

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

dpkg -L coreutils grep findutils util-linux | grep /bin/ | xargs grep pthread               
Binary file /usr/bin/timeout matches
Binary file /usr/bin/sort matches

Таким образом, единственным инструментом, который действительно может быть распараллелен, является sort.(timeoutссылается на libpthread только потому, что он ссылается на librt. )GNU sortработает параллельно :количество потоков можно настроить с помощью параметра--parallel, и по умолчанию используется один поток на процессор до 8.(Использование большего количества процессоров дает все меньше и меньше пользы по мере увеличения числа процессоров , уменьшаясь со скоростью, зависящей от того, насколько распараллеливаема задача.)

grepвообще не распараллеливается. Библиотека PCRE на самом деле связана с библиотекой pthread только потому, что она предоставляет потокобезопасные -функции, использующие блокировки, а функции управления блокировками находятся в библиотеке pthread.

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

28
18.03.2021, 23:24

Другой способ найти ответ — использовать что-то вроде sysdigдля проверки системных вызовов, выполняемых процессом. Например, если вы хотите увидеть, создает ли rmкакие-либо потоки (с помощью системного вызова clone), вы можете сделать:

# sysdig proc.name=rm and evt.type=clone and evt.dir='<'

С этим бегом я сделал:

$ mkdir foo
$ cd foo
$ touch {1..9999}
$ rm *

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

Обратите внимание, что clone()также является основой fork(), поэтому, если инструмент запускает какой-либо другой процесс (, например find... -exec), вы увидите этот вывод. Флаги будут отличаться от варианта использования «создать новый поток» :

.
# sysdig proc.name=find and evt.type=clone and evt.dir='<'
...
1068339 18:55:59.702318832 2 find (2960545) < clone res=0 exe=find args=/tmp/foo.-type.f.-exec.rm.{}.;. tid=2960545(find) pid=2960545(find) ptid=2960332(find) cwd= fdlimit=1024 pgft_maj=0 pgft_min=1 vm_size=9100 vm_rss=436 vm_swap=0 comm=find cgroups=cpuset=/.cpu=/user.slice.cpuacct=/user.slice.io=/user.slice.memory=/user.slic... flags=25165824(CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID) uid=1026 gid=1026 vtid=2960545(find) vpid=2960545(find)
7
18.03.2021, 23:24

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

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

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

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

Операция mv— это операция renameв одном или двух каталогах, которая требует блокировки каталога в ядре. Другая операция переименования этих каталогов не может выполняться одновременно, если только она не будет реализована не -атомарным способом.

Самая старая бесплатная tarреализация(star)с другой стороны распараллелена с 30 лет по отношению к двум основным задачам :Есть два процесса и часть общей памяти между ними, что позволяет одному процессу выполнять чтение/запись архива и другой процесс для одновременного выполнения операций ввода-вывода файловой системы.

На ваш конкретный вопрос, связанный с grep, можно ответить «в основном да», поскольку предварительная выборка файловой системы в ядре будет выполняться быстрее с более чем одним ЦП, чем с одним ЦП. Если файл, с которым вы работаете, невелик и если этот файл уже находится в кэше ядра, преимущества предварительной выборки отсутствуют...

Кстати, :Современные оболочки имеют встроенную timeфункцию, которая не только показывает время, но и вычисляет процентное соотношение, вычисляемое из отношения суммы времени ЦП USER и SYS и времени настенных часов. Если связанный вывод timeпревышает 100 %, у вас была запущена утилита, которая использовала преимущество наличия более одного ЦП. Для утилит без резьбы -:однако обычно это что-то вроде 105%.

Наконец, :распараллеливание также имеет место на уровне процесса, и распараллеленная makeлегко может работать в 3 раза быстрее, чем -не распараллеленная версия.

Если ваша платформа позволяет отключать ЦП во время выполнения, я рекомендую вам отключить n-1ЦП и сравнить результаты с многопроцессорной средой на идентичной машине.

4
18.03.2021, 23:24

Теги

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