Julia 类型不稳定性导致的性能差异?

Julia 类型不稳定性导致的性能差异?,julia,Julia,我正在julia0.4-prerelease中尝试以下代码,它以两种不同的方式执行矩阵求幂(精确展开与系列展开)。我尝试使用几种方法来获取数组维度n和设置单位矩阵eye(n) 在这里,我观察到当n是一个动态变量(没有类型注释)时,代码会比其他变量慢很多。例如,(1a)和(2-1)的组合给出 下面的“慢”结果,而其他组合给出“快”结果(快1000倍以上) 这是因为for循环内部发生了“类型不稳定”吗?我很困惑,因为eye(n)总是Array{Float64,2}(仅用于初始化),并且似乎没有(隐式

我正在julia0.4-prerelease中尝试以下代码,它以两种不同的方式执行矩阵求幂(精确展开与系列展开)。我尝试使用几种方法来获取数组维度
n
和设置单位矩阵
eye(n)

在这里,我观察到当n是一个动态变量(没有类型注释)时,代码会比其他变量慢很多。例如,(1a)和(2-1)的组合给出 下面的“慢”结果,而其他组合给出“快”结果(快1000倍以上)


这是因为for循环内部发生了“类型不稳定”吗?我很困惑,因为
eye(n)
总是
Array{Float64,2}
(仅用于初始化),并且似乎没有(隐式)类型更改。同样令人困惑的是,(1e)和(2-1)的组合很快,其中动态
n
是用size()而不是length()设置的。总的来说,为了获得良好的性能,是否最好显式地注释数组维度变量?

我认为差异主要来自编译时间。如果我再放两个
test()
s,我会得到以下结果:

使用
2-1
1a

  73.599 milliseconds (70583 allocations: 3537 KB)
norm(B - Bref) = 4.485301019485633e-14
  15.165 microseconds (200 allocations: 11840 bytes)
norm(B - Bref) = 4.485301019485633e-14
  10.844 microseconds (200 allocations: 11840 bytes)
norm(B - Bref) = 4.485301019485633e-14
   8.662 microseconds (180 allocations: 11520 bytes)
norm(B - Bref) = 4.485301019485633e-14
   7.968 microseconds (180 allocations: 11520 bytes)
norm(B - Bref) = 4.485301019485633e-14
   7.654 microseconds (180 allocations: 11520 bytes)
norm(B - Bref) = 4.485301019485633e-14
使用
2-2
1a

  73.599 milliseconds (70583 allocations: 3537 KB)
norm(B - Bref) = 4.485301019485633e-14
  15.165 microseconds (200 allocations: 11840 bytes)
norm(B - Bref) = 4.485301019485633e-14
  10.844 microseconds (200 allocations: 11840 bytes)
norm(B - Bref) = 4.485301019485633e-14
   8.662 microseconds (180 allocations: 11520 bytes)
norm(B - Bref) = 4.485301019485633e-14
   7.968 microseconds (180 allocations: 11520 bytes)
norm(B - Bref) = 4.485301019485633e-14
   7.654 microseconds (180 allocations: 11520 bytes)
norm(B - Bref) = 4.485301019485633e-14
不过,编译时间的差异来自于编译的代码不同。这一点,以及剩余的一些时间上的微小差异,实际上来自于一种类型的不稳定性。查看
@code\u warntype test()
的这一部分,了解
1a
版本:

  GenSym(0) = (Base.LinAlg.__eig#214__)(GenSym(19),A::Array{Float64,2})::Tuple{Any,Any}
  #s8 = 1
  GenSym(22) = (Base.getfield)(GenSym(0),1)::Any
  GenSym(23) = (Base.box)(Base.Int,(Base.add_int)(1,1)::Any)::Int64
  lam = GenSym(22)
  #s8 = GenSym(23)
  GenSym(24) = (Base.getfield)(GenSym(0),2)::Any
  GenSym(25) = (Base.box)(Base.Int,(Base.add_int)(2,1)::Any)::Int64
  U = GenSym(24)
  #s8 = GenSym(25) # line 7:
  Bref = U * (Main.diagm)((Main.exp)(lam)::Any)::Any * (Main.ctranspose)(U)::Any::Any # line 9:
  n = (Main.length)(lam)::Any # line 11:
  B = (Main.eye)(n)::Any # line 11:
  X = (Main.eye)(n)::Any # line 13: # util.jl, line 170:
我将其理解为类型推断无法找出
eig
的返回类型。然后这会传播到B和X。如果添加
n::Int
,最后一行将更改为

  n = (top(typeassert))((top(convert))(Main.Int,(Main.length)(lam)::Any)::Any,Main.Int)::Int64 # line 11:
  B = (Base.eye)(Base.Float64,n::Int64,n::Int64)::Array{Float64,2} # line 11:
  X = (Base.eye)(Base.Float64,n::Int64,n::Int64)::Array{Float64,2} # line 13: # util.jl, line 170:

因此,
B
X
输入正确。-如果您想获得最佳性能,除了自己注释外,似乎没有太多选择。

Hmm。。我也得到了同样的结果(对test()的第二次和第三次调用变为“fast”)。我想象JIT编译器首先编译这个文件作为一个整体,并测量for循环的计算时间,但事实上编译是为更小的代码块执行的,@time包括这样的编译时间(如您所解释的)?time绝对包括编译,还有为编译后的函数分配的内存。我已经更新了我的答案,以便更清楚地了解为什么要首先编译不同的代码。是的,非常感谢。现在我明白主要是因为编译时间的不同。此外,我还阅读了您链接的页面(以及从那里链接的另一个页面),似乎有关于eig()的微妙问题。。。(而且@code_warntype的输出非常复杂@@。)无论如何,“Any”的出现似乎是一个警告标志。非常感谢:与
julia一起运行DTry--color=yes
-任何
都会弹出明亮的红色!