【问题标题】:Is the Julia language really as fast as it claims?Julia 语言真的像它声称的那样快吗?
【发布时间】:2013-10-13 00:47:59
【问题描述】:

在 this post 之后,我决定将 Julia 与 GNU Octave 进行基准测试,结果与 julialang.org 中说明的加速不一致。

我用CXXFLAGS='-std=c++11 -O3'编译了Julia和GNU Octave,得到的结果:

GNU Octave

a=0.9999;

tic;y=a.^(1:10000);toc
Elapsed time is 0.000159025 seconds.

tic;y=a.^(1:10000);toc
Elapsed time is 0.000162125 seconds.

tic;y=a.^(1:10000);toc
Elapsed time is 0.000159979 seconds.

--

tic;y=cumprod(ones(1,10000)*a);toc
Elapsed time is 0.000280142 seconds.

tic;y=cumprod(ones(1,10000)*a);toc
Elapsed time is 0.000280142 seconds.

tic;y=cumprod(ones(1,10000)*a);toc
Elapsed time is 0.000277996 seconds.

朱莉娅

tic();y=a.^(1:10000);toc()
elapsed time: 0.003486508 seconds

tic();y=a.^(1:10000);toc()
elapsed time: 0.003909662 seconds

tic();y=a.^(1:10000);toc()
elapsed time: 0.003465313 seconds

--

tic();y=cumprod(ones(1,10000)*a);toc()
elapsed time: 0.001692931 seconds

tic();y=cumprod(ones(1,10000)*a);toc()
elapsed time: 0.001690245 seconds

tic();y=cumprod(ones(1,10000)*a);toc()
elapsed time: 0.001689241 seconds

有人能解释一下为什么 Julia 在这些基本操作上比 GNU Octave 慢吗?加热后,它应该调用 LAPACK/BLAS 没有开销,对吧?

编辑:

正如 cmets 和答案中所解释的,上面的代码不是一个好的基准,也不能说明在实际应用程序中使用该语言的好处。我曾经认为 Julia 是一个更快的“Octave/MATLAB”,但它远不止于此。这是朝着高效、高性能、科学计算迈出的一大步。通过使用 Julia,我能够 1) 在我的研究领域中胜过用 Fortran 和 C++ 编写的软件,以及 2) 为用户提供更好的 API。

【问题讨论】:

  • 我相当肯定这些操作——.^ 或cumprod——都不是 BLAS 或 LAPACK 的一部分。这些操作只是作为 Octave 源代码的一部分在 C 中实现,而在 Julia 中作为 Julia 基本发行版的一部分实现。
  • @StefanKarpinski,我的意思是ones(1,10000)*a,内部可能使用霍纳规则或其他东西。但是你是对的,我不应该为这个特殊的 sn-p 代码提到 LAPACK/BLAS,我的手指总是在不知不觉中输入它们。 :)
  • 不到 1 秒太短,没有意义。

标签: benchmarking octave julia


【解决方案1】:

您正在使用全局变量,这是 Julia 中的一个性能问题。

问题在于,只要您的代码调用另一个函数,全局变量就有可能改变类型。因此,编译器必须生成极其缓慢的代码,无法对所使用的全局变量的类型做出任何假设。

根据https://docs.julialang.org/en/stable/manual/performance-tips/ 对代码进行简单修改应该会产生更令人满意的结果。

【讨论】:

  • 我在回答您的问题时简要说明了为什么会发生这种情况。基本上,问题在于,每当您的代码调用另一个函数时,全局变量可能会更改类型。因此,编译器必须生成极其缓慢的代码,无法对所使用的全局变量的类型做出任何假设。
  • 是的,这并不完全直观 - 我应该在我的回复中添加更多内容。
  • @StefanKarpinski 感谢您改进此答案。
  • 看起来链接给出了 404 错误。我认为这个链接应该给出预期的页面:docs.julialang.org/en/v1/manual/performance-tips.
【解决方案2】:

像 .^ 这样的矢量化操作正是 Octave 擅长的那种事情,因为它们实际上完全是用专门的 C 代码实现的。在构建 Octave 时编译的代码中的某处,有一个 C 函数计算 .^ 用于双精度和双精度数组 - 这就是您在这里真正计时的地方,而且速度很快,因为它是用 C 编写的。Julia 的另一方面,.^ 运算符是用 Julia 编写的:

julia> a = 0.9999;

julia> @which a.^(1:10000)
.^(x::Number,r::Ranges{T}) at range.jl:327

该定义包含以下内容:

.^(x::Number, r::Ranges) = [ x^y for y=r ]

它使用一维数组推导将x 提升为r 范围内的每个值y,并将结果作为向量返回。

Edward Garson 说得很对,不应该使用全局变量来获得 Julia 的最佳性能。原因是编译器不能很好地推断全局变量的类型,因为它们可以在执行离开当前范围的任何时候发生变化。离开当前范围听起来并不经常发生,但在 Julia 中,即使是索引到数组或添加两个整数之类的基本操作实际上也是方法调用,因此会离开当前范围。然而,在这个问题的代码中,所有时间都花在了 .^ 函数中,因此 a 是一个全局函数这一事实实际上并不重要:

julia> @elapsed a.^(1:10000)
0.000809698

julia> let a = 0.9999;
         @elapsed a.^(1:10000)
       end
0.000804208

最终,如果您所做的只是在浮点数组上调用矢量化操作,Octave 就可以了。但是,即使在高级动态语言中,这实际上也不是大部分时间花费的地方。如果你发现自己想用 for 循环遍历数组,用标量算法对每个元素进行操作,你会发现 Octave 在这种事情上相当慢——通常比 C 或 Julia 代码慢数千倍同一件事情。另一方面,在 Julia 中编写 for 循环是一件非常合理的事情——事实上,我们所有的排序代码都是用 Julia 编写的,并且在性能上与 C 相当。还有许多其他与性能无关的使用 Julia 的原因。作为 Matlab 的克隆,Octave 继承了 Matlab 的许多设计问题,并且作为通用编程语言表现不佳。例如,您不会想在 Octave 或 Matlab 中编写 Web 服务,但在 Julia 中这样做是 quite easy。

【讨论】:

  • 很高兴帮助回答您的问题。作为一项政策,我们尽量不使用 C 语言来实现。这让我们诚实——我们必须让 Julia 足够快以允许我们这样做。最好用语言来做所有事情,然后再慢一点,直到我们可以使编译器变得更好,然后一切都变得更快——系统代码和用户代码都一样。
  • 看起来“相当简单”的链接现在会导致 404 错误:(
猜你喜欢
  • 2016-01-26
  • 1970-01-01
  • 2011-09-25
  • 2011-02-28
  • 2018-04-05
  • 2015-09-11
  • 1970-01-01
  • 2017-06-01
  • 2015-10-13
相关资源
最近更新 更多