Julia (Julia-lang) 与 Fortran 和 Python 的性能比较
Posted
技术标签:
【中文标题】Julia (Julia-lang) 与 Fortran 和 Python 的性能比较【英文标题】:Julia (Julia-lang) Performance Compared to Fortran and Python 【发布时间】:2014-01-04 00:21:15 【问题描述】:我改编了一个简单的程序来计算和绘制 Julia 的运动涡流来测试语言,我也是用 Python 编写的,没有什么特别的原因。
(免责声明:1. 我读到的关于 *** 的每一次性能比较都因不全面/不正确/写得好/相关等而受到抨击 - 我不是假装这是一个真正的比较,我只是想知道如何让 Julia 更快。2. 我知道 python 可以被优化,在 Cython 等中实现,这不是这个讨论的一部分,它只是在这里作为 Julia 和 Python 中等效函数的参考。)
代码和性能结果可见in a gist。
Julia 的性能明显慢于 Fortran。执行计算本身所花费的时间是(50000 个时间步):
Fortran: 0.051s
Julia: 2.256s
Python: 30.846s
Julia 比 Fortran 慢得多(约慢 44 倍),差距缩小,但在 10 倍以上的时间步长 (0.50s vs 15.24s
) 时仍然显着。
这些结果与the julia home page 上显示的结果明显不同。我究竟做错了什么?我可以将 Julia 修复得更快吗?
我已经浏览了 Julia Performance Tips 页面和 Julia 主页上比较背后的代码,但没有什么值得我解决的。
另外有趣的是,Julia 加载 PyPlot (5secs
ish!!) 的速度非常慢,而且读取文本文件的速度也比 Python 慢得多。我可以做些什么来改善这些事情吗?
请注意,上述时间并未显示 Julia 和 Python 的加载时间,它只是计算 AFAIK 所需的原始时间 - 请参阅代码。对于fortran,这就是全部。在每种情况下,绘图已大致关闭,以便进行速度比较。
计算机:Intel i7-3770,16GB 内存,SSD HD,操作系统:Ubuntu 13.10 64bit.,Fortran:gfortran,GNU Fortran (Ubuntu/Linaro 4.8.1-10ubuntu9) 4.8.1,Julia:版本 0.3.0- prerelease+396 (2013-12-12 00:18 UTC), Commit c5364db* (0 days old master), x86_64-linux-gnu, Python: 2.7.5+
更新:
根据 ivarne 的建议,我重写了 Julia 脚本(在上面的要点中更新):将 grunt 工作封装在函数中,声明所有内容的类型并将矩阵的不同元素拆分为不同的数组(如果适用)。 (我在很多地方都包含了 Float64,因为我尝试使用 Float32 看看是否有帮助,但大部分时间都没有)。
结果如下:
50,000
时间步长:
Fortran: 0.051s (entire programme)
Julia: raw calc.: 0.201s, calc. and return (?): 0.758s, total exec.: 6.947s
500,000
时间步长:
Fortran: 0.495s (entire programme)
Julia: raw calc.: 1.547s, calc. and return (?): 2.094s, total exec.: 8.521s
总结:
你可以加快 Julia 的速度。
您可以显着影响 Julia 的速度,具体取决于您衡量其性能的方式。
【问题讨论】:
他们确实在基准测试中提到了使用 BLAS。 matmul 的性能在 Fortran、C、Julia 和 MATLAB 中几乎相同,这也让其脱颖而出。如果测试的很大一部分花费在某个预编译库中,那么语言之间的比较可能不是公平的。也许您应该尽可能尝试用 BLAS 调用替换部分代码并再次进行比较? 在 Julia 中不需要在函数上声明类型,除非你想使用它进行多次调度。列表推导通常也无需任何努力即可获得正确的类型。如果您有类型/不可变结构,则必须声明类型以获得良好的性能。 A +1 仅用于免责声明。 【参考方案1】:我已经关注 Julia 项目一段时间了,我对可能相关的代码有一些 cmets。
您似乎在全局范围内运行了大量代码。 Julia 目前的全局环境非常慢,因为每次迭代都必须检查所有变量的类型。循环通常应该写在一个函数中。 您似乎使用了数组切片。目前,由于 Julia 没有快速的 Array 视图,因此会生成副本。您可能会尝试将它们切换为子数组,但它们目前比应有的要慢得多。PyPlot(和任何其他包)的加载时间是一个已知问题,因为将 Julia 代码解析和编译为机器代码非常耗时。有一些想法是为这个过程设置一个缓存,这样这个过程就可以瞬间完成,但它还没有完成。 Base 库目前以编译状态缓存,因此大部分基础设施现在都在 master 分支上。
添加: 我试图在一个独立的函数中运行测试并得到这些结果。看到这个gist
解析:
elapsed time: 0.334042578 seconds (11797548 bytes allocated)
主测试循环的树连续符文。
elapsed time: 0.62999287 seconds (195210884 bytes allocated)
elapsed time: 0.39398753 seconds (184735016 bytes allocated)
elapsed time: 0.392036875 seconds (184735016 bytes allocated)
请注意在第一次运行后时序如何改进,因为再次使用了编译后的代码。
更新 2 通过一些改进的内存处理(确保重复使用数组,因为分配不会复制),我将时间缩短到 0.2 秒(在我的机器上)。为了避免分配新数组,肯定可以做更多的事情,但它开始有点棘手。
这行不符合你的想法:
vx_old = vx
但这做你想做的事:
copy!(vx_old, vx)
并去向量化一个循环。
x += 0.5*(vx + vx_old)*delta_t
y += 0.5*(vy + vy_old)*delta_t
到:
for i = 1:nvortex
x[i] += 0.5*(vx[i] + vx_old[i])*delta_t
y[i] += 0.5*(vy[i] + vy_old[i])*delta_t
end
【讨论】:
【参考方案2】:@ivarne 涵盖了这一点,但需要更多关注:
julia> @time x=[1:10000];
elapsed time: 1.544e-5 seconds (80120 bytes allocated)
julia> @time y = x[1:10000];
elapsed time: 2.6857e-5 seconds (80120 bytes allocated)
哇。这需要大量的时间和内存。
julia> @time z = sub(x,1:10000);
elapsed time: 6.239e-6 seconds (296 bytes allocated)
好多了。为什么[:]
不做sub
做的事情?我不知道。嗯,我有点。当您转到索引z[10]
时,Julia 认为,hrmm,z 就像 x,只是索引偏移了 0,所以z[10]
是x[10+0]
。你去吧。如果您进行大量索引,那么从长远来看,这一点额外的添加将使您付出代价。要解决此问题,您需要一个与 Julia 的宗教背道而驰的概念,例如指针。
更新 Julia 现在弃用 [:]
(版本 0.4.0)
julia> @time x=[1:10000];
WARNING: [a] concatenation is deprecated; use collect(a) instead
in depwarn at deprecated.jl:73
in oldstyle_vcat_warning at ./abstractarray.jl:29
in vect at abstractarray.jl:32
while loading no file, in expression starting on line 155
0.530051 seconds (180.12 k allocations: 9.429 MB, 5.26% gc time)
julia> @time x=[1:10000];
WARNING: [a] concatenation is deprecated; use collect(a) instead
in depwarn at deprecated.jl:73
in oldstyle_vcat_warning at ./abstractarray.jl:29
in vect at abstractarray.jl:32
while loading no file, in expression starting on line 155
0.001373 seconds (303 allocations: 714.656 KB)
收集速度更快
julia> @ time x=collect(1:10000);
0.003991 seconds (35 allocations: 80.078 KB)
julia> @ time x=collect(1:10000);
0.000031 seconds (8 allocations: 78.406 KB)
相当于子数组
julia> @time z = sub(x,1:10000);
0.067002 seconds (36.27 k allocations: 1.792 MB)
julia> @time z = sub(x,1:10000);
0.000016 seconds (7 allocations: 288 bytes)
【讨论】:
到目前为止,数组切片进行复制的原因是,当切片在原始内存中不连续时,很难处理结果。找出解决此问题的技巧是避免复制的剩余工作。以上是关于Julia (Julia-lang) 与 Fortran 和 Python 的性能比较的主要内容,如果未能解决你的问题,请参考以下文章