计算外积和的最快方法是什么[Julia]
在Julia中,最快的方法是什么: 其中是时间t处变量的一维列向量 在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
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)