Performance awk超慢处理多行但不多列
在研究这一点时,面临的挑战是采用以下矩阵:Performance awk超慢处理多行但不多列,performance,awk,Performance,Awk,在研究这一点时,面临的挑战是采用以下矩阵: 4 5 6 2 9 8 4 8 m d 6 7 9 5 4 g t 7 4 2 4 2 5 3 h 5 6 2 5 s 3 4 r 5 7 1 2 2 4 1 4 1 9 0 5 6 d f x c a 2 3 4 5 9 0 0 3 2 1 4 q w 变成: 4 5 m d t 7 h 5 r 5 4 1 x c 0 0 6 2 # top of next 2 columns... 6 7 4 2 ... each N element
4 5 6 2 9 8 4 8
m d 6 7 9 5 4 g
t 7 4 2 4 2 5 3
h 5 6 2 5 s 3 4
r 5 7 1 2 2 4 1
4 1 9 0 5 6 d f
x c a 2 3 4 5 9
0 0 3 2 1 4 q w
变成:
4 5
m d
t 7
h 5
r 5
4 1
x c
0 0
6 2 # top of next 2 columns...
6 7
4 2
... each N elements from each row of the matrix -- in this example, N=2...
3 4
4 1
d f
5 9
q w # last element is lower left of matrix
OP声明输入比示例“大得多”,但没有指定实际输入的形状(数百万行?数百万列?或两者兼而有之?)
我(错误地)假设该文件有数百万行(后来被指定为有数百万列)
但有趣的是,如果数据的形状是数百万列,那么大多数编写的awk
都是完全可以接受的速度
示例:@glennjackman是一个完全可用的awk,只要长端是列而不是行。
在这里,您可以使用他的Perl生成行X列的示例矩阵。下面是Perl:
perl -E '
my $cols = 2**20; # 1,048,576 columns - the long end
my $rows = 2**3; # 8 rows
my @alphabet=( 'a'..'z', 0..9 );
my $size = scalar @alphabet;
for ($r=1; $r <= $rows; $r++) {
for ($c = 1; $c <= $cols; $c++) {
my $idx = int rand $size;
printf "%s ", $alphabet[$idx];
}
printf "\n";
}' >file
下面是一个Perl,无论数据的形状如何,它都足够快:
$ cat col.pl
push @rows, [@F];
END {
my $delim = "\t";
my $cols_per_group = 2;
my $col_start = 0;
while ( 1 ) {
for my $row ( @rows ) {
print join $delim, @{$row}[ $col_start .. ($col_start + $cols_per_group - 1) ];
}
$col_start += $cols_per_group;
last if ($col_start + $cols_per_group - 1) > $#F;
}
}
这里有一个替代的awk
,速度较慢,但速度一致(需要预先计算文件中的行数):
下面是具有许多列的计时(即,在上面生成文件的Perl脚本中,my$cols=2**20
和my$rows=2**3
):
印刷品:
# 2**20 COLUMNS; 2**3 ROWS
glenn jackman awk
real 0m4.460s
user 0m4.344s
sys 0m0.113s
glenn jackman gawk
real 0m4.493s
user 0m4.379s
sys 0m0.109s
perl
real 0m3.005s
user 0m2.774s
sys 0m0.230s
dawg Python
real 0m2.871s
user 0m2.721s
sys 0m0.148s
dawg awk
real 0m11.356s
user 0m11.038s
sys 0m0.312s
但是通过设置my$cols=2**3
和my$rows=2**20
来转换数据的形状,并运行相同的计时:
# 2**3 COLUMNS; 2**20 ROWS
glenn jackman awk
real 23m15.798s
user 16m39.675s
sys 6m35.972s
glenn jackman gawk
real 21m49.645s
user 16m4.449s
sys 5m45.036s
perl
real 0m3.605s
user 0m3.348s
sys 0m0.228s
dawg Python
real 0m3.157s
user 0m3.065s
sys 0m0.080s
dawg awk
real 0m11.117s
user 0m10.710s
sys 0m0.399s
因此,问题:
如果数据被转换成数百万行而不是数百万列,那么什么会导致第一个awk速度慢100倍
它是相同数量的元素处理和相同的总数据。调用join
函数的次数相同。保存在变量中的字符串连接是awk中最慢的操作之一(IIRC,它比I/O慢)由于您经常需要找到一个新的内存位置来保存连接的结果,而且随着行变长,awk脚本中会发生更多这种情况,因此可能是发布的解决方案中的所有字符串连接导致了速度减慢
类似的内容应该很快,不应该依赖于有多少字段和多少记录:
$ cat tst.awk
{
for (i=1; i<=NF; i++) {
vals[++numVals] = $i
}
}
END {
for (i=1; i<=numVals; i+=2) {
valNr = i + ((i-1) * NF) # <- not correct, fix it!
print vals[valNr], vals[valNr+1]
}
}
弦乐剧:
$ awk '
{
a[NR]=$0 # hash rows to a
c[NR]=1 # index pointer
}
END {
b=4 # buffer size for match
i=NR # row count
n=length(a[1]) # process til the first row is done
while(c[1]<n)
for(j=1;j<=i;j++) { # of each row
match(substr(a[j],c[j],b),/([^ ]+ ?){2}/) # read 2 fields and separators
print substr(a[j],c[j],RLENGTH) # output them
c[j]+=RLENGTH # increase index pointer
}
}' file
$awk'
{
a[NR]=$0#将行散列到a
c[NR]=1#索引指针
}
结束{
b=4#匹配的缓冲区大小
i=NR#行数
n=长度(a[1])#处理直到完成第一行
而(c[1]Ed Morton的方法确实解决了速度问题
下面是我编写的支持变量列的awk
:
$ cat col.awk
{
for (i=1; i<=NF; i++) {
vals[++numVals] = $i
}
}
END {
for(col_offset=0; col_offset + cols <= NF; col_offset+=cols) {
for (i=1; i<=numVals; i+=NF) {
for(j=0; j<cols; j++) {
printf "%s%s", vals[i+j+col_offset], (j<cols-1 ? FS : ORS)
}
}
}
}
$ time awk -f col.awk -v cols=2 file >file.cols
real 0m5.810s
user 0m5.468s
sys 0m0.339s
$cat col.awk
{
对于(i=1;i@EdMorton:这个问题的原始标题是awk中的字符串连接问题?是的,我确实这么认为。超快速完成这个问题的奇怪习惯用法是什么?很好。大小为2^20 x 2^3和2^3 x 2^20的数据集大约需要4秒。@JamesBrown感谢并感谢您对它进行测试!gawk还支持数组数组。如果您可以安装ll perl或python然后你就可以安装gawk了。@EdMorton我有gawk。我猜在完成了三个循环之后,我已经完成了!我指的是你答案中的最后一句话,但是伙计,对数组数组的强大支持(比如perl或python…)确实很好
听起来好像你不认为阵列的阵列在awk中可用。
# 2**3 COLUMNS; 2**20 ROWS
glenn jackman awk
real 23m15.798s
user 16m39.675s
sys 6m35.972s
glenn jackman gawk
real 21m49.645s
user 16m4.449s
sys 5m45.036s
perl
real 0m3.605s
user 0m3.348s
sys 0m0.228s
dawg Python
real 0m3.157s
user 0m3.065s
sys 0m0.080s
dawg awk
real 0m11.117s
user 0m10.710s
sys 0m0.399s
$ cat tst.awk
{
for (i=1; i<=NF; i++) {
vals[++numVals] = $i
}
}
END {
for (i=1; i<=numVals; i+=2) {
valNr = i + ((i-1) * NF) # <- not correct, fix it!
print vals[valNr], vals[valNr+1]
}
}
$ cat tst.awk
{
for (i=1; i<=NF; i++) {
vals[++numVals] = $i
}
}
END {
inc = NF - 1
for (i=0; i<NF; i+=2) {
for (j=1; j<=NR; j++) {
valNr = i + j + ((j-1) * inc)
print vals[valNr], vals[valNr+1]
}
}
}
$ awk -f tst.awk file
4 5
m d
t 7
h 5
r 5
4 1
x c
0 0
6 2
6 7
4 2
6 2
7 1
9 0
a 2
3 2
9 8
9 5
4 2
5 s
2 2
5 6
3 4
1 4
4 8
4 g
5 3
3 4
4 1
d f
5 9
q w
$ awk '
{
a[NR]=$0 # hash rows to a
c[NR]=1 # index pointer
}
END {
b=4 # buffer size for match
i=NR # row count
n=length(a[1]) # process til the first row is done
while(c[1]<n)
for(j=1;j<=i;j++) { # of each row
match(substr(a[j],c[j],b),/([^ ]+ ?){2}/) # read 2 fields and separators
print substr(a[j],c[j],RLENGTH) # output them
c[j]+=RLENGTH # increase index pointer
}
}' file
$ cat col.awk
{
for (i=1; i<=NF; i++) {
vals[++numVals] = $i
}
}
END {
for(col_offset=0; col_offset + cols <= NF; col_offset+=cols) {
for (i=1; i<=numVals; i+=NF) {
for(j=0; j<cols; j++) {
printf "%s%s", vals[i+j+col_offset], (j<cols-1 ? FS : ORS)
}
}
}
}
$ time awk -f col.awk -v cols=2 file >file.cols
real 0m5.810s
user 0m5.468s
sys 0m0.339s