我们的测试代码递归版本的斐波那契数列计算函数,由于递归版本涉及到大量重复计算,所以运行速度极慢。

一、python

  用 Python 计算第40项,耗时36.6秒。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import time

def lib(n):
    if n in [1,2]:
        return 1
    return fib(n-1) + fib(n-2)

start = time.time()
result = fib(40)
end = time.time()
print(f'{end-start}')

二、cython

  Cython 是把 Python 代码编译为 C 语言代码python的库。

1
2
3
4
5
6
import time

def lib(n):
    if n in [1,2]:
        return 1
    return fib(n-1) + fib(n-2)

  把这个文件保存为fib.pyx。然后我们创建一个setup.py文件,文件内容如下:

1
2
3
4
from setuptools import setup
from Cython.Build import cythonize

setup(ext_modules=cythonize('fib.pyx'))

  这个文件的作用,就是调用 Cython 的cythonize函数把 Python 代码转换为 C 代码。接下来,开始编译代码,执行如下命令:

1
python3 setup.py build_ext --inplace

  运行完成以后,会生成一个fib.cpython-37m-darwin.so。这个文件你可以改名字,例如改成fib.so。还有一个文件叫做fib.c。该文有3200多行。并且可以直接把它删掉。

1
2
3
4
5
6
7
import time
from fib import fib

start = time.time()
result = fib(40)
end = time.time()
print(f'{end-start}')

  计算斐波那契数列第40项只需要5秒钟。使用 Cython,不仅可以提高程序的运行速度,还可以把你的核心代码转换为.so文件,防止别人反编译看到你的代码。

三、golang

  我们只需要把 Golang 写的程序编译为.so文件就可以在 Python 里面调用了。
  首先修改一下我们的 Golang 代码,把计算斐波拉契数列的函数fib的首字母改成大写。在 Golang 中,只有首字母大写的函数,才能被package外面的代码调用。修改以后的代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
package main

import (
    "C"
)

//export Fib
func Fib(n int) int {
    if n == 1 || n == 2{
        return 1
    } else {
        return Fib(n - 1) + Fib(n - 2)
    }
}

func main() {}

  这里的注释//export Fib不能省略,因为在编译成.so文件的时候,编译器会寻找这个注释。同时,为了能正常编译,我们也必须导入C这个包。把代码保存为fib.go,然后我们使用如下命令进行编译:

1
go build -buildmode=c-shared -o _fib.so fib.go

  这条命令指定了编译模式为c-shared,编译完成以后,当前文件夹下面会生成一个_fib.so文件。这就是我们需要用 Python 载入的文件了。下面,我们创建一个 Python 文件load.py,代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
import ctypes
import time


so = ctypes.cdll.LoadLibrary('./_fib.so')
fib = so.Fib

start = time.time()
result = fib(40)
end = time.time()
print(f'{end-start}')

  在这段代码里面,通过 Python 自带的 ctypes 模块,加载 _fib.so 文件,然后提取出里面的 Fib 函数。这个函数就是用递归方式计算斐波那契数列的函数了。只需要 0.5 秒。
  但需要注意的是,由于 Golang 是静态强类型语言,定义函数的时候需要声明变量类型。如果你要导出的函数是字符串,那么变量类型需要改为 *C.char 而不是简单的 string。这里涉及到 C 语言中的变量类型与 Golang 中变量类型的映射关系。本文的例子中,由于整型比较特殊,直接使用 int 就可以了。但其他的类型可能并没有这么简单。

Reference

1、 https://mp.weixin.qq.com/s?__biz=MzI2MzEwNTY3OQ==&mid=2648978427&idx=1&sn=a0650a8ccf90c697078001bd57a8d1f7&scene=21#wechat_redirect
2、 https://mp.weixin.qq.com/s/H1Fe7BXdKIeEzIxjLxlYBg

打赏

微信 微信 支付宝 支付宝
万分感谢