在 AIX/bash 上的 bash 循环中读取文件比在 Linux/ksh 中慢得多 - BLOCKSIZE?

Posted

技术标签:

【中文标题】在 AIX/bash 上的 bash 循环中读取文件比在 Linux/ksh 中慢得多 - BLOCKSIZE?【英文标题】:Reading file in bash loop on AIX/bash is much slower than in Linux/ksh - BLOCKSIZE? 【发布时间】:2018-04-25 11:15:53 【问题描述】:

我们有一个在 RHEL Linux 中开发的自定义脚本(在 ksh 中)。 功能是 1) 读取输入的 ASCII 文件 2) 使用 sed -i inplace files 将 "\" 替换为 "\\" 3) 将历史文件加载到内存中 4) 将数据与当天进行比较 5) 生成净变化记录

在平台升级期间,我们必须在 AIX 7.1 和 用 bash 替换了 ksh,因为 typeset -A 在 ksh AIX 上不可用,而 sed -i 命令用 perl -pi -e 和脚本的其余部分几乎相同。

我们观察到脚本在 Linux 中处理 1 小时(691 个文件),但在 AIX 中则需要 7 多个小时。

我们观察到对于一个输入文件,下面的 sn-p 存在性能差异,Linux 代码在 1-2 秒内完成,而在 AIX 中需要 13-15 秒。由于每个文件的性能差异,对于 691 个文件,脚本需要 7 小时才能完成。

能否请您帮助我了解我们是否可以调整此脚本以在 AIX 上获得更好的性能。任何指针都会非常有帮助。 提前感谢您的帮助!

在下面添加测试结果以获得更精确的问题

Linux 测试脚本:

#!/bin/sh
export LANG="C"
echo `date`
typeset -A Archive_Lines
if [ -f "8249cii1.ASC" ]
then
echo `date` Starting sed
sed -i 's/\\/\\\\/g' 1577cii1.ASC
echo `date` Ending sed
while read line; do
 if [[ "$#line" == "401" ]]
 then
 Archive_Lines["$line:0:19""$line:27"]="$line:27:10"
else
echo $#line
fi
done < 1577cii1.ASC
echo `date` Starting sed
sed -i 's/\\\\/\\/g' 1577cii1.ASC
echo `date` Ending sed
fi
echo `date`

Linux 执行:

ksh read4.sh
Sun Nov 12 15:03:18 CST 2017
Sun Nov 12 15:03:18 CST 2017 Starting sed
Sun Nov 12 15:03:19 CST 2017 Ending sed
402
405
403
339
403
403
Sun Nov 12 15:03:22 CST 2017 Starting sed
Sun Nov 12 15:03:23 CST 2017 Ending sed
Sun Nov 12 15:03:23 CST 2017

AIX 测试脚本:

#!/usr/bin/bash
export LANG="C"
echo `date`
typeset -A Archive_Lines
if [ -f "1577cii1.ASC" ]
then
echo `date` Starting perl
perl -pi -e 's/\\/\\\\/g' 1577cii1.ASC
echo `date` Ending perl
while read line; do
 if [[ "$#line" == "401" ]]
 then
 Archive_Lines["$line:0:19""$line:27"]="$line:27:10"
else
echo $#line
 fi
done < 1577cii1.ASC
echo `date` Starting perl
perl -pi -e 's/\\\\/\\/g' 1577cii1.ASC
echo `date` Ending perl
fi
echo `date`

AIX 测试执行:

  bash read_test.sh
    Sun Nov 12 15:00:17 CST 2017
    Sun Nov 12 15:00:17 CST 2017 Starting perl
    Sun Nov 12 15:00:18 CST 2017 Ending perl
    402
    405
    313
    403
    337
    403
    403
    Sun Nov 12 15:01:29 CST 2017 Starting perl
    Sun Nov 12 15:01:29 CST 2017 Ending perl
    Sun Nov 12 15:01:29 CST 2017

将 Archive_Lines["$line:0:19""$line:27"]="$line:27:10" 替换为 echo"。"

 bash read_test.sh
Sun Nov 12 16:56:27 CST 2017
Sun Nov 12 16:56:27 CST 2017 Starting perl
Sun Nov 12 16:56:27 CST 2017 Ending perl
.
.
.
.
.
Sun Nov 12 16:56:42 CST 2017 Starting perl
Sun Nov 12 16:56:42 CST 2017 Ending perl
Sun Nov 12 16:56:42 CST 2017

使用 Archive_Lines["$line:0:19""$line:27"]="$line:27:10"

 bash read_test.sh
Sun Nov 12 16:59:52 CST 2017
Sun Nov 12 16:59:52 CST 2017 Starting perl
Sun Nov 12 16:59:52 CST 2017 Ending perl
402
405
313
403
337
403
403
Sun Nov 12 17:01:11 CST 2017 Starting perl
Sun Nov 12 17:01:11 CST 2017 Ending perl
Sun Nov 12 17:01:11 CST 2017

谢谢, 瓦姆西

【问题讨论】:

是否有一个部分“明显”慢?此外,可能值得大量替换脚本(例如,使用 perl)。 应该在Code Review 上发布工作代码以进行优化。 @user2864740 - 在我的测试运行期间,我看到读取行和加载到关联数组中进行比较的 while 循环导致了差异。有人建议使用 BLOCKSIZE 参数,但我无法确定如何在 AIX 或 Linux 上检查此变量并进行修改。 对于ksh 中的typeset -A 支持,考虑切换到ksh93(我在AIX 上通过ksh93 广泛使用typeset -A); 尝试隔离差异。当您将Archive_Lines["$line:0:19""$line:27"]="$line:27:10" 替换为echo "." 时,您还会看到不同吗? 【参考方案1】:

正如 Walter 所建议的那样,bash 中的子字符串处理(可能还有长度测试)似乎存在一些性能问题。

看看其他解决方案的时间安排可能会很有趣。

这是一个简单的awk 解决方案,它应该与原始 bash/substring 逻辑执行相同的操作(使用您当前的示例数据文件;没有行长的输出!= 401):

awk 'length($0)==401  print substr($0,1,20)substr($0,28)"|"substr($0,28,10) ' 1577cii1.ASC | \
while IFS="|" read idx val
do
    Archive_Lines["$idx"]="$val"
done
length($0)==401 : 如果行长是 401 那么 ... print ...."|" ... :打印由管道 (|) 分隔的 2 部分输出/字段,其中字段是... substr($0,1,20)substr($0,28) :相当于你的 $line:0:19$line:27 substr($0,28,10) :相当于你的 $line:27:10 此时,长度为 401 的每一行都在生成像 string1|string2 这样的输出 while IFS="|" read idx val :将输入拆分为 2 个变量 ... Archive_Lines["$idx"]="$val" : 使用 2 个变量作为数组索引/值对

注意:添加管道 (|) 作为字段分隔符以防您的子字符串可能包含空格;当然,如果您的子字符串可以包含管道 (|),则替换为不会出现在您的子字符串中并且可以用作字段分隔符的其他字符。

目的是看看awk's内置的长度/子串处理是否比bash's长度/子串处理快...

【讨论】:

【参考方案2】:

这解决了我的问题

#!/usr/bin/ksh93
export LANG="C"
echo `date`
typeset -A Archive_Lines
if [ -f "1577cii1.ASC" ]
then
echo `date` Starting perl
perl -pi -e 's/\\/\\\\/g' 1577cii1.ASC
echo `date` Ending perl
while read line; do
 if [[ "$#line" == "401" ]]
 then
Archive_Lines[$line:0:19$line:27]="$line:27:10"
else
echo $#line
 fi
done < 1577cii1.ASC
echo `date` Starting perl
perl -pi -e 's/\\\\/\\/g' 1577cii1.ASC
echo `date` Ending perl
fi
echo `date`


ksh93 read_test3.sh
Sun Nov 12 19:19:34 CST 2017
Sun Nov 12 19:19:34 CST 2017 Starting perl
Sun Nov 12 19:19:34 CST 2017 Ending perl
402
405
403
339
403
403
Sun Nov 12 19:19:38 CST 2017 Starting perl
Sun Nov 12 19:19:39 CST 2017 Ending perl
Sun Nov 12 19:19:39 CST 2017

【讨论】:

嗯,所以“只是”切换到ksh93 的情况?如果是这样,听起来您的 AIX 管理员需要追踪您的 bash 版本的潜在问题…… @markp:是的,看起来 ksh 在关联数组方面的性能比 bash 更好,因为它只接受数字索引并且处理速度可能很慢。总之,我的问题解决了!感谢在这个过程中帮助过我的所有人。 请在答案中添加一些注释以确定“究竟是什么”解决了问题 - 这将使答案在未来更加有用。

以上是关于在 AIX/bash 上的 bash 循环中读取文件比在 Linux/ksh 中慢得多 - BLOCKSIZE?的主要内容,如果未能解决你的问题,请参考以下文章

While 循环在 Bash 的第一行之后停止读取

在 awk 循环中访问 bash 数组

使用for循环bash脚本逐行读取文件[重复]

在一段时间读取循环bash中从第4行获取变量

如何使用 for 循环在 Matlab 中读取许多声音文件?

Bash怎么逐行读取一个文件