设为首页 - 加入收藏 ASP站长网(Aspzz.Cn)- 科技、建站、经验、云计算、5G、大数据,站长网!
热搜: 重新 试卷 文件
当前位置: 首页 > 运营中心 > 建站资源 > 优化 > 正文

为什么Julia比Python快?因为天生理念就更先进啊(4)

发布时间:2019-02-26 02:41 所属栏目:21 来源:机器之心编译
导读:output:: 3-elementArray{Any,1}: 1.0 hi! :Symbolic 抽象类型的一种不太极端的形式是 Union 类型,例如: a=Vector{Union{Float64,Int}}(undef,3) a[1]=1.0 a[2]=3 a[3]=1/4 a output: 3-elementArray{Union{Flo

output::

  1. 3-element Array{Any,1}: 
  2.  1.0        
  3.  "hi!"     
  4.  :Symbolic 

抽象类型的一种不太极端的形式是 Union 类型,例如:

  1. a = Vector{Union{Float64,Int}}(undef,3) 
  2. a[1] = 1.0 
  3. a[2] = 3 
  4. a[3] = 1/4 

output:

  1. 3-element Array{Union{Float64, Int64},1}: 
  2. 1.0  
  3. 3    
  4. 0.25 

该案例只接受浮点型和整型数值,然而它仍然是一种抽象类型。一般在抽象类型上调用函数并不能知道任何元素的具体类型,例如在以上案例中每一个元素可能是浮点型或整型。因此通过多重分派实现优化,编译器并不能知道每一步的类型。因为不能完全优化,Julia 语言和其它脚本语言一样都会放慢速度。

这就是高性能原则:尽可能使用严格的类型。遵守这个原则还有其它优势:一个严格的类型 Vector{Float64} 实际上与 C/Fortran 是字节兼容的(byte-compatible),因此它无需转换就能直接用于 C/Fortran 程序。

高性能的成本

很明显 Julia 语言做出了很明智的设计决策,因而在成为脚本语言的同时实现它的性能目标。然而,它到底损失了些什么?下一节将展示一些由该设计决策而产生的 Julia 特性,以及 Julia 语言各处的一些解决工具。

1. 可选的性能

前面已经展示过,Julia 会通过很多方式实现高性能(例如 @inbounds),但它们并不一定需要使用。我们可以使用类型不稳定的函数,它会变得像 MATLAB/R/Python 那样慢。如果我们并不需要顶尖的性能,我们可以使用这些便捷的方式。

2. 检测类型稳定性

因为类型稳定性极其重要,Julia 语言会提供一些工具以检测函数的类型稳定性,这在 @code_warntype 宏中是最重要的。下面我们可以检测类型稳定性:

  1. @code_warntype 2^5 
  2.  
  3. Body::Int64 
  4. │220 1 ─ %1 = invoke Base.power_by_squaring(_2::Int64, _3::Int64)::Int64 
  5. │    └──      return %1 

注意这表明函数中的变量都是严格类型,那么 expo 函数呢?

  1. @code_warntype 2^5 
  2.  
  3. Body::Union{Float64, Int64} 
  4. │╻╷ >2 1 ─ %1  = (Base.slt_int)(0, y)::Bool 
  5. │    └──       goto #3 if not %1 
  6. │  3 2 ─ %3  = π (x, Int64) 
  7. │╻  ^  │   %4  = invoke Base.power_by_squaring(%3::Int64, _3::Int64)::Int64 
  8. │    └──       return %4 
  9. │  5 3 ─ %6  = π (x, Int64) 
  10. ││╻  Type  │   %7  = (Base.sitofp)(Float64, %6)::Float64 
  11. │  6 │   %8  = π (%7, Float64) 
  12. │╻  ^  │   %9  = (Base.sitofp)(Float64, y)::Float64 
  13. ││   │   %10 = $(Expr(:foreigncall, "llvm.pow.f64", Float64, svec(Float64, Float64), :(:llvmcall), 2, :(%8), :(%9), :(%9), :(%8)))::Float64 
  14. │    └──       return %10 

函数返回可能是 4% 和 10%,它们是不同的类型,所以返回的类型可以推断为 Union{Float64,Int64}。为了准确追踪不稳定性产生的位置,我们可以使用 Traceur.jl:

  1. using Traceur 
  2. @trace expo(2,5) 
  3.  
  4. ┌ Warning: x is assigned as Int64 
  5. └ @ In[8]:2 
  6. ┌ Warning: x is assigned as Float64 
  7. └ @ In[8]:5 
  8. ┌ Warning: expo returns Union{Float64, Int64} 
  9. └ @ In[8]:2 

output:32

这表明第 2 行 x 分派为整型 Int,而第 5 行它被分派为浮点型 Float64,所以类型可以推断为 Union{Float64,Int64}。第 5 行是明确调用 convert 函数的位置,因此这为我们确定了问题所在。原文后面还介绍了如何处理不稳定类型,以及全局变量 Globals 拥有比较差的性能,希望详细了解的读者可查阅原文。

结 论

设计上 Julia 很快。类型稳定性和多重分派对 Julia 的编译做特化很有必要,使其工作效率非常高。此外,鲁棒性的类型系统同样还需要在细粒度水平的类型上正常运行,因此才能尽可能实现类型稳定性,并在类型不稳定的情况下尽可能获得更高的优化。

原文链接:https://ucidatascienceinitiative.github.io/IntroToJulia/Html/WhyJulia

【本文是51CTO专栏机构“机器之心”的原创译文,微信公众号“机器之心( id: almosthuman2014)”】

戳这里,看该作者更多好文

(编辑:ASP站长网)

网友评论
推荐文章
    热点阅读