循环与扩展的性能

循环与扩展的性能

需要专家对以下比较提出建议:

使用循环的代码段:

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

使用简单扩展的代码段:

gzip -d `cat large_file_list`

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

答案1

并发症

以下仅有时有效:

gzip -d `cat large_file_list`

三个问题是(在bash大多数其他类似 Bourne 的 shell 中):

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

  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,因此速度更快。它也是安全的:文件名也将受到保护分词路径名扩展

答案2

我怀疑这会很重要。

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

我的循环看起来像

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有一些与相关文件匹配的模式。这样我们就不会依赖于文件的数量,也不依赖于文件名中使用的字符(它们可能包含换行符或其他空白字符,或者以破折号开头等)

有关的:

答案3

在这两者中,将所有文件传递给一次调用的那个gzip可能会更快,正是因为您只需要启动gzip一次。 (也就是说,如果该命令完全有效,请参阅其他答案以了解注意事项。)

但是,我想提醒大家的是优化的黄金法则:不要过早这样做。

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

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

  2. 措施。确实,这是最好的确定方法。

    您将亲眼看到结果(或使用您自己的秒表),并且它们将适用于你的情况互联网上的随机答案可能不会。将这两个变体放入脚本中并运行time script1.sh, and time script2.sh。 (使用空压缩文件列表来测量开销的绝对量。)

答案4

你的磁盘速度有多快?

这应该使用您的所有 CPU:

parallel -X gzip -d :::: large_file_list

因此,您的限制可能是磁盘的速度。

您可以尝试调整-j

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

这将像上一个命令一样并行运行一半的作业,并且会减少对磁盘的压力,因此根据您的磁盘,这可能会更快。

相关内容