![f9ff8752bcfc26824e35f62124c3c106.png](https://i-blog.csdnimg.cn/blog_migrate/9128be9fab147a76254c3befd8aa6d00.jpeg)
最近在知乎上看了一篇关于用C++加速Python的短文,受益匪浅。同时也受到启发,撰写此文作为以后的参考。
作为Python的用户经常碰到的一个问题就是速度太慢,一般来说速度下降的一个主要原因是来自多重的for循环。如何给现有的Python代码加速其实是Python用户的一门必修课。比较熟悉numpy的用户,可以熟练地写成矩阵化操作,这样可以大大加速运行的效率。这和MATLAB里多用矩阵操作而少用for循环是一个道理。但往往我们中的大多数并没有高超的numpy技巧。即便是有,Python代码的阅读性可能反而会下降。就算退一步讲,写成了比较好的numpy的代码也未必会比C/C++的代码快。在不放弃Python语言的前提下,怎么用C语言来提速呢?方法有很多,前面的所指的短文已经给出了一个答案。这里我再用一个例子做一个简单的说明,希望对大家有所帮助。首先代码都可以取到,
https://github.com/yanfeit/PerlinNoisegithub.com关于Perlin噪声,我就不详细介绍。简单地说来,Perlin噪声具有光滑性,自然性和随机性的特点。感兴趣的读者可以找到很多相关资料,在这里我推荐两个,pvigier的GitHub site和Adrian's 的博客。Pvigier的Perlin噪声是用numpy来实现的,读者如果对自己numpy的技巧深感自信,可以去阅读一下他写的代码。阅读他的代码之后,我们可以给自己两个问题:
- 我是否可以写出这样高度矩阵化操作的numpy代码?
- 是否我们遇到的所用问题都可以用numpy矩阵化的操作来解决?
我想读者心中可能也会犯嘀咕,确实,高度矩阵化的操作需要程序员有高超的numpy技巧。反正我自愧不如,认为我很难写出那样漂亮的代码。我们再来看看Adrian的博客,这是篇博客文中的上乘之作, 是关于Perlin噪声的一个详细介绍,配合C#来实现。我想大多数读者可能和我一样,对写成for循环的形式感到极度舒适。而阅读这样的代码我想读者们也是驾轻就熟吧。所以我首先制作了一个Perlin噪声的C++代码(其实只用到了C的成分),之后我们会使用ctypes来调用动态链接库的代码。
创建动态链接库
cd build
$ cmake ..
$ make
$ mv ./lib/libperlinNoise.dylib ../python
为了快速测试一下效果,读者可以尝试执行以上的代码。
用了make之后我们会在/build/lib目录下得到一个libperlinNoise.dylib的动态链接库文件,在这个库里面我们可以调用两个函数。它们的接口如下所示,
// 你可以在./lib/PerlinNoise.h的文件中找到相应代码。
两个函数返回的是指向Float的指针,我选用了单精度的浮点数也就是float。这里面有个需要注意的地方,函数切记不要返回指向一个超过二维数组的指针,其实根本就没有这样的定义,具体请看这个帖子。有了libperlinNoise.dylib这个动态链接库之后,剩下的任务就交给Python了。以下是我的代码的一部分(借鉴了Pvigier的代码,在./python/cppnoise.py中可以找到相应的代码),
# 我们所采用加速的方法,ctypes是build-in package
相比于木盏的函数,我这里相对来说复杂一点点。我们需要注意的是,我们得告诉函数传入的参数的类型和返回的类型和大小,这点至关重要。
# 读取动态链接库,
在Python中调用动态链接库后得到的加速效果(当然我在C++用了单精度的float),读者可以自行修改成双精度去测试一下。
$ python caltime.py
2D noise, numpy time consuming: 0.08261830806732177
3D noise, numpy time consuming: 4.525643992424011
2D noise, cpp time consuming: 0.007184123992919922
3D noise, cpp time consuming: 0.1645211935043335
我们可以发现C语言的代码可以说快了将近10倍以上。
最后上个图,以飨读者。
![eb2f44ef428187b6b7f63d1f91fbf51e.gif](https://i-blog.csdnimg.cn/blog_migrate/4a512b04a2f4e2ef57f39fb53afba9b8.gif)