“无用”地使用“cat”可以提高性能。为什么?

“无用”地使用“cat”可以提高性能。为什么?

文件 1..64 每个都是 160 MB,存储在 RAM 磁盘中。

生成者:

seq 120 | parallel -k 'seq {}0000000 {}9999999 | fmt -30' | head -c 10G > 10G
parallel --pipepart --block -1 -a 10G -j 64 'cat > {#}'

nocat:

#!/bin/bash

export LC_ALL=C
sort -m \
     <(sort -m  \
            <(sort -m  \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 1; sort ) < 1) \
                                        <((rm 2; sort ) < 2) ) \
                                 <(sort -m  \
                                        <((rm 3; sort ) < 3) \
                                        <((rm 4; sort ) < 4) ) ) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 5; sort ) < 5) \
                                        <((rm 6; sort ) < 6) ) \
                                 <(sort -m  \
                                        <((rm 7; sort ) < 7) \
                                        <((rm 8; sort ) < 8) ) ) ) \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 9; sort ) < 9) \
                                        <((rm 10; sort ) < 10) ) \
                                 <(sort -m  \
                                        <((rm 11; sort ) < 11) \
                                        <((rm 12; sort ) < 12) ) ) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 13; sort ) < 13) \
                                        <((rm 14; sort ) < 14) ) \
                                 <(sort -m  \
                                        <((rm 15; sort ) < 15) \
                                        <((rm 16; sort ) < 16) ) ) ) ) \
            <(sort -m  \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 17; sort ) < 17) \
                                        <((rm 18; sort ) < 18) ) \
                                 <(sort -m  \
                                        <((rm 19; sort ) < 19) \
                                        <((rm 20; sort ) < 20) ) ) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 21; sort ) < 21) \
                                        <((rm 22; sort ) < 22) ) \
                                 <(sort -m  \
                                        <((rm 23; sort ) < 23) \
                                        <((rm 24; sort ) < 24) ) ) ) \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 25; sort ) < 25) \
                                        <((rm 26; sort ) < 26) ) \
                                 <(sort -m  \
                                        <((rm 27; sort ) < 27) \
                                        <((rm 28; sort ) < 28) ) ) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 29; sort ) < 29) \
                                        <((rm 30; sort ) < 30) ) \
                                 <(sort -m  \
                                        <((rm 31; sort ) < 31) \
                                        <((rm 32; sort ) < 32) ) ) ) ) ) \
     <(sort -m  \
            <(sort -m  \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 33; sort ) < 33) \
                                        <((rm 34; sort ) < 34) ) \
                                 <(sort -m  \
                                        <((rm 35; sort ) < 35) \
                                        <((rm 36; sort ) < 36) ) ) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 37; sort ) < 37) \
                                        <((rm 38; sort ) < 38) ) \
                                 <(sort -m  \
                                        <((rm 39; sort ) < 39) \
                                        <((rm 40; sort ) < 40) ) ) ) \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 41; sort ) < 41) \
                                        <((rm 42; sort ) < 42) ) \
                                 <(sort -m  \
                                        <((rm 43; sort ) < 43) \
                                        <((rm 44; sort ) < 44) ) ) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 45; sort ) < 45) \
                                        <((rm 46; sort ) < 46) ) \
                                 <(sort -m  \
                                        <((rm 47; sort ) < 47) \
                                        <((rm 48; sort ) < 48) ) ) ) ) \
            <(sort -m  \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 49; sort ) < 49) \
                                        <((rm 50; sort ) < 50) ) \
                                 <(sort -m  \
                                        <((rm 51; sort ) < 51) \
                                        <((rm 52; sort ) < 52) ) ) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 53; sort ) < 53) \
                                        <((rm 54; sort ) < 54) ) \
                                 <(sort -m  \
                                        <((rm 55; sort ) < 55) \
                                        <((rm 56; sort ) < 56) ) ) ) \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 57; sort ) < 57) \
                                        <((rm 58; sort ) < 58) ) \
                                 <(sort -m  \
                                        <((rm 59; sort ) < 59) \
                                        <((rm 60; sort ) < 60) ) ) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 61; sort ) < 61) \
                                        <((rm 62; sort ) < 62) ) \
                                 <(sort -m  \
                                        <((rm 63; sort ) < 63) \
                                        <((rm 64; sort ) < 64) ) ) ) ) ) |
    md5sum

withcat:

#!/bin/bash

export LC_ALL=C
sort -m \
     <(sort -m  \
            <(sort -m  \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 1; sort ) < 1) \
                                        <((rm 2; sort ) < 2) | cat) \
                                 <(sort -m  \
                                        <((rm 3; sort ) < 3) \
                                        <((rm 4; sort ) < 4) | cat) | cat) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 5; sort ) < 5) \
                                        <((rm 6; sort ) < 6) | cat) \
                                 <(sort -m  \
                                        <((rm 7; sort ) < 7) \
                                        <((rm 8; sort ) < 8) | cat) | cat) | cat) \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 9; sort ) < 9) \
                                        <((rm 10; sort ) < 10) | cat) \
                                 <(sort -m  \
                                        <((rm 11; sort ) < 11) \
                                        <((rm 12; sort ) < 12) | cat) | cat) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 13; sort ) < 13) \
                                        <((rm 14; sort ) < 14) | cat) \
                                 <(sort -m  \
                                        <((rm 15; sort ) < 15) \
                                        <((rm 16; sort ) < 16) | cat) | cat) | cat) | cat) \
            <(sort -m  \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 17; sort ) < 17) \
                                        <((rm 18; sort ) < 18) | cat) \
                                 <(sort -m  \
                                        <((rm 19; sort ) < 19) \
                                        <((rm 20; sort ) < 20) | cat) | cat) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 21; sort ) < 21) \
                                        <((rm 22; sort ) < 22) | cat) \
                                 <(sort -m  \
                                        <((rm 23; sort ) < 23) \
                                        <((rm 24; sort ) < 24) | cat) | cat) | cat) \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 25; sort ) < 25) \
                                        <((rm 26; sort ) < 26) | cat) \
                                 <(sort -m  \
                                        <((rm 27; sort ) < 27) \
                                        <((rm 28; sort ) < 28) | cat) | cat) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 29; sort ) < 29) \
                                        <((rm 30; sort ) < 30) | cat) \
                                 <(sort -m  \
                                        <((rm 31; sort ) < 31) \
                                        <((rm 32; sort ) < 32) | cat) | cat) | cat) | cat) | cat) \
     <(sort -m  \
            <(sort -m  \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 33; sort ) < 33) \
                                        <((rm 34; sort ) < 34) | cat) \
                                 <(sort -m  \
                                        <((rm 35; sort ) < 35) \
                                        <((rm 36; sort ) < 36) | cat) | cat) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 37; sort ) < 37) \
                                        <((rm 38; sort ) < 38) | cat) \
                                 <(sort -m  \
                                        <((rm 39; sort ) < 39) \
                                        <((rm 40; sort ) < 40) | cat) | cat) | cat) \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 41; sort ) < 41) \
                                        <((rm 42; sort ) < 42) | cat) \
                                 <(sort -m  \
                                        <((rm 43; sort ) < 43) \
                                        <((rm 44; sort ) < 44) | cat) | cat) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 45; sort ) < 45) \
                                        <((rm 46; sort ) < 46) | cat) \
                                 <(sort -m  \
                                        <((rm 47; sort ) < 47) \
                                        <((rm 48; sort ) < 48) | cat) | cat) | cat) | cat) \
            <(sort -m  \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 49; sort ) < 49) \
                                        <((rm 50; sort ) < 50) | cat) \
                                 <(sort -m  \
                                        <((rm 51; sort ) < 51) \
                                        <((rm 52; sort ) < 52) | cat) | cat) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 53; sort ) < 53) \
                                        <((rm 54; sort ) < 54) | cat) \
                                 <(sort -m  \
                                        <((rm 55; sort ) < 55) \
                                        <((rm 56; sort ) < 56) | cat) | cat) | cat) \
                   <(sort -m  \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 57; sort ) < 57) \
                                        <((rm 58; sort ) < 58) | cat) \
                                 <(sort -m  \
                                        <((rm 59; sort ) < 59) \
                                        <((rm 60; sort ) < 60) | cat) | cat) \
                          <(sort -m  \
                                 <(sort -m  \
                                        <((rm 61; sort ) < 61) \
                                        <((rm 62; sort ) < 62) | cat) \
                                 <(sort -m  \
                                        <((rm 63; sort ) < 63) \
                                        <((rm 64; sort ) < 64) | cat) | cat) | cat) | cat) | cat) | cat |
    md5sum

唯一的区别是 in withcateverysort -m被通过管道传输到cat.

“猫的无用用途”——人们会让你相信withcat会比 慢nocat。然而,事实恰恰相反:

$ time bash nocat
c933d81faea7b8dec8eb64ca0b044d74  -

real    3m40.854s
user    2m48.687s
sys     0m49.135s
$ time bash withcat
c933d81faea7b8dec8eb64ca0b044d74  -

real    2m21.812s
user    2m16.651s
sys     1m36.135s

该测试在 64 核机器上运行,不执行任何其他操作。一切都在 RAM 中(所以这不是由于磁盘速度慢造成的)。每个测试运行3次,上面给出了最佳时间。所有三项测试均在最佳时间的 5 秒内完成(因此这并非侥幸)。

为什么通过管道将输出传输到 会更快cat

编辑

小组输入是否cat会更大块?和/或是否sort刷新每行的输出?

为了测试这个我尝试过:

$ strace -ff sort -m <(sort 1) <(sort 2) 2>fromsort | cat >/dev/null
$ strace -ff sort -m <(sort 1 | cat ) <(sort 2 | cat) 2>fromcat | cat >/dev/null

如果cat将其分成更大的块,我们期望read返回更大的块。但它没有:

$ grep -E 'read|write' fromsort |field 1,5|sort | uniq -c 
      1 openat(AT_FDCWD,        3
      8 pread64(3,      =
      1 read(3, 3771
  40989 read(3, 4096
      2 read(3, 832
      1 read(3, unknown
      1 read(4, 0
      1 read(4, 2241
  40959 read(4, 4096
      1 write(1,        1916
  81949 write(1,        4096
$ grep -E 'read|write' fromcat |field 1,5|sort | uniq -c 
      1 openat(AT_FDCWD,        3
      8 pread64(3,      =
      1 read(3, 3771
  40989 read(3, 4096
      2 read(3, 832
      1 read(3, unknown
      1 read(4, 2241
  40959 read(4, 4096
      1 read(4, unknown
      1 write(1,        1916
  81949 write(1,        4096

在这两种情况下,readwrite都是 4K。

(顺便,sort 如果从文件而不是从管道读取,则读取(更多)更大的块,但这里不是这种情况)。

编辑2

上述的目的是为了证明附加cat并不总是无用的;并找出造成这种情况的原因。

目标不是对数据进行排序。

但如果你的目标曾是要对数据进行排序,为什么不直接使用sort内置的--parallel

默认情况下sort似乎--parallel 8在 64 核机器上使用。top显示它使用了高达 800% 的 CPU。您可以强制它使用 64 核--parallel 64

$ time sort {1..64} | md5sum
real    9m4.005s
user    29m56.454s
sys     5m49.560s

$ time sort --parallel 64 {1..64} | md5sum
real    6m50.332s
user    35m55.040s
sys     11m37.609s

所以 GNU 排序--parallel比上面慢得多。以上内容现已提供parsorthttp://git.savannah.gnu.org/cgit/parallel.git/tree/src/parsort

答案1

这绝不是“猫的无用之举”。

some_command | cat | some_command

这不是传统意义上的“猫的无用之用”,通常是由于对外壳的无知而产生的。相反,这似乎是有意尝试利用猫的动态来做某事。在这种情况下,我相信它是缓存。


我的第二个想法

即使读取和写入的大小没有任何不同,也有一些可能无法检测到的事情也可能在起作用。

首先(这非常重要):为什么处理已排序的数组比处理未排序的数组更快?。如果您执行任何更改 CPU 处理此操作的顺序,则时间可能会改变。如果cat成功地使每次sort运行时间更长而不挂起(并切换到不同的进程),那么这可能会极大地影响 CPU 的分支预测并导致更长或更短的时间。

其次,即使读取的数量和大小不受影响,任务必须挂起(阻止)的次数也可能不同。这本身可能会增加或减少开销。因此,即使读取和写入的大小相同, (缓存)层也可能会减少每次读取和写入cat的次数。read()write()

Cat 可能只是强制排序等待更长时间,从而有更多可用的工作,而无需挂起并减少每个进程阻塞的次数。这将很难被发现。


我的第一个想法

我的期望是,如果您将两个版本放入各自的脚本中并strace -f在每个脚本上运行,您将在带有 cat 的示例中看到更少的读或/写调用。至少,我希望使用cat.我的期望sort是它会写入单行并且不会在内部缓冲太多。事实上,我希望它能read()在足够大的块中,但只能write()在单行中。这意味着它的设计不适合自身的管道传输。

正如 laktak 指出的那样他的回答, cat 以 128KB 的块读取(看这里)但管道通常只缓冲 64KB。如果我是对的,那么当 cat 挂起等待 aread()完成时,这将为写入sort操作提供一个大的(128 + 64 KB)缓冲区,无需挂起即可写入。到恢复时,cat将有大量数据(比sort单次写入发送的数据多得多)传递到下一个sort。因此,下一个sort可以从中读取很多内容而不会被暂停。

我也怀疑添加最接近的文件层cat对性能几乎没有影响或负面影响。这些文件已经缓存在您的 RAM 磁盘上。但是调用之间的层sort将充当缓冲区,并且应该减少数量。这才是真正的“cat 无用用途”,即那些使用 cat 读取文件的情况。这就是以下形式:

cat some_file | some_command

一个有趣的实验

我很想知道是否可以通过增加管道上的缓冲区大小来产生相同的效果。如果您使用正确的编程语言(而不是 shell)设置相同的管道。例如,在 C 中,您可以使用pipe()dup2()fork()和首先exec()调用每个管道来创建管道ioctl()以提高缓冲区大小(请参阅管道容量

答案2

我的猜测是,您对 cat 的使用会限制每个命令的吞吐量,从而使它们并行运行得更快。

cat读取你的数据128KB 的块。由于我无法重现您的测试,您能否尝试替换您的用法catdd证明我是对还是错?

dd status=none bs=128K应该具有相同的效果cat- 尝试增加/减少块大小并比较结果。

相关内容