计算外积和的最快方法是什么[Julia]

计算外积和的最快方法是什么[Julia],julia,Julia,在Julia中,最快的方法是什么: 其中是时间t处变量的一维列向量 在Julia代码中,一个选项是: A = zeros(n,n); for j=1:T A = A + Y(j,:)'*Y(j,:); end 在哪里 Y = [y_1' ... y_T']` 是一个(Txn)矩阵 但是,有没有更快的办法?谢谢。如果您事先知道y\u t的组成部分,最简单、最容易、最快的方法就是: A = Y*Y' 其中y\u t的不同值作为列存储在矩阵y中 如果您事先不知道y

在Julia中,最快的方法是什么:

其中是时间t处变量的一维列向量

在Julia代码中,一个选项是:

A = zeros(n,n);
for j=1:T
    A = A + Y(j,:)'*Y(j,:);
end
在哪里

Y = [y_1' 
    ... 
    y_T']` 
是一个
(Txn)
矩阵


但是,有没有更快的办法?谢谢。

如果您事先知道
y\u t
的组成部分,最简单、最容易、最快的方法就是:

A = Y*Y'
其中
y\u t
的不同值作为列存储在矩阵
y

如果您事先不知道
y\u t
的组成部分,可以使用BLAS:

n = 100;
t = 1000;
Y = rand(n,t);

outer_sum = zeros(n,n);

for tdx = 1:t
    BLAS.ger!(1.0, Y[:,tdx], Y[:,tdx], outer_sum)
end
如果您是BLAS新手,希望在此处帮助解释此函数中的参数,请参阅(类似示例)

这里的关键之一是将
y\t
向量存储为列,而不是
y
的行,因为访问列要比访问行快得多。有关这方面的更多信息,请参见Julia

更新第二个选项的(如果事先不知道
Y
的组件是什么,BLAS有时会最快,但并不总是最快。最大的决定因素是您使用的向量的大小。调用BLAS会产生一定的开销,因此只有在某些设置下才有价值。Julia的本机矩阵乘法将自动选择whet但是,如果您提前知道您正在处理一个BLAS将是最优的情况,那么您可以通过提前指定它来为Julia优化器节省一些工作(从而加快代码的速度)


请参见下面roygvib的精彩回应。它提供了许多创造性和指导性的方法来计算这一点积的总和。在某些情况下,许多方法将比BLAS更快。从roygvib提供的时间试验来看,盈亏平衡点似乎在
n=3000
左右。为了完成,下面是另一个例子,矢量化方法:

假设Y如下所示:

julia> Y = rand(1:10, 10,5)
10×5 Array{Int64,2}:
 2   1   6   2  10
 8   2   6   8   2
 2  10  10   4   6
 5   9   8   5   1
 5   4   9   9   4
 4   6   3   4   8
 2   9   2   8   1
 6   8   5  10   2
 1   7  10   6   9
 8   7  10  10   8
其思想是创建一个在维度1和维度3中定义的数组,该数组只有一列,然后将其与在维度2和维度3中定义的数组相乘,该数组只有一行。您的“时间”变量沿维度3变化。这基本上导致每个时间步中的单个kronecker乘积串联沿时间(即第三维度)方向

julia> KroneckerProducts = permutedims(Y, [2,3,1]) .* permutedims(Y, [3,2,1]);
现在我不清楚你的最终结果是否是一个“nxn”矩阵,由每个“kronecker”位置的所有计时总和产生

julia> sum(KroneckerProducts, 3)
5×5×1 Array{Int64,3}:
[:, :, 1] =
 243  256  301  324  192
 256  481  442  427  291
 301  442  555  459  382
 324  427  459  506  295
 192  291  382  295  371
或者简单地说是大规模3D阵列中所有元素的总和

julia> sum(KroneckerProducts)
8894
选择你喜欢的毒药:p


我不确定这是否会比上面Michael的方法快,因为
置换IMS
步骤可能很昂贵,而且对于非常大的阵列来说,这实际上可能是一个瓶颈(但我不知道它是如何在Julia中实现的……也许不是!),因此它可能不一定比每个时间步的简单循环迭代性能更好,即使它是“矢量化代码”。您可以尝试这两种方法,并亲自查看特定阵列的最快速度!

为了进行比较,我尝试了几种代码来计算a矩阵(我希望这是OP想要的…),包括内置矩阵乘法、BLAS.ger!和显式循环:

print_(x) = print(rpad(x,12))

# built-in vector * vector'
function perf0v( n, T, Y )
    print_("perf0v")
    out = zeros(n,n)
    for t = 1 : T
        out += slice( Y, :,t ) * slice( Y, :,t )'
    end
    return out
end

# built-in matrix * matrix'
function perf0m( n, T, Y )
    print_("perf0m")
    out = Y * Y'
    return out
end

# BLAS.ger!
function perf1( n, T, Y )
    print_("perf1")
    out = zeros(n,n)
    for t = 1 : T
        BLAS.ger!( 1.0, Y[ :,t ], Y[ :,t ], out )
    end
    return out
end

# BLAS.ger! with sub
function perf1sub( n, T, Y )
    print_("perf1sub")
    out = zeros(n,n)
    for t = 1 : T
        BLAS.ger!( 1.0, sub( Y, :,t ), sub( Y, :,t ), out )
    end
    return out
end

# explicit loop
function perf2( n, T, Y )
    print_("perf2")
    out = zeros(n,n)
    for t  = 1 : T,
        i2 = 1 : n,
        i1 = 1 : n
        out[ i1, i2 ] += Y[ i1, t ] * Y[ i2, t ]
    end
    return out
end

# explicit loop with simd
function perf2simd( n, T, Y )
    print_("perf2simd")
    out = zeros(n,n)
    for i2 = 1 : n,
        i1 = 1 : n
        @simd for t = 1 : T
            out[ i1, i2 ] += Y[ i1, t ] * Y[ i2, t ]
        end
    end
    return out
end

# transposed perf2
function perf2tr( n, T, Yt )
    print_("perf2tr")
    out = zeros(n,n)
    for t  = 1 : T,
        i2 = 1 : n,
        i1 = 1 : n
        out[ i1, i2 ] += Yt[ t, i1 ] * Yt[ t, i2 ]
    end
    return out
end

# transposed perf2simd
function perf2simdtr( n, T, Yt )
    print_("perf2simdtr")
    out = zeros(n,n)
    for i2 = 1 : n,
        i1 = 1 : n
        @simd for t = 1 : T
            out[ i1, i2 ] += Yt[ t, i1 ] * Yt[ t, i2 ]
        end
    end
    return out
end

#.........................................................

n = 100
T = 1000
@show n, T

Y = rand( n, T )
Yt = copy( Y' )

out = Dict()

for loop = 1:2
    println("loop = ", loop)

    for fn in [ perf0v, perf0m, perf1, perf1sub, perf2, perf2simd ]
        @time out[ fn ] = fn( n, T, Y )
    end
    for fn in [ perf2tr, perf2simdtr ]
        @time out[ fn ] = fn( n, T, Yt )
    end
end

# Check
error = 0.0
for k1 in keys( out ),
    k2 in keys( out )
    @assert sumabs( out[ k1 ] ) > 0.0
    @assert sumabs( out[ k2 ] ) > 0.0
    error += sumabs( out[ k1 ] - out[ k2 ] )
end
@show error
使用
julia-O--check bounds=no test.jl
(0.4.5版)获得的结果是:

对于一些不同的n&T值:

(n,T) = (1000,100)
loop = 2
perf0v        0.610885 seconds (2.01 k allocations: 1.499 GB, 25.11% gc time)
perf0m        0.008866 seconds (9 allocations: 7.630 MB)
perf1         0.182409 seconds (606 allocations: 9.177 MB)
perf1sub      0.180720 seconds (806 allocations: 7.657 MB, 0.67% gc time)
perf2         0.104961 seconds (6 allocations: 7.630 MB)
perf2simd     0.119964 seconds (6 allocations: 7.630 MB)
perf2tr       0.137186 seconds (6 allocations: 7.630 MB)
perf2simdtr   0.103878 seconds (6 allocations: 7.630 MB)

(n,T) = (2000,100)
loop = 2
perf0v        2.514622 seconds (2.01 k allocations: 5.993 GB, 24.38% gc time)
perf0m        0.035801 seconds (9 allocations: 30.518 MB)
perf1         0.473479 seconds (606 allocations: 33.591 MB, 0.04% gc time)
perf1sub      0.475796 seconds (806 allocations: 30.545 MB, 0.95% gc time)
perf2         0.422808 seconds (6 allocations: 30.518 MB)
perf2simd     0.488539 seconds (6 allocations: 30.518 MB)
perf2tr       0.554685 seconds (6 allocations: 30.518 MB)
perf2simdtr   0.400741 seconds (6 allocations: 30.518 MB)

(n,T) = (3000,100)
loop = 2
perf0v        5.444797 seconds (2.21 k allocations: 13.483 GB, 20.77% gc time)
perf0m        0.080458 seconds (9 allocations: 68.665 MB)
perf1         0.927325 seconds (806 allocations: 73.261 MB, 0.02% gc time)
perf1sub      0.926690 seconds (806 allocations: 68.692 MB, 0.51% gc time)
perf2         0.958189 seconds (6 allocations: 68.665 MB)
perf2simd     1.067098 seconds (6 allocations: 68.665 MB)
perf2tr       1.765001 seconds (6 allocations: 68.665 MB)
perf2simdtr   0.902838 seconds (6 allocations: 68.665 MB) 

嗯,所以内置的矩阵*矩阵(Y*Y')是最快的。看起来BLAS gemm在最后被调用(来自@less Y*Y')。

注意:StackOverflow不允许LaTeX格式:(-hm,用Y表示,以便他利用列主顺序是一个很好的观点。从某种意义上说,微观优化,但仍然相关,因为他要求的是“最快的方式”。BLAS是否最有效取决于矩阵/向量的大小,更大的尺寸往往有利于BLAS。同样在您的时间试验中,为什么不也包括
Y*Y'
?在上述模式中,
Y*Y'
最快(可能是因为BLAS)。我不知道这是BLAS到目前为止自动计算的…谢谢:)视情况而定,BLAS并不总是如此。我的理解是,Julia将尝试确定哪一个是最有效的。调用BLAS会有一些开销,因此并不总是有意义的。首先,Julia的内置函数往往非常好,但如果您提前知道BLAS将是最好的,那么您可以节省一点o如果没有做出决定,朱莉娅需要运用其逻辑做出决定(这通常是最好的,但并不总是最好的)。
(n,T) = (100,1000)
loop = 2
perf0v        0.056345 seconds (15.04 k allocations: 154.803 MB, 31.66% gc time)
perf0m        0.000785 seconds (7 allocations: 78.406 KB)
perf1         0.155182 seconds (5.96 k allocations: 1.846 MB)
perf1sub      0.155089 seconds (8.01 k allocations: 359.625 KB)
perf2         0.011192 seconds (6 allocations: 78.375 KB)
perf2simd     0.016677 seconds (6 allocations: 78.375 KB)
perf2tr       0.011698 seconds (6 allocations: 78.375 KB)
perf2simdtr   0.009682 seconds (6 allocations: 78.375 KB)
(n,T) = (1000,100)
loop = 2
perf0v        0.610885 seconds (2.01 k allocations: 1.499 GB, 25.11% gc time)
perf0m        0.008866 seconds (9 allocations: 7.630 MB)
perf1         0.182409 seconds (606 allocations: 9.177 MB)
perf1sub      0.180720 seconds (806 allocations: 7.657 MB, 0.67% gc time)
perf2         0.104961 seconds (6 allocations: 7.630 MB)
perf2simd     0.119964 seconds (6 allocations: 7.630 MB)
perf2tr       0.137186 seconds (6 allocations: 7.630 MB)
perf2simdtr   0.103878 seconds (6 allocations: 7.630 MB)

(n,T) = (2000,100)
loop = 2
perf0v        2.514622 seconds (2.01 k allocations: 5.993 GB, 24.38% gc time)
perf0m        0.035801 seconds (9 allocations: 30.518 MB)
perf1         0.473479 seconds (606 allocations: 33.591 MB, 0.04% gc time)
perf1sub      0.475796 seconds (806 allocations: 30.545 MB, 0.95% gc time)
perf2         0.422808 seconds (6 allocations: 30.518 MB)
perf2simd     0.488539 seconds (6 allocations: 30.518 MB)
perf2tr       0.554685 seconds (6 allocations: 30.518 MB)
perf2simdtr   0.400741 seconds (6 allocations: 30.518 MB)

(n,T) = (3000,100)
loop = 2
perf0v        5.444797 seconds (2.21 k allocations: 13.483 GB, 20.77% gc time)
perf0m        0.080458 seconds (9 allocations: 68.665 MB)
perf1         0.927325 seconds (806 allocations: 73.261 MB, 0.02% gc time)
perf1sub      0.926690 seconds (806 allocations: 68.692 MB, 0.51% gc time)
perf2         0.958189 seconds (6 allocations: 68.665 MB)
perf2simd     1.067098 seconds (6 allocations: 68.665 MB)
perf2tr       1.765001 seconds (6 allocations: 68.665 MB)
perf2simdtr   0.902838 seconds (6 allocations: 68.665 MB)