【发布时间】:2018-09-13 21:25:09
【问题描述】:
作为 Julia 的起点,我决定实现一个简单的 Strassen 产品:
@inbounds function strassen_product(A :: Array{Num, 2}, B :: Array{Num, 2}, k = 2) :: Array{Num, 2} where {Num <: Number}
A_height, A_width = size(A)
B_height, B_width = size(B)
@assert A_height == A_width == B_height == B_width "Matrices are noth both square or of equal size."
@assert isinteger(log2(A_height)) "Size of matrices is not a power of 2."
if A_height ≤ k
return A * B
end
middle = A_height ÷ 2
A₁₁, A₁₂ = A[1:middle, 1:middle], A[1:middle, middle+1:end]
A₂₁, A₂₂ = A[middle+1:end, 1:middle], A[middle+1:end, middle+1:end]
B₁₁, B₁₂ = B[1:middle, 1:middle], B[1:middle, middle+1:end]
B₂₁, B₂₂ = B[middle+1:end, 1:middle], B[middle+1:end, middle+1:end]
P₁ = strassen_product(A₁₁ + A₂₂, B₁₁ + B₂₂)
P₂ = strassen_product(A₂₁ + A₂₂, B₁₁ )
P₃ = strassen_product(A₁₁, B₁₂ - B₂₂)
P₄ = strassen_product(A₂₂, B₂₁ - B₁₁)
P₅ = strassen_product(A₁₁ + A₁₂, B₂₂ )
P₆ = strassen_product(A₂₁ - A₁₁, B₁₁ + B₁₂)
P₇ = strassen_product(A₁₂ - A₂₂, B₂₁ + B₂₂)
C₁₁ = P₁ + P₄ - P₅ + P₇
C₁₂ = P₃ + P₅
C₂₁ = P₂ + P₄
C₂₂ = P₁ + P₃ - P₂ + P₆
return [ C₁₁ C₁₂ ;
C₂₁ C₂₂ ]
end
一切顺利。实际上,我喜欢像 @inbounds 这样的不安全优化的整个想法,它实际上确实会大大影响性能,最多不会是几毫秒。
现在,为了进一步优化,因为我没有 for 循环,将使用视图用于那些 A₁₁ 等矩阵,因此不会发生复制。
所以我在包含索引的 4 行前面打了@views。当然我得到了一个错误,因为几行之后递归调用需要Array{...}参数而不是SubArray{...}。所以我将参数的类型和返回类型更改为AbstractArray{Num, 2}。这次它起作用了,因为AbstractArray 是数组类型的基本类型,但是......性能急剧下降,实际上慢了 10 倍,分配更多。
我的测试用例是这样的:
A = rand(1:10, 4, 4)
B = rand(1:10, 4, 4)
@time C = strassen_product(A, B)
使用@views + AbstractArray时:
0.457157 seconds (1.96 M allocations: 98.910 MiB, 5.56% gc time)
使用无视图版本时:
0.049756 seconds (126.92 k allocations: 5.603 MiB)
差别很大,应该更快的版本慢了 9-10 倍,分配量增加了大约 15 倍,空间几乎是其他版本的 20 倍。
编辑:这不是这两种情况的第一次运行,而是 ~10 次测试运行的最“中值”值。不是第一次运行,当然也不是最小或最大峰值。
编辑:我使用的是 1.0 版。
我的问题是:为什么会这样?我没有得到什么?我的推理是使用视图而不是副本会更快......错了吗?
【问题讨论】:
-
看起来您的时间是从第一次运行开始的:在这种情况下,主要成本是 JIT 编译。运行函数第一次运行后尝试计时。
-
您是否只运行一次
@time?第一次 JIT 编译时,请注意后续调用速度更快。我建议使用BenchmarkTools包中的@btime代替这些微基准测试。 -
计时后续运行,我仍然得到大致相同的计时。请注意,您仍在为输出分配结果:尝试就地完成整个事情。函数参数中的类型说明符也不会影响性能。有关详细信息,请参阅docs.julialang.org/en/v1/manual/performance-tips。
-
@SimonByrne 他们不是第一次运行,5-10 次运行的“中值”值越大
-
您使用的是哪个 Julia 版本?对于 Julia 1.0 上的两个版本,我得到的时间几乎相同(
@views版本略有优势)。
标签: arrays optimization julia