我通常使用的是paste -sd+|bc
:
$ time seq 1 20000000 | paste -sd+|bc
200000010000000
real 0m10.092s
user 0m10.854s
sys 0m0.481s
(对于严格的Posix 合规性,paste
需要提供一个明确的参数:paste -sd+ -|bc
paste
但是,对于较大的输入,这将失败,因为bc
在评估之前将整个表达式缓冲在内存中。在我的系统上,bc
尝试添加 1 亿个数字时内存不足,尽管它能够完成 7000 万个。但其他系统可能具有较小的容量。
由于bc
有变量,您可以通过重复添加到变量而不是构造单个长表达式来避免长行。这是(据我所知)100% Posix 兼容,但有 3 倍的时间损失:
$ time seq 1 20000000|sed -e's/^/s+=/;$a\' -es|bc
200000010000000
real 0m29.224s
user 0m44.119s
sys 0m0.820s
处理输入大小超过bc
的缓冲容量的另一种方法是使用标准xargs
工具将数字添加到组中:
$ time seq 1 100000000 |
> IFS=+ xargs sh -c 'echo "$*"' _ | bc | paste -sd+ | bc
5000000050000000
real 1m0.289s
user 1m31.297s
sys 0m19.233s
每个xargs
评估使用的输入行数会因系统而异,但通常会有数百个,并且可能更多。显然,xargs | bc
可以任意链接调用以增加容量。
在超出命令容量的系统上,可能需要使用开关限制xargs
扩展的大小。除了进行实验来确定缓冲区限制之外,没有可移植的方法来确定该限制可能是多少,但它肯定不低于保证至少为 2048 的限制。即使有 100 位加数,这也将允许减少了 20 倍,因此假设您准备等待几个月才能完成,那么 10 个管道链将处理超过 10 13 个加数。-s
ARG_MAX
bc
bc
LINE_MAX
xargs|bc
作为构建大型固定长度管道的替代方法,您可以使用函数递归地管道输出,xargs|bc
直到只产生一个值:
radd () {
if read a && read b; then
{ printf '%s\n%s\n' "$a" "$b"; cat; } |
IFS=+ xargs -s $MAXLINE sh -c 'echo "$*"' _ |
bc | radd
else
echo "$a"
fi
}
如果你对 使用一个非常保守的值MAXLINE
,上面的速度会很慢,但如果值更大,它不会比简单的paste|bc
解决方案慢很多:
$ time seq 1 20000000 | MAXLINE=2048 radd
200000010000000
real 1m38.850s
user 0m46.465s
sys 1m34.503s
$ time seq 1 20000000 | MAXLINE=60000 radd
200000010000000
real 0m12.097s
user 0m17.452s
sys 0m5.090s
$ time seq 1 100000000 | MAXLINE=60000 radd
5000000050000000
real 1m3.972s
user 1m31.394s
sys 0m27.946s
除了bc
解决方案之外,我还计算了其他一些可能性。如上图,输入 2000 万个数字,paste|bc
耗时 10 秒。这几乎与将 2000 万个数字相加所用的时间相同
gawk -M '{s+=$0} END{print s}'
诸如python
并被perl
证明更快的编程语言:
# 9.2 seconds to sum 20,000,000 integers
python -c $'import sys\nprint(sum(int(x) for x in sys.stdin))'
# 5.1 seconds
perl -Mbignum -lne '$s+=$_; END{print $s}'
我无法dc -f - -e '[+z1<r]srz1<rp'
在大输入上进行测试,因为它的性能似乎是二次的(或更差);它在 3 秒内将 25000 个数字相加,但将 500000 和 1900000 用了 19 秒,而将 1000000000000000000000000000000000000000000000000000000000000000000000 加了 90 秒。
虽然bc
不是最快的并且内存限制需要笨拙的解决方法,但它的优点是可以在符合 Posix 的系统上开箱即用,而无需安装任何标准实用程序 ( awk
) 的增强版本或 Posix 不需要的编程语言 (perl
和python
) .