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