基本的 POSIX 实用程序是否并行化?

hom*_*ris 19 utilities multithreading parallelism

在常见的Linux发行版,做事业一样rmmvlsgrepwc,等在平行于它们的参数运行?

换句话说,如果我grep在 32 线程 CPU 上处理一个大文件,它会比在双核 CPU 上运行得更快吗?

Gil*_*il' 28

您可以通过检查该实用程序是否与pthread库链接来获得第一印象。任何使用操作系统线程的动态链接程序都应该使用 pthread 库。

ldd /bin/grep | grep -F libpthread.so
Run Code Online (Sandbox Code Playgroud)

例如在 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
Run Code Online (Sandbox Code Playgroud)

但是,由于程序链接到本身与 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
Run Code Online (Sandbox Code Playgroud)

因此,实际上有机会被并行化的唯一工具是sort. (timeout仅链接到 libpthread,因为它链接到 librt。)GNUsort确实并行工作:可以使用该--parallel选项配置线程数,默认情况下,它每个处理器使用一个线程,最多 8 个。(使用更多的处理器会越来越少)随着处理器数量的增加而受益,逐渐减少的速度取决于任务的可并行性。)

grep根本没有并行化。PCRE 库实际上链接到 pthread 库只是因为它提供了使用锁的线程安全函数,并且锁操作函数在 pthread 库中。

在处理大量数据时,从并行化中受益的典型简单方法是将这些数据拆分为多个片段,然后并行处理这些片段。在 grep 的情况下,保持文件大小可管理(例如,如果它们是日志文件,请经常轮换它们)并在每​​个文件上调用 grep 的单独实例(例如使用GNU Parallel)。请注意,grepping 通常是 IO 绑定的(如果您有一个非常复杂的正则表达式,或者如果您遇到 GNU grep 的某些 Unicode 极端情况,它的性能很差,则它仅受 CPU 限制),因此您不太可能从中受益有很多线程。

  • 这个答案对线程化了哪些 POSIX 实用程序做出了正确的结论,但未来的读者应该注意……如果应用于其他实用程序,这个答案可能会产生误导。原因:一些工具使用 [fork()](https://man7.org/linux/man-pages/man2/fork.2.html) 并行化,这不是 pthreads 的一部分。一些工具作为插件动态加载到库中,但不会出现在 `ldd` 中。即使核心程序没有,您也不能排除使用线程的插件。 (9认同)
  • @AdminBee 问题是关于“POSIX 实用程序”,而不是关于 POSIX _platforms_。`grep` 是一个 POSIX 实用程序,无论整个平台是否已经过 POSIX 合规性测试。(顺便说一句,“Linux 不是 POSIX”是错误的,因为存在一个带有 Linux 内核(和 GNU 用户空间)的平台,该平台已通过 POSIX 合规性认证。) (7认同)
  • @schily 所有现代 unix 都包含 pthread 和 libc,但是在与 POSIX 线程相关函数(例如 [Solaris](https://docs.oracle.com/)上的“pthread_create”)链接时,您仍然需要“-lpthread” cd/E53394_01/pdf/E54803.pdf)、[Linux](https://www.man7.org/linux/man-pages/man3/pthread_create.3.html)、[FreeBSD](https://www. freebsd.org/cgi/man.cgi?query=pthread_create&sektion=3&manpath=FreeBSD+12.1-RELEASE+and+Ports) 等 (3认同)
  • @schily 这在 _some_ Unix 系统上可能是正确的,但问题特别提到了“common Linux distributive (sic.)”,但事实并非如此。 (3认同)

And*_*ton 7

找到答案的另一种方法是使用类似sysdig检查进程执行的系统调用的方法。例如,如果您想查看是否rm创建了任何线程(通过clone系统调用),您可以执行以下操作:

# sysdig proc.name=rm and evt.type=clone and evt.dir='<'
Run Code Online (Sandbox Code Playgroud)

随着那次跑步,我做到了:

$ mkdir foo
$ cd foo
$ touch {1..9999}
$ rm *
Run Code Online (Sandbox Code Playgroud)

并没有看到克隆 - 那里没有线程。你可以对其他工具重复这个实验,但我认为你不会发现它们是线程化的。

请注意,这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)
Run Code Online (Sandbox Code Playgroud)

  • @schily 小心:通过进程“模拟”线程来自 Solaris,所以你在这里批评 Solaris。现在,你能不能停止对 Linux 的执着追求?只是让认识你的人感到无聊,让不认识你的人感到困惑。 (7认同)
  • @schily:建议是检查系统调用以创建线程。显然,确切的系统调用取决于确切的操作系统,并且 `clone` 字面上是作为 **example** 给出的。 (3认同)
  • 您还可以使用例如`strace -e clone rm *` 来测试特定的执行,它的优点是不需要以root 身份进行任何干预。这不会检测到具有默认为关闭的并行化选项的程序,但无论如何这里没有方法是完全准确的。 (2认同)
  • _问题说`POSIX`..._。这个问题还说“在一个常见的 Linux 分布式 [原文如此] ......” (2认同)

ctr*_*lor 7

请参阅xargs或 gnu parallel,了解如何并行运行它们。

然而,随着更多进程的添加,可并行化的部分将趋向于零时间。这将留下不可并行的部分,不会变得更快。因此,通过添加更多进程,任务的速度是有限的。很快,您就会遇到添加流程几乎没有什么区别的情况。

然后是通信开销:添加进程会使其变慢。如果添加进程的好处低于添加它的成本,那么它可能会变慢。