朱莉娅不稳定的时间

时间:2017-11-14 03:18:42

标签: julia

我使用Matlab几年后开始学习Julia。我开始实现一个简单的多项式乘法(没有FFT)来尝试理解类型稳定性的作用。该项目的一个重要部分是需要快速多项式乘法器。但是,我有以下时间,我根本无法理解。

function cauchyproduct(L::Array{Float64},R::Array{Float64})
    # good one for floats
    N = length(L)
    prodterm = zeros(1,2N-1)
    for n=1:N
        Lterm = view(L,1:n)
        Rterm = view(R,n:-1:1)
        prodterm[n] = dot(Lterm,Rterm)
    end

    for n = 1:N-1
        Lterm = view(L,n+1:N)
        Rterm = view(R,N:-1:n+1)
        prodterm[N+n] = dot(Lterm,Rterm)
    end
    prodterm
end



testLength = 10000
goodL = rand(1,testLength)
goodR = rand(1,testLength)
for j in 1:10
    @time cauchyproduct(goodL,goodR)
end
@which cauchyproduct(goodL,goodR)

我从这段代码的2次连续运行中得到以下时间。从一次奔跑到另一次奔跑的这些时间完全不稳定。一般来说,每次测试的时间范围可以在.05s到2s之间。通常,单个运行for循环的时间都将具有相似的时序(如下例所示),但即使如此,情况也始终如此。偶尔,我有它等候 .05s .05s 1.9s .04s .05s 2.1s时 等等。

知道为什么会这样吗?

  0.544795 seconds (131.08 k allocations: 5.812 MiB)
  0.510395 seconds (120.00 k allocations: 5.340 MiB)
  0.528362 seconds (120.00 k allocations: 5.340 MiB, 0.94% gc time)
  0.507156 seconds (120.00 k allocations: 5.340 MiB)
  0.507566 seconds (120.00 k allocations: 5.340 MiB)
  0.507932 seconds (120.00 k allocations: 5.340 MiB)
  0.527383 seconds (120.00 k allocations: 5.340 MiB)
  0.513301 seconds (120.00 k allocations: 5.340 MiB, 0.83% gc time)
  0.509347 seconds (120.00 k allocations: 5.340 MiB)
  0.509177 seconds (120.00 k allocations: 5.340 MiB)
  0.052247 seconds (120.00 k allocations: 5.340 MiB, 7.95% gc time)
  0.049644 seconds (120.00 k allocations: 5.340 MiB)
  0.047275 seconds (120.00 k allocations: 5.340 MiB)
  0.049163 seconds (120.00 k allocations: 5.340 MiB)
  0.049029 seconds (120.00 k allocations: 5.340 MiB)
  0.054050 seconds (120.00 k allocations: 5.340 MiB, 8.36% gc time)
  0.047010 seconds (120.00 k allocations: 5.340 MiB)
  0.051240 seconds (120.00 k allocations: 5.340 MiB)
  0.050961 seconds (120.00 k allocations: 5.340 MiB)
  0.049841 seconds (120.00 k allocations: 5.340 MiB, 4.90% gc time)

编辑:显示的时序是通过连续两次执行已定义函数下的代码获得的。具体来说,代码块

goodL = rand(1,testLength)
goodR = rand(1,testLength)
for j in 1:10
    @time cauchyproduct(goodL,goodR)
end

在不同的运行中给出了截然不同的时序(没有重新编译它上面的函数)。在所有时间中,都会调用相同的cauchyproduct方法(顶级版本)。希望这能澄清问题所在。

编辑2:我在最后将代码块更改为以下

testLength = 10000
goodL = rand(1,testLength)
goodR = rand(1,testLength)
for j = 1:3
    @time cauchyproduct(goodL,goodR)
end


for j = 1:3
    goodL = rand(1,testLength)
    goodR = rand(1,testLength)
    @time cauchyproduct(goodL,goodR)
end

@time cauchyproduct(goodL,goodR)
@time cauchyproduct(goodL,goodR)
@time cauchyproduct(goodL,goodR)

并在2次重复执行新块时得到以下时间。

时间1:

  0.045936 seconds (120.00 k allocations: 5.340 MiB)
  0.045740 seconds (120.00 k allocations: 5.340 MiB)
  0.045768 seconds (120.00 k allocations: 5.340 MiB)
  1.549157 seconds (120.00 k allocations: 5.340 MiB, 0.14% gc time)
  0.046797 seconds (120.00 k allocations: 5.340 MiB)
  0.046637 seconds (120.00 k allocations: 5.340 MiB)
  0.047143 seconds (120.00 k allocations: 5.341 MiB)
  0.049088 seconds (120.00 k allocations: 5.341 MiB, 3.88% gc time)
  0.049246 seconds (120.00 k allocations: 5.341 MiB)

时间2:

  2.250852 seconds (120.00 k allocations: 5.340 MiB)
  2.370882 seconds (120.00 k allocations: 5.340 MiB)
  2.247676 seconds (120.00 k allocations: 5.340 MiB, 0.14% gc time)
  1.550661 seconds (120.00 k allocations: 5.340 MiB)
  0.047258 seconds (120.00 k allocations: 5.340 MiB)
  0.047169 seconds (120.00 k allocations: 5.340 MiB)
  0.048625 seconds (120.00 k allocations: 5.341 MiB, 4.02% gc time)
  0.045489 seconds (120.00 k allocations: 5.341 MiB)
  0.049457 seconds (120.00 k allocations: 5.341 MiB)

太困惑了。

1 个答案:

答案 0 :(得分:3)

简短回答:您的代码有点奇怪,因此可能会以意想不到的方式触发垃圾回收,导致时间变化。

答案很长:我同意你得到的时间有点奇怪。我不能完全确定我能确切地确定造成问题的原因,但我99%肯定这与垃圾收集有关。

所以,你的代码有点奇怪,因为你允许任何维度的输入数组,即使你然后调用dot函数(一个BLAS例程来获取两个向量的点积)。如果您没有意识到,如果您想要一个向量,请使用Array{Float64,1},并使用矩阵Array{Float64,2},依此类推。或者您也可以使用别名Vector{Float64}Matrix{Float64}

我注意到的第二个奇怪的事情是,在测试中,您生成rand(1, N)。这将返回Array{Float64,2},即矩阵。要获得Array{Float64, 1},即向量,您可以使用rand(N)。然后在您的函数中,您可以查看大小为1xN的矩阵。现在,Julia使用列主要排序,因此对于向量使用1xN对象将真正低效,并且可能是您奇怪时间的来源。在引擎盖下,我怀疑对dot的调用将涉及将这些事物转换为浮点数的常规向量,因为dot最终会反馈到需要此输入类型的基础BLAS例程。所有这些转换都意味着大量的临时存储,需要在某些时候进行垃圾收集,这可能是不同时间的来源(90%的时间,相同代码的不同时间是垃圾的结果)收集器被触发 - 有时以非常意想不到的方式)。

因此,可能有几种方法可以改进以下内容,但我的快速和脏版本的功能如下所示:

function cauchyproduct(L::AbstractVector{<:Number}, R::AbstractVector{<:Number})
    length(L) != length(R) && error("Length mismatch in inputs")
    N = length(L)
    prodterm = zeros(1,2*N-1)
    R = flipdim(R, 1)
    for n=1:N
        prodterm[n] = dot(view(L, 1:n), view(R, N-n+1:N))
    end
    for n = 1:N-1
        prodterm[N+n] = dot(view(L, n+1:N), view(R, 1:N-n))
    end
    return prodterm
end

注意,我在循环之前翻转R,因此内存不需要在循环内反复重新排序。毫无疑问,这会导致您的奇怪的垃圾收集问题。然后,应用你的测试(我认为最好在循环中移动数组生成,以防一些聪明的缓存问题抛出时间):

testLength = 10000
for j = 1:20
    goodL = rand(testLength);
    goodR = rand(testLength);
    @time cauchyproduct(goodL,goodR);
end

我们得到这样的东西:

  0.105550 seconds (78.19 k allocations: 3.935 MiB, 2.91% gc time)
  0.022421 seconds (40.00 k allocations: 2.060 MiB)
  0.022527 seconds (40.00 k allocations: 2.060 MiB)
  0.022333 seconds (40.00 k allocations: 2.060 MiB)
  0.021568 seconds (40.00 k allocations: 2.060 MiB)
  0.021837 seconds (40.00 k allocations: 2.060 MiB)
  0.022155 seconds (40.00 k allocations: 2.060 MiB)
  0.022071 seconds (40.00 k allocations: 2.060 MiB)
  0.021720 seconds (40.00 k allocations: 2.060 MiB)
  0.024774 seconds (40.00 k allocations: 2.060 MiB, 9.13% gc time)
  0.021714 seconds (40.00 k allocations: 2.060 MiB)
  0.022066 seconds (40.00 k allocations: 2.060 MiB)
  0.021815 seconds (40.00 k allocations: 2.060 MiB)
  0.021819 seconds (40.00 k allocations: 2.060 MiB)
  0.021928 seconds (40.00 k allocations: 2.060 MiB)
  0.021795 seconds (40.00 k allocations: 2.060 MiB)
  0.021837 seconds (40.00 k allocations: 2.060 MiB)
  0.022285 seconds (40.00 k allocations: 2.060 MiB)
  0.021380 seconds (40.00 k allocations: 2.060 MiB)
  0.023828 seconds (40.00 k allocations: 2.060 MiB, 6.91% gc time)

第一次迭代是测量编译时间而不是运行时间,所以应该忽略(如果你不知道我的意思,那么请查看官方文档的性能提示部分)。如您所见,剩余的迭代速度更快,而且非常稳定。