AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • 主页
  • 系统&网络
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • 主页
  • 系统&网络
    • 最新
    • 热门
    • 标签
  • Ubuntu
    • 最新
    • 热门
    • 标签
  • Unix
    • 最新
    • 标签
  • DBA
    • 最新
    • 标签
  • Computer
    • 最新
    • 标签
  • Coding
    • 最新
    • 标签
主页 / unix / 问题 / 501828
Accepted
dessert
dessert
Asked: 2019-02-21 03:32:16 +0800 CST2019-02-21 03:32:16 +0800 CST 2019-02-21 03:32:16 +0800 CST

为什么打开文件比读取可变内容更快?

  • 772

在bash脚本中,我需要/proc/文件中的各种值。到目前为止,我有几十行直接像这样对文件进行 grepping:

grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo

为了提高效率,我将文件内容保存在一个变量中并grepped:

a=$(</proc/meminfo)
echo "$a" | grep -oP '^MemFree: *\K[0-9]+'

而不是多次打开文件,这应该只打开一次并 grep 变量内容,我认为这会更快 - 但实际上它更慢:

bash 4.4.19 $ time for i in {1..1000};do grep ^MemFree /proc/meminfo;done >/dev/null
real    0m0.803s
user    0m0.619s
sys     0m0.232s
bash 4.4.19 $ a=$(</proc/meminfo)
bash 4.4.19 $ time for i in {1..1000};do echo "$a"|grep ^MemFree; done >/dev/null
real    0m1.182s
user    0m1.425s
sys     0m0.506s

dash和也是如此zsh。我怀疑/proc/文件的特殊状态是一个原因,但是当我将内容复制/proc/meminfo到常规文件并使用时,结果是相同的:

bash 4.4.19 $ cat </proc/meminfo >meminfo
bash 4.4.19 $ time for i in $(seq 1 1000);do grep ^MemFree meminfo; done >/dev/null
real    0m0.790s
user    0m0.608s
sys     0m0.227s

使用 here 字符串来保存管道会稍微快一些,但仍然不如文件快:

bash 4.4.19 $ time for i in $(seq 1 1000);do <<<"$a" grep ^MemFree; done >/dev/null
real    0m0.977s
user    0m0.758s
sys     0m0.268s

为什么打开文件比从变量中读取相同内容更快?

bash shell-script
  • 3 3 个回答
  • 5062 Views

3 个回答

  • Voted
  1. Best Answer
    Stéphane Chazelas
    2019-02-21T03:59:54+08:002019-02-21T03:59:54+08:00

    在这里,这不是关于打开文件与读取变量的内容,而是更多关于是否分叉额外的进程。

    grep -oP '^MemFree: *\K[0-9]+' /proc/meminfo派生一个执行grep打开的进程/proc/meminfo(一个虚拟文件,在内存中,不涉及磁盘 I/O)读取它并匹配正则表达式。

    其中最昂贵的部分是分叉进程并加载 grep 实用程序及其库依赖项,进行动态链接,打开语言环境数据库,磁盘上的数十个文件(但可能缓存在内存中)。

    相比之下,关于读取的部分/proc/meminfo微不足道,内核需要很少的时间来生成其中的信息,并且grep需要很少的时间来读取它。

    如果你在上面运行strace -c,你会看到用于读取的一个open()和一个系统调用与启动其他所有操作相比是花生(不计算分叉)。read()/proc/meminfogrepstrace -c

    在:

    a=$(</proc/meminfo)
    

    在大多数支持该$(<...)ksh 运算符的 shell 中,shell 只是打开文件并读取其内容(并去除尾随的换行符)。bash不同并且效率低得多,因为它分叉一个进程来进行读取并通过管道将数据传递给父进程。但是在这里,它已经完成了一次,所以没关系。

    在:

    printf '%s\n' "$a" | grep '^MemFree'
    

    shell 需要生成两个进程,它们同时运行,但通过管道相互交互。管道的创建、拆除以及从中写入和读取的成本很少。更大的成本是产生额外的进程。进程的调度也有一些影响。

    您可能会发现使用 zsh<<<运算符会稍微快一些:

    grep '^MemFree' <<< "$a"
    

    在 zsh 和 bash 中,这是通过将 的内容写入$a临时文件来完成的,这比产生额外进程的成本更低,但与直接获取数据相比可能不会给您带来任何收益/proc/meminfo。这仍然比/proc/meminfo在磁盘上复制的方法效率低,因为临时文件的写入是在每次迭代时完成的。

    dash不支持 here-strings,但它的 heredocs 是使用不涉及产生额外进程的管道实现的。在:

     grep '^MemFree' << EOF
     $a
     EOF
    

    外壳创建一个管道,分叉一个进程。子进程grep以其标准输入作为管道的读取端执行,父进程将内容写入管道的另一端。

    但是,管道处理和进程同步仍然可能比直接获取数据更昂贵/proc/meminfo。

    内容/proc/meminfo很短,制作时间也不长。如果你想节省一些 CPU 周期,你想删除昂贵的部分:分叉进程和运行外部命令。

    喜欢:

    IFS= read -rd '' meminfo < /proc/meminfo
    memfree=${meminfo#*MemFree:}
    memfree=${memfree%%$'\n'*}
    memfree=${memfree#"${memfree%%[! ]*}"}
    

    避免bash其模式匹配非常低效。使用zsh -o extendedglob,您可以将其缩短为:

    memfree=${${"$(</proc/meminfo)"##*MemFree: #}%%$'\n'*}
    

    请注意,这^在许多 shell 中是特殊的(Bourne、fish、rc、es 和 zsh,至少带有扩展的 glob 选项),我建议引用它。另请注意,echo不能用于输出任意数据(因此我在printf上面使用)。

    • 48
  2. Prvt_Yadav
    2019-02-21T04:03:44+08:002019-02-21T04:03:44+08:00

    在您的第一种情况下,您只是使用 grep 实用程序并从 file 中查找某些内容/proc/meminfo,这/proc是一个虚拟文件系统,因此/proc/meminfofile 位于内存中,并且只需要很少的时间即可获取其内容。

    但是在第二种情况下,您正在创建一个管道,然后使用该管道将第一个命令的输出传递给第二个命令,这很昂贵。

    区别在于/proc(因为它在内存中)和管道,请参见下面的示例:

    time for i in {1..1000};do grep ^MemFree /proc/meminfo;done >/dev/null
    
    real    0m0.914s
    user    0m0.032s
    sys     0m0.148s
    
    
    cat /proc/meminfo > file
    time for i in {1..1000};do grep ^MemFree file;done >/dev/null
    
    real    0m0.938s
    user    0m0.032s
    sys     0m0.152s
    
    
    time for i in {1..1000};do echo "$a"|grep ^MemFree; done >/dev/null
    
    real    0m1.016s
    user    0m0.040s
    sys     0m0.232s
    
    • 6
  3. user232326
    2019-02-23T20:58:02+08:002019-02-23T20:58:02+08:00

    在这两种情况下,您都在调用外部命令(grep)。外部调用需要一个子shell。分叉该外壳是延迟的根本原因。这两种情况是相似的,因此:相似的延迟。

    如果您只想读取外部文件一次并多次使用它(从变量中),请不要离开外壳:

    meminfo=$(< /dev/meminfo)    
    time for i in {1..1000};do 
        [[ $meminfo =~ MemFree:\ *([0-9]*)\ *.B ]] 
        printf '%s\n' "${BASH_REMATCH[1]}"
    done
    

    grep 调用只需要大约 0.1 秒而不是 1 秒。

    • 1

相关问题

  • 从文本文件传递变量的奇怪问题

  • 虽然行读取保持转义空间?

  • MySQL Select with function IN () with bash array

  • `tee` 和 `bash` 进程替换顺序

  • 运行一个非常慢的脚本直到它成功

Sidebar

Stats

  • 问题 205573
  • 回答 270741
  • 最佳答案 135370
  • 用户 68524
  • 热门
  • 回答
  • Marko Smith

    模块 i915 可能缺少固件 /lib/firmware/i915/*

    • 3 个回答
  • Marko Smith

    无法获取 jessie backports 存储库

    • 4 个回答
  • Marko Smith

    如何将 GPG 私钥和公钥导出到文件

    • 4 个回答
  • Marko Smith

    我们如何运行存储在变量中的命令?

    • 5 个回答
  • Marko Smith

    如何配置 systemd-resolved 和 systemd-networkd 以使用本地 DNS 服务器来解析本地域和远程 DNS 服务器来解析远程域?

    • 3 个回答
  • Marko Smith

    dist-upgrade 后 Kali Linux 中的 apt-get update 错误 [重复]

    • 2 个回答
  • Marko Smith

    如何从 systemctl 服务日志中查看最新的 x 行

    • 5 个回答
  • Marko Smith

    Nano - 跳转到文件末尾

    • 8 个回答
  • Marko Smith

    grub 错误:你需要先加载内核

    • 4 个回答
  • Marko Smith

    如何下载软件包而不是使用 apt-get 命令安装它?

    • 7 个回答
  • Martin Hope
    user12345 无法获取 jessie backports 存储库 2019-03-27 04:39:28 +0800 CST
  • Martin Hope
    Carl 为什么大多数 systemd 示例都包含 WantedBy=multi-user.target? 2019-03-15 11:49:25 +0800 CST
  • Martin Hope
    rocky 如何将 GPG 私钥和公钥导出到文件 2018-11-16 05:36:15 +0800 CST
  • Martin Hope
    Evan Carroll systemctl 状态显示:“状态:降级” 2018-06-03 18:48:17 +0800 CST
  • Martin Hope
    Tim 我们如何运行存储在变量中的命令? 2018-05-21 04:46:29 +0800 CST
  • Martin Hope
    Ankur S 为什么 /dev/null 是一个文件?为什么它的功能不作为一个简单的程序来实现? 2018-04-17 07:28:04 +0800 CST
  • Martin Hope
    user3191334 如何从 systemctl 服务日志中查看最新的 x 行 2018-02-07 00:14:16 +0800 CST
  • Martin Hope
    Marko Pacak Nano - 跳转到文件末尾 2018-02-01 01:53:03 +0800 CST
  • Martin Hope
    Kidburla 为什么真假这么大? 2018-01-26 12:14:47 +0800 CST
  • Martin Hope
    Christos Baziotis 在一个巨大的(70GB)、一行、文本文件中替换字符串 2017-12-30 06:58:33 +0800 CST

热门标签

linux bash debian shell-script text-processing ubuntu centos shell awk ssh

Explore

  • 主页
  • 问题
    • 最新
    • 热门
  • 标签
  • 帮助

Footer

AskOverflow.Dev

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve