为什么读取文件比读取变量更快?

为什么读取文件比读取变量更快?

我不明白使用两个基本脚本(在高端服务器上运行)运行的简单性能测试的结果:

性能变量

#!/bin/zsh -f

MYVAR=`cat $1`
for i in {1..10}
do
  echo $MYVAR
done

perfCat.zsh

#!/bin/zsh -f

for i in {1..10}
do
cat $1
done

性能测试结果:

> time ./perfVar.zsh BigTextFile > /dev/null
./perfVar.zsh FE > /dev/null  6.86s user 0.32s system 100% cpu 7.177 total
> time ./perfCat.zsh BigTextFile > /dev/null
./perfCat.zsh FE > /dev/null  0.01s user 0.10s system 91% cpu 0.118 total

我原本以为访问 VARIABLE 比读取文件系统上的 FILE 要快得多...为什么会出现这种结果?有没有办法通过减少对文件系统的访问次数来优化 perfCat.zsh 脚本?

答案1

我能够在 Bash 中重现相同的行为。这里的主要问题是您使用 shell 变量的方式不符合它们的设计要求;因此没有对其进行优化。当您执行“echo $HUGEVAR”时,shell 必须构建一个包含 $HUGEVAR 全部内容的命令行(即使“echo”是内置命令,仍然有一个命令行)。

因此,shell 将 HUGEVAR 扩展为一个大字符串,然后再次对其进行解析,以根据空格将其拆分为 echo 命令的单个参数列表。(请注意,这将导致将输入文件中的连续空格字符折叠为单个空格字符)。显然,对于大字符串,此过程效率不高。

您应该多次使用“cat bigfile”的方法;并允许操作系统的文件系统缓存完成其工作,并加快对大文件的重复访问;您可以避免使用 echo 时 shell 对字符串进行的细微(可能是不必要的)修改(此外,“cat”方法将适用于二进制文件,而 shell 方法可能会破坏二进制数据)。

答案2

在 bash 和 csh 中,变量选择...

#!/usr/bin/env bash
MYVAR=`cat $1`

#!/usr/bin/env tcsh
set myvar=`cat $1`

... 将导致它执行cat命令以及可能发生的任何文本解释。例如,如果环境变量 LANG 设置为 UTF8,或者它将换行符转换为空格。最后,它需要分配空间来存储结果cat

相比之下,脚本 #2 只是 cat 文件并完成了操作。事实上,由于它是写入/dev/null,因此这也可能提高性能。

尝试写入文件而不是/dev/null重新计时。它几乎肯定会更快,但计时可能会更加一致。

最后,让它只计时循环,而不是计时整个脚本。如果你想要计时的是从变量读取,而不是从文件读取,那么你就没有正确地计时。

编辑

time为了计时,我建议不要使用以下命令:

#!/usr/bin/env bash

# do some stuff
date --rfc-3339=ns
for (( i = 0; i < 10; i++ )); do
  # Some more stuff
done;
date --rfc-3339=ns

这将输出精确到纳秒的当前日期和时间。

答案3

变量赋值(与大多数其他脚本内置函数相比)是一项昂贵的操作。您看到如此巨大的性能差异的原因在于您处理的数据的大小。从表面上看,您似乎只将数据分配给单个变量(MYVAR)一次,但实际上 zsh 会在每次 echo 调用时将数据分配给临时位置(映射和取消映射内存)。通常这不是问题,但在处理大量数据时,它会变得明显。

cat 循环更优越的原因有两个:数据大小和文件系统缓存。

相关内容