我使用下面的awk
命令在现有.dat
文件中的表中创建新的UUID列。
$ awk '("uuidgen" | getline uuid) > 0 {print uuid "|" $0} {close("uuidgen")}' $filename > ${filename}.pk
问题是我的.dat
文件很大(例如50-60 GB),即使在较小的数据文件(例如15MB)上,此awk
命令也要花费数小时。
有什么方法可以提高此awk
命令的速度吗?
答案 0 :(得分:1)
我想知道是否通过不用awk打开和关闭uuidgen
每行来节省时间。
$ function regen() { while true; do uuidgen; done; }
$ coproc regen
$ awk -v f="$filename" '!(getline line < f){exit} {print $0,line}' OFS="|" < /dev/fd/${COPROC[0]} > "$filename".pk
这已经从变量中读取您的“真实”文件名,并从标准输入中读取了uuid,因为对uuidgen的调用是由bash“ coprocess”处理的。 getline
周围的时髦之处在于,一旦awk用完$filename
的输入,便告诉awk退出。另外,请注意,awk从输入重定向中获取输入,而不是直接读取文件。这个很重要; /dev/fd/##
处的文件描述符是一个 bash 东西,awk无法打开它。
从理论上讲,这应该可以节省您执行不必要的系统调用来打开,运行和关闭uuidgen
二进制文件的时间。另一方面,通过在循环中运行uuidgen
,协同进程几乎可以做同样的事情。也许您会在SMP环境中看到一些改进。我没有用于基准测试的50GB文本文件。我很想听听您的结果。
请注意,coproc
是bash版本4引入的功能。使用/dev/fd/*
要求bash在文件描述符支持下进行编译。在我的系统中,这还意味着我必须确保已安装fdescfs(5)
。
我刚在系统(FreeBSD 11)上注意到以下内容:
$ /bin/uuidgen -
usage: uuidgen [-1] [-n count] [-o filename]
如果您的uuidgen
也有一个-n
选项,那么将它与ANY值一起添加到您的regen()
函数中可能是一个有用的优化方法,以减少命令需要执行的次数。重新打开。例如:
$ function regen() { while true; do uuidgen -n 100; done; }
这将导致uuidgen每100行输入仅被调用一次,而不是每行被调用。
如果您正在运行Linux,则取决于您的设置方式,您可能会有UUID的备用来源。注意:
$ awk -v f=/proc/sys/kernel/random/uuid '{getline u<f; close(f); print u,$0}' OFS="|" "$filename" "$filename".pk
这不需要bash coproc
,它只是直接从提供内核的Linux内核函数中awk直接读取了随机uuid。您仍在关闭输入的每一行的文件句柄,但至少不必执行uuidgen二进制文件。
YMMV。我不知道您正在运行什么操作系统,所以我不知道哪种可能适合您。
答案 1 :(得分:0)
我只是在猜测真正的问题是您正在为每行运行一个子流程。您可以逐行显式读取文件,并逐行读取batch-uuidgen的输出,因此只能一次处理一个子进程。不幸的是,uuidgen不能那样工作。
也许是另一个解决方案?
perl -MData::UUID -ple 'BEGIN{ $ug = Data::UUID->new } $_ = lc($ug->to_string($ug->create)) . " | " . $_' $filename > ${filename}.pk
这会更快吗?
答案 2 :(得分:0)
您的脚本正在调用shell来调用awk来调用shell来调用uuidgen。 Awk是用于处理文本的工具,它不是shell(可以从中调用其他工具的环境),所以不要那样做,只需从shell调用uuidgen:
$ cat file
foo .*
bar stuff
here
$ xargs -d $'\n' -n 1 printf '%s|%s\n' "$(uuidgen)" < file
5662f3bd-7818-4da8-9e3a-f5636b174e94|foo .*
5662f3bd-7818-4da8-9e3a-f5636b174e94|bar stuff
5662f3bd-7818-4da8-9e3a-f5636b174e94|here