实战案例题目为:
Problem 249 - Project Eulerprojecteuler.net虽然说Project Euler官方不鼓励用户公开答案或者代码, 但是, 这个属于陈年老题, 网上答案一大堆, 不差我一个, 而且题目难度也不大(难度等级为60%, 最高为100%, 最低为5%), 就违规一次, 将它作为讲解的案例题目好了.
本文章就不解释题目的算法了, 而是从程序代码实现的层面来讲解Julia的一些优化技巧.
废话不多说, 先贴代码, 再解释.
1 MATLAB的向量化版:
function
运行结果:
10.207570 秒。
如果是循环版的话, 时间得乘以100, 循环版代码我就不贴了.
2 Julia的向量化版:
using
运行结果:
julia
3 Julia的loop版:
function
运行结果:
julia
4 Julia的loop2版:
function
运行结果:
julia
5 Julia的loop3版:
function
运行结果:
julia
结果汇总一下(取平均值, 单位是秒):
- MATLAB的向量化版: 10.207570
- Julia的向量化版: 5.251
- Julia的loop版: 2.952
- Julia的loop2版: 1.731
- Julia的loop3版: 0.321392
MATLAB版本为R2018b
Julia版本为1.0.1
下面是讲解部分:
1 Julia的向量化版是MATLAB的向量化版的最直接翻译, 速度快了进一倍, 我估计是因为MATLAB的mod函数太慢.
2 Julia的loop版是Julia的向量化版最直接的转换, 速度又提升了近一倍.
3 Julia的loop2版相比Julia的loop版差别就在求余运算上, 将求余"%"改为了减法"-", 速度又提升了近一倍.
4 Julia的loop3版相比Julia的loop2版, 差别就在使用了宏命令"@inbounds"和"@simd", 速度提升到了5倍多. 这个有些让我吃惊.
"@inbounds"的意思是: 不负责检查索引号是否越界了, 让用户自己去确保那些索引号是不越界的, 万一一不小心越界了, 有可能让程序崩溃.
这个特性和C/C++的数组很像.
为了速度, 冒着程序崩溃的风险还是值得的, 更何况之前写了那么多的版本, 已经能够确保不越界了.
"@simd"的意思是: 编译器重新制定一个顺序(不一定是乱序, 根据需要, 可能是有规律的), 而不是for循环里面的顺序, 目的是有利于CPU的向量化(具体我也不太懂, 有兴趣的, 可以自己查资料).
具体到程序, 求和并不依赖于执行顺序的, 因为加法运算符合"交换律".
另外:
一个网友用C++写了程序, 在他电脑上运行时间为0.171秒.
假设按照主频进行线性换算, 他那个0.171到我这要0.19秒,
我的Julia时间是0.32秒, 2倍以内, 我已经很满意了.
总结:
1 Julia的循环比MATLAB的循环速度快得多, 当程序无法进行向量化运算的时候, Julia相比MATLAB就体现出了优势了.
2 Julia的优化技巧相比MATLAB多得多.
比如这里用到的宏命令"@inbounds"和"@simd", MATLAB就没有对等的.
还有很多其他技巧, 我会在以后提到.
3 Julia的性能测试相比MATLAB更加注重"内存".
---2018年12月28日更新------------------------------
关于SIMD, 补充一些知识(来自于教材"Julia High Performance"中的113/132):
SIMD提速的本质是: 处理器一个周期能够计算多个数字, 如果按照for循环一次让它计算一个数字, 就是计算资源的浪费. "@simd"就是让处理器一次能计算多个数字, 充分利用它的计算能力.
@simd能够提速的前提条件(只是列举一些, 不是全部):
1 for循环loop之间是相互独立的.
2 循环体里面没有分支结构(if, else), 也没有函数调用.
3 循环的次数是明显的.
4 循环的步长为1
5 边界检查必须关闭. 也就是说" @simd"必须配合"@inbounds".
关于第5点, 我之前的实验发现了这种现象:
测试代码:
using
测试性能:
using
2019-12-3更新
Python
警告:
稍微有一些赖皮, 因为求质数的部分移到了函数外面去, 因为如果在函数内部的话, numba老是会出现警告信息.
但考虑到程序的瓶颈不在求质数的部分, 因此, 赖皮的程度是可以接受的, 其他语言求不再修改重新测试了.
测试了一下求质数的部分:
发现这部分确实不是性能瓶颈. 因此, 我将它们移除函数进行测试, 问题不大.
结果汇总一下(取平均值, 单位是秒):
- MATLAB的向量化版: 10.207570
- Julia的向量化版: 5.251
- Julia的loop版: 2.952
- Julia的loop2版: 1.731
- Julia的loop3版: 0.321392
- Python的numba版: 0.477
MATLAB版本为R2018b
Julia版本为1.0.1
Python 3.7.3
Numba 0.46.0
Python的numba版稍慢于Julia的loop3版.
但快于Julia的其他版本.
我觉得主要原因是因为numba版没有"关闭边界检查"的命令, 也没有"simd"命令.
但整体性能上还是让人满意的.
相关文章:
haitao:Julia, Python(Numba), C++, MATLAB 性能测试之“冰雹数”zhuanlan.zhihu.com