与 Python+Numba LLVM/JIT 编译的代码相比,Julia 的性能

Posted

技术标签:

【中文标题】与 Python+Numba LLVM/JIT 编译的代码相比,Julia 的性能【英文标题】:Julia performance compared to Python+Numba LLVM/JIT-compiled code 【发布时间】:2015-06-15 10:15:03 【问题描述】:

到目前为止我看到的 Julia 的性能基准,例如在 http://julialang.org/,将 Julia 与纯 Python 或 Python+NumPy 进行比较。与 NumPy 不同,SciPy 使用 BLAS 和 LAPACK 库,我们可以在其中获得最佳的多线程 SIMD 实现。如果我们假设 Julia 和 Python 在调用 BLAS 和 LAPACK 函数时的性能相同(在底层),那么在使用 Numba 或 NumbaPro 处理不调用 BLAS 或 LAPACK 函数的代码时,Julia 的性能与 CPython 相比如何?

我注意到的一件事是 Julia 使用的是 LLVM v3.3,而 Numba 使用的是基于 LLVM v3.5 构建的 llvmlite。 Julia 的旧 LLVM 是否会阻止在 Intel Haswell(AVX2 指令)等较新架构上实现最佳 SIMD?

我对意大利面条代码和处理非常大向量的小型 DSP 循环的性能比较感兴趣。由于将数据移入和移出 GPU 设备内存的开销,对我而言,后者由 CPU 比 GPU 更有效地处理。我只对单个 Intel Core-i7 CPU 的性能感兴趣,因此集群性能对我来说并不重要。我特别感兴趣的是创建 DSP 函数的并行实现的简单性和成功性。

这个问题的第二部分是 Numba 与 NumbaPro 的比较(忽略 MKL BLAS)。考虑到 Numba 中 @jit 装饰器的新 nogil 参数,真的需要 NumbaPro 的 target="parallel" 吗?

【问题讨论】:

@user3666197 煽动响应者并支持关于 SO 响应者的阴谋论不会引起对您的事业的同情。您的回答冗长且难以理解。您随后的 cmets 侮辱了 Julia 用户对 SO 的善意,他们自愿花时间回答问题。如果您对 Julia 性能时间与 Python/Numba 有建设性的批评,请考虑在 SO 或 Julia 用户列表上发布一个单独的问题。打嗝的这个问题不是合适的途径。 尊敬的 Kevin L. Keys,感谢您对已删除评论的回复,事实#1 删除帖子的做法称为审查,无论执行该类的动机如何的权力。 事实#2 LuaJIT 讨论中记录的不公平计时做法的引用是引用,而不是意见,越少侮辱。 Fact#3 建设性提案自答复的第一篇文章以来提出,作为可重复的 MCVE,允许运行 连贯-实验,而后来的 cmets 带来了但不连贯的测试因素(+来自记录的主要 Lua 事件的新光)。 科学批判性思维的美丽和力量在于它能够重复测试以确认或使理论、模型或测试无效。如果打嗝询问了 numba-LLVM/JIT 编译的性能,并且发表的声明说 GIL 步进的解释代码运行速度慢了 22 倍,那么下面提出的实验测试了相干实验的速度预期区域(应该在一边运行和更新语言维护者+使用更正的公平计时方法)。 已向教授发送了这方面的研究建议。 Sanders(现在,MIT Julia Lab)完全可行。 最后但并非最不重要的一点是,鉴于您的论点力求保护 (cit.:)“...... Julia 用户在 SO 上自愿花时间回答问题的善意”,让我请求您同样尊重我自愿回答 @hiccup 的问题和善意传达核心优点的同时被曝光重复审查和破坏性的投票歇斯底里。如果有人认为下面的答案难以理解和/或冗长,它会努力在可重复的 MCVE 实验中引用事实,以让那些可以+想要重新运行它的人获得结果。 鉴于之前几个关于缓存层次结构对测试的影响的 cmets 已被删除,并且希望审查员不会删除指向类似动机的 Jean-François Puget (IBM France) 的彻底实验的链接重新测试 Sebastian F. Walter 的测试,但是在实际大小的矩阵上(不同的缓存策略确实显示出它们的优势)>>> ibm.com/developerworks/community/blogs/jfp/entry/… 其中 SciPy+LAPACK 在上述矩阵大小上显示出它们显着的优势1000x1000。 【参考方案1】:

这是一个非常广泛的问题。关于基准测试请求,您最好自己运行一些符合您自己需求的小型基准测试。要回答其中一个问题:

我注意到的一件事是 Julia 使用的是 LLVM v3.3,而 Numba 使用的是基于 LLVM v3.5 构建的 llvmlite。 Julia 的旧 LLVM 是否会阻止在 Intel Haswell(AVX2 指令)等较新架构上实现最佳 SIMD?

[2017/01+:以下信息不再适用于当前的 Julia 版本]

Julia 确实在 LLVM 3.3 中关闭了 avx2,因为 Haswell 上有一些严重的错误。

Julia 使用 LLVM 3.3 构建当前版本和 nightlies,但您可以使用 3.5、3.6 和通常的 svn trunk 构建(如果我们尚未针对特定日期的某些 API 更改进行更新,请提交问题)。为此,请在Make.user 中设置LLVM_VER=svn(例如),然后按照构建说明进行操作。

【讨论】:

【参考方案2】:

请参阅here(第 4 节)了解我个人参与的一些同行评审基准。比较的是 Julia 和 PyPy。

【讨论】:

我将 PyPy 排除在外,因为它不支持 SciPy、matplotlib、64 位 Windows+Python 和 Python 3.3+。 2013 年撰写参考论文时,PyPy 也不支持 BLAS & LAPACK。对于科学应用,我更喜欢与 CPython+SciPy+LLVM(Numba 或 NumbaPro)进行比较。【参考方案3】:

(比较无与伦比,永远是一把双刃剑。

以下内容是出于公平的信念,即应将 LLVM/JIT 驱动的代码基准与其他一些 LLVM/JIT 驱动的替代方案进行比较,以得出任何得出的结论应作为合理支持的决策的基础。)


简介:numba 的东西和 [us] 结果在页面下方有点低)

恕我直言,julia-lang 官方网站提供了一组性能测试列表,其中陈述了两类事实。第一个,与性能测试的执行方式有关(julia,使用 LLVM 编译的代码执行 v/s python,仍然是 GIL 步进的解释代码执行)。第二,其他语言完成同一个“benchmark-task”需要多长时间,以C编译代码执行为相对时间单位=1.0

The chapter header, above a Table with results, says (cit.:)

高性能 JIT 编译器 Julia 基于 LLVM 的即时 (JIT) 编译器与该语言的设计相结合,使其性能接近并经常与 C 相匹配。 我认为比较苹果和苹果会更严格一些,并且只采用了一个“benchmark-task”-s,称为 pi-sum

这是解释型 python 的第二差,运行速度比 LLVM/JIT 编译的 julia-code 或 C 编译的替代方案慢 21.99 倍

于是小实验故事开始了。

@numba.jit( JulSUM, nogil = True )

让我们开始比较苹果和苹果。如果 julia 代码被报告运行速度提高了 22 倍,那么我们首先测量一个普通解释的 python 代码运行。

>>> def JulSUM():
...     sum = 0.
...     j   = 0
...     while j < 500:
...           j   += 1
...           sum  = 0.
...           k    = 0
...           while k < 10000:
...                 k   += 1
...                 sum += 1. / ( k * k )
...     return sum
...
>>> from zmq import Stopwatch
>>> aClk = Stopwatch()
>>> aClk.start();_=JulSUM();aClk.stop()
1271963L
1270088L
1279277L
1277371L
1279390L
1274231L

所以,pi-sum 的核心运行大约 1.27x.xxx [us] ~ 大约 1.27~1.28 [s]

鉴于julia-lang 网站上的table row for pi-sum in language presentation,LLVM/JIT 驱动的 julia 代码执行速度应该快 22 倍,即低于 ~57.92 [ms]

>>> 1274231 / 22
57919

所以,让我们使用 numba.jit (v24.0)

将橙子转换为苹果
>>> import numba
>>> JIT_JulSUM = numba.jit( JulSUM )
>>> aClk.start();_=JIT_JulSUM();aClk.stop()
1175206L
>>> aClk.start();_=JIT_JulSUM();aClk.stop()
35512L
37193L
37312L
35756L
34710L

因此,在 JIT 编译器完成工作后,numba-LLVM'ed python 的基准测试时间在某处大约 34.7 ~ 37.3 [ms]

我们能走得更远吗?

哦,当然,我们还没有对 numba 进行太多调整,虽然代码示例如此琐碎,但预计未来不会出现太多令人惊讶的进步。

首先,让我们删除这里不必要的 GIL 步进:

>>> JIT_NOGIL_JulSUM = numba.jit( JulSUM, nogil = True )
>>> aClk.start();_=JIT_NOGIL_JulSUM();aClk.stop()
85795L
>>> aClk.start();_=JIT_NOGIL_JulSUM();aClk.stop()
35526L
35509L
34720L
35906L
35506L

nogil=True 并没有使执行更远,但仍会减少几个 [ms],使 所有结果低于 ~ 35.9 [ms]

>>> JIT_NOGIL_NOPYTHON_JulSUM = numba.jit( JulSUM, nogil = True, nopython = True )
>>> aClk.start();_=JIT_NOGIL_NOPYTHON_JulSUM();aClk.stop()
84429L
>>> aClk.start();_=JIT_NOGIL_NOPYTHON_JulSUM();aClk.stop()
35779L
35753L
35515L
35758L
35585L
35859L

nopython=True 只进行最后的润色以获得所有结果始终低于 ~ 35.86 [ms](对比 ~57.92 [ms ] 对于 LLVM/JIT-julia )


DSP 处理结语:

关于加速 DSP 处理的额外好处的 OP 问题,可以尝试测试 numba + Intel Python(通过 Anaconda ),英特尔在二进制文件中开辟了新视野,针对 IA64 处理器内部进行了优化,因此代码执行可能会享受额外的 CPU 绑定技巧,基于英特尔对 ILP4 的了解,矢量化和分支预测详细说明了他们自己的 CPU-s在运行时展示。值得一试来比较这一点(另外一个人可能会喜欢他们集成到 VisualStudio 中的非破坏性代码分析工具,其中可以实时分析体外代码执行热点——这是 DSP 工程师会喜欢的东西,他/她不会吗?

【讨论】:

您真的在自己的机器上运行了 Julia 代码吗?哪个确切的代码?时间是什么?我建议将工作量乘以至少 100 倍,以便进行更公平的比较。 (是的,500x 重复的 10k 循环可以运行更多次,但是我保留了引用的 julia-lang 站点方法 1:1)。 比较 Julia 和 numba 既明智又有趣。但为了做到这一点,代码显然必须在同一台机器上运行。 对于这个特殊的微基准测试,Julia 0.5 的速度是我机器上的 numba 的两倍。 这里有一个example 的替代方法,在这种方法中,GitHub 可能比 *** 更适合扩展讨论和分析。

以上是关于与 Python+Numba LLVM/JIT 编译的代码相比,Julia 的性能的主要内容,如果未能解决你的问题,请参考以下文章

Numba具有什么性能?

Python numpy:无法将 datetime64[ns] 转换为 datetime64[D](与 Numba 一起使用)

Numba 可以与 Tensorflow 一起使用吗?

如何使 numba @jit 使用所有 cpu 内核(并行化 numba @jit)

使用Numba加速OpenCV Python视频流代码。提升6.5倍性能

分配给数组时Numba慢吗?