为什么使用pipe道进行sorting(Linux命令)速度慢?

我有一个大的〜8GB的文本文件,我需要做一些简单的过滤,然后sorting所有的行。 我在一台配有SSD和128GB RAM的28核心机器上。 我努力了

方法1

awk '...' myBigFile | sort --parallel = 56 > myBigFile.sorted 

方法2

 awk '...' myBigFile > myBigFile.tmp sort --parallel 56 myBigFile.tmp > myBigFile.sorted 

令人惊讶的是,方法1需要11.5分钟,而方法2只需要(0.75 + 1 <2)分钟。 为什么sorting如此缓慢pipe道? 这不是平行吗?

编辑

awkmyBigFile并不重要,这个实验可以简单地使用seq 1 10000000 | sort --parallel 56来重复 seq 1 10000000 | sort --parallel 56 (感谢@Sergei Kurenkov),我还观察到在我的机器上使用无pipe道版本的速度提高了六倍。

从管道读取时, sort假定文件很小,对于小文件并行则没有帮助。 要进行sort以利用并行性,您需要告诉它使用-S分配一个较大的主内存缓冲区。 在这种情况下,数据文件大约是8GB,所以你可以使用-S8G 。 但是,至少在你的系统上有128GB的主内存,方法2可能仍然会更快。

这是因为在方法2中的sort可以从文件的大小知道它是巨大的,并且它可以在文件中寻找(这对管道来说都是不可能的)。 此外,由于与这些文件大小相比,你拥有的内存非常多,所以在awk退出之前myBigFile.tmp的数据不需要写入光盘, sort就可以从缓存而不是光盘读取文件。 所以方法1和方法2(在像你有很多内存的机器上)之间的主要区别在于,方法2中的sort知道文件是巨大的,可以很容易地划分工作(可能使用seek,但我没有看在实现中),而在方法1中, sort必须发现数据是巨大的,并且在读取输入时不能使用任何并行性,因为它不能查找管道。

我认为从管道读取时排序不使用线程。

  1. 我已经使用这个命令为你的第一个案件。 而且它显示,即使被告知使用4, sort只使用1个atop实际上也表明只有一个线程在sort

     /usr/bin/time -v bash -c "seq 1 1000000 | sort --parallel 4 > bf.txt" 
  2. 我已经使用这个命令你的第二个案例。 这表明排序使用2个CPU。 atop还显示有四个线程在sort

     /usr/bin/time -v bash -c "seq 1 1000000 > tmp.bf.txt && sort --parallel 4 tmp.bf.txt > bf.txt" 

在你的第一个场景中,sort是一个I / O绑定的任务,它会从stdin中执行大量的read系统调用。 在第二种情况下,排序使用mmap syscalls来读取文件,避免成为I / O绑定的任务。

以下是第一和第二种情况的结果:

 $ /usr/bin/time -v bash -c "seq 1 10000000 | sort --parallel 4 > bf.txt" Command being timed: "bash -c seq 1 10000000 | sort --parallel 4 > bf.txt" User time (seconds): 35.85 System time (seconds): 0.84 Percent of CPU this job got: 98% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:37.43 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 9320 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 0 Minor (reclaiming a frame) page faults: 2899 Voluntary context switches: 1920 Involuntary context switches: 1323 Swaps: 0 File system inputs: 0 File system outputs: 459136 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0 $ /usr/bin/time -v bash -c "seq 1 10000000 > tmp.bf.txt && sort --parallel 4 tmp.bf.txt > bf.txt" Command being timed: "bash -c seq 1 10000000 > tmp.bf.txt && sort --parallel 4 tmp.bf.txt > bf.txt" User time (seconds): 43.03 System time (seconds): 0.85 Percent of CPU this job got: 175% Elapsed (wall clock) time (h:mm:ss or m:ss): 0:24.97 Average shared text size (kbytes): 0 Average unshared data size (kbytes): 0 Average stack size (kbytes): 0 Average total size (kbytes): 0 Maximum resident set size (kbytes): 1018004 Average resident set size (kbytes): 0 Major (requiring I/O) page faults: 0 Minor (reclaiming a frame) page faults: 2445 Voluntary context switches: 299 Involuntary context switches: 4387 Swaps: 0 File system inputs: 0 File system outputs: 308160 Socket messages sent: 0 Socket messages received: 0 Signals delivered: 0 Page size (bytes): 4096 Exit status: 0 

你有更多的系统调用,如果你使用管道。

 seq 1000000 | strace sort --parallel=56 2>&1 >/dev/null | grep read | wc -l 2059 

没有管道的文件被映射到内存。

 seq 1000000 > input strace sort --parallel=56 input 2>&1 >/dev/null | grep read | wc -l 33 

内核调用在大多数情况下是瓶颈。 这就是sendfile发明的原因。

AFIAK恕我直言,在你的第一个办法首先它将做awk操作,然后它进行转发该操作到下一个排序命令,所以这将发生在单一镜头和awk的输出将作为排序的输入在这里所以绝对要存储的值(你的Input_file值)更多的内存将被采取,因为没有其他文件正在创建用于存储的值(awk的这些值将存储在内存中,awk操作和排序将只从那里输入)。 另一方面,在第二个命令中,正在创建一个正在被排序命令使用的新文件,因此内存不需要保存整个8 GB左右的文件,因此占用的内存会更少。