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

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

在常见的 Linux 发行版中,诸如rmmvlsgrepwc等实用程序是否对其参数并行运行?

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

答案1

您可以通过检查实用程序是否与库链接来获得第一印象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。)GNUsort确实并行工作:可以使用以下命令配置线程数:--parallel选项,默认情况下每个处理器使用一个线程,最多 8 个。(随着处理器数量的增加,使用更多处理器带来的好处越来越少,逐渐减少的速度取决于任务的并行化程度。)

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

在处理大量数据时,从并行化中受益的典型简单方法是将这些数据分割成多个片段,然后并行处理这些片段。对于 grep,保持文件大小可控(例如,如果它们是日志文件,则足够频繁地轮换它们)并在每​​个文件上调用单独的 grep 实例(例如使用GNU 并行)。请注意,grepping 通常是 IO 绑定的(如果您有一个非常复杂的正则表达式,或者如果您遇到 GNU grep 的一些 Unicode 极端情况(性能较差),那么它只是 CPU 绑定),因此您不太可能从中获得太多好处有很多线程。

答案2

找到答案的另一种方法是使用诸如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)

答案3

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

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

然后是通信开销:添加进程会使速度变慢。如果添加进程的收益低于添加进程的成本,那么进程可能会变慢。

答案4

如果您基本上对您指定的实用程序感兴趣,那么不太可能存在这些命令的线程版本。

更糟糕的是,如果这种变体确实存在,它很可能会比单线程版本慢。

这是由于您命名的实用程序都具有大量文件系统交互(如果以多线程完成)会损害内核优化(例如预读)。

一个良好实现的内核例如检测文件中的线性读取并引起线性读取,例如通过提前获取grep要获取的文件内容。grep

操作mvrename对一个或两个目录的操作,并且需要内核中的目录锁。对这些目录的另一个重命名操作不能同时发生,除非以非原子方式实现。

另一方面,最古老的免费tar实现 ( ) 自 30 年来就在两个基本任务方面进行了并行化:有两个进程和两个进程之间的一块共享内存,允许一个进程执行存档读/写操作,另一个进程可以执行存档读/写操作star同时执行文件系统 I/O。

您的具体问题grep可以用“基本上是”来回答,因为使用多个 CPU 时内核中的文件系统预取会比只有一个 CPU 时更快。如果您操作的文件不是很大并且该文件已经在内核缓存内,则没有预取优势......

顺便说一句:现代 shell 有一个内置time功能,不仅可以显示时间,还可以根据 USER 和 SYS CPU 时间与挂钟时间之和的比率计算百分比。如果相关time输出超过 100%,则您运行的实用程序确实利用了多个 CPU。然而,对于非线程实用程序,该值通常约为 105%。

最后:并行化也发生在进程级别,并且并行化版本的make运行速度可以轻松地比非并行化版本快 3 倍。

如果您的平台允许您在运行时关闭 CPU,我鼓励您关闭n-1CPU 并将结果与​​其他相同的计算机上的多 CPU 环境进行比较。

相关内容