循环性能与扩展性能


9

需要以下比较的专家建议:

使用循环的代码段:

for file in `cat large_file_list`
do
    gzip -d $file
done

使用简单扩展的代码段:

gzip -d `cat large_file_list`

哪一个会更快?必须操纵大数据集。


1
正确的答案取决于在gzip系统上启动需要多长时间,文件列表中的文件数量以及这些文件的大小。
库萨兰达

文件列表将包含大约1000-10000个文件。大小从几千字节到500 MB不等。我不知道在系统中启动gzip需要多长时间。任何方式检查?
里昂,

1
好的,那也可能取决于filenames长度。如果文件名很长,那么如果您尝试不带循环执行某些文件,则某些系统可能会生成“参数列表太长”错误,因为命令替换会导致命令行执行的命令行太长。如果您不想依赖列表中的文件数,请使用循环。与将要对它们执行的其他处理相比,您是否花费大量时间对这些文件进行解压缩?
库萨兰达

Leon看一下我的测试结果:在我的设置中,“ huge-arglist”比“ loop”快20倍。

要在进程启动和命令行长度之间找到一个满意的中间值,请使用类似的方法,xargs gzip -d < large_file_list但要注意文件名中的空格,可能使用tr \\n \\0 large_file_list | xargs -0 gzip -d
w00t

Answers:


19

并发症

以下内容仅在某些情况下有效:

gzip -d `cat large_file_list`

存在三个问题(bash以及大多数其他类似Bourne的外壳):

  1. 如果任何文件名中包含空格标签或换行符,它将失败(假设$IFS尚未修改)。这是因为shell的单词spliting

  2. 如果任何文件名中包含全球通用字符,也很容易失败。这是因为Shell会将路径名扩展应用于文件列表。

  3. 如果文件名以-POSIXLY_CORRECT=1以第一个文件开头)或任何文件名为,则文件名也将失败-

  4. 如果文件名太多而无法容纳在一个命令行中,它将也失败。

下面的代码与上面的代码存在相同的问题(第四个除外)

for file in `cat large_file_list`
do
    gzip -d $file
done

可靠的解决方案

如果您large_file_list每行只有一个文件名,而其中-没有一个名为的文件,并且您使用的是GNU系统,请使用:

xargs -rd'\n' gzip -d -- <large_file_list

-d'\n'告诉xargs将输入的每一行视为一个单独的文件名。

-r告诉xargs如果输入文件为空,则不运行命令。

--告诉gzip以下参数即使以开头也不应被视为选项--单独仍然会被视为-而不是被调用的文件-

xargs会在每个命令行上放置许多文件名,但不会超过命令行限制。这减少了gzip必须启动过程的次数,因此可以加快速度。这也是安全的:文件名也将受到保护,免受分路径名扩展的影响


感谢您的详细答复。我们了解您提到的3个问题。文件名很简单,不会遇到这些挑战,因为列表最多可以容纳20000个。我的问题基本上是这两个部分的性能。谢谢。
里昂,

1
@Leon for循环到目前为止是最慢的。其他两种方法彼此之间的速度非常接近。
John1024 '19

7
另外,不要忽略潜在的问题:StackExchange上的许多问题都是因为单词拆分路径名扩展发生在不期望的人身上。
John1024 '19

5
另请注意,使用读取文件存在差异xargs:至少GNU版本具有--arg-file选项(简称-a)。所以可以xargs -a large_file_list -rd'\n' gzip -d 代替。实际上,除了<是shell运算符并且可以xargs从stdin 进行读取(该shell“链接”到文件),而-a可以xargs显式打开有问题的文件之外,没有任何区别
Sergiy Kolodyazhnyy

2
terdon在另一条评论中指出,该文件parallel用于运行的多个副本gzip,但是xargs(至少是GNU的),也有相应的-P开关。在多核计算机上,这可能会有所作为。但是反压缩也可能完全受I / O约束。
ilkkachu

12

我怀疑这会很重要。

我会使用一个循环,只是因为我不知道列表文件中列出了多少文件,而且我(通常)不知道任何文件名的名称中是否有空格。如果生成的列表的长度过长,那么执行将生成非常长的参数列表的命令替换可能会导致“参数列表过长”错误。

我的循环看起来像

while IFS= read -r name; do
    gunzip "$name"
done <file.list

另外,这将允许我在命令之后插入用于处理数据的gunzip命令。实际上,根据实际数据是什么以及需要使用什么数据,甚至有可能根本不用保存到文件就可以处理它:

while IFS= read -r name; do
    zcat "$name" | process_data
done <file.list

(哪里process_data有一些管道从标准输入中读取未压缩的数据)

如果数据处理比解压缩花费的时间更长,那么循环是否更有效的问题就变得无关紧要了。

理想情况下,我希望不要处理文件名列表,而是使用文件名遍历模式,例如

for name in ./*.gz; do
    # processing of "$name" here
done

./*.gz与相关文件匹配的某种模式在哪里。这样,我们既不依赖文件数量也不依赖文件名中使用的字符(它们可能包含换行符或其他空格字符,或者以破折号等开头)。

有关:


5

在这两个文件中,将所有文件传递给单个调用的文件gzip可能会更快,这完全是因为您只需要启动gzip一次。(也就是说,如果命令完全起作用,请参阅其他说明以获取警告。)

但是,我想提醒一下优化黄金法则不要过早地做到这一点。

  1. 在知道这是一个问题之前,不要优化这种事情。

    程序的这部分需要很长时间吗?好吧,解压缩大文件可能会,而且无论如何您都必须这样做,所以回答起来可能并不容易。

  2. 测量。确实,这是确定的最佳方法。

    您将用自己的眼睛(或秒表)查看结果,它们将适用于您的情况,Internet上的随机答案可能不会。将两个变体都放入脚本中并运行time script1.shtime script2.sh。(使用空的压缩文件列表来执行此操作以测量开销的绝对值。)


0

您的磁盘有多快?

这应该使用您所有的CPU:

parallel -X gzip -d :::: large_file_list

因此,您的限制可能取决于磁盘的速度。

您可以尝试使用进行调整-j

parallel -j50% -X gzip -d :::: large_file_list

这将与上一个命令并行运行一半的作业,并且将减轻磁盘负担,因此根据磁盘的不同,它可能会更快。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.