前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >30倍!使用Cython加速Python代码

30倍!使用Cython加速Python代码

作者头像
量化投资与机器学习微信公众号
发布于 2019-07-30 09:37:13
发布于 2019-07-30 09:37:13
1.8K00
代码可运行
举报
运行总次数:0
代码可运行

标星★公众号 爱你们♥

作者:George Seif、Thomas Wolf、Lukas Frei

编译:1+1=6 | 公众号海外部

近期原创文章:

5种机器学习算法在预测股价的应用(代码+数据)

Two Sigma用新闻来预测股价走势,带你吊打Kaggle

♥ 2万字干货:利用深度学习最新前沿预测股价走势

机器学习在量化金融领域的误用!

基于RNN和LSTM的股市预测方法

如何鉴别那些用深度学习预测股价的花哨模型?

优化强化学习Q-learning算法进行股市

WorldQuant 101 Alpha、国泰君安 191 Alpha

基于回声状态网络预测股票价格(附代码)

计量经济学应用投资失败的7个原因

配对交易千千万,强化学习最NB!(文档+代码)

关于高盛在Github开源背后的真相!

新一代量化带货王诞生!Oh My God!

独家!关于定量/交易求职分享(附真实试题)

Quant们的身份危机!

拿起Python,防御特朗普的Twitter

AQR最新研究 | 机器能“学习”金融吗?

前言

你可能经常会一次又一次地听到关于Python的抱怨,Python跑起来太慢了!

与许多其他编程语言相比,Python的确很慢。

有几种不同的方法可以使代码提速:

如果你的代码是纯Python。如果你有一个很大的for循环,你只能使用它,而不能放入矩阵中,因为数据必须按顺序处理,那该怎么办?有没有办法加快Python本身的速度?

来吧,看看Cython!

文末下载Cython相关书籍

什么是Cython?

Cython的核心是Python和C / C++之间的一个中间步骤。它允许N你编写纯Python代码,只需要做一些小修改,然后将其直接翻译成C代码

Cython 语言是 Python 的一个超集,它包含有两种类型的对象:

  • Python 对象就是我们在常规 Python 中使用到的那些对象,诸如数值、字符串、列表和类实例等等。
  • Cython C 对象就是那些 C 和 C++ 对象,诸如双精度、整型、浮点数、结构和向量,它们能够由 Cython 在超级高效的低级语言代码中进行编译。

你对Python代码所做的唯一调整就是向每个变量添加类型信息。通常,我们可以像这样在Python中声明一个变量:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
x = 0.5

使用Cython,我们为该变量添加一个类型:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cdef float x = 0.5

这告诉Cython,变量是浮点数,就像我们在C中所做的一样。对于纯Python,变量的类型是动态确定的。Cython中类型的显式声明使其转为C代码成为可能,因为显式类型声明需要+。

有很多办法来测试、编译和发布 Cython 代码。Cython 甚至可以像 Python 一样直接用于 Jupyter Notebook 中。有很多办法来测试、编译和发布 Cython 代码。Cython 甚至可以像 Python 一样直接用于 Jupyter Notebook 中。

安装Cython只需要一行pip:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
pip install cython

使用Cython需要安装C语言编译器,因此,安装过程会根据你当前的操作系统而有所不同。对于Linux,通常使用GNU C编译器(gncc)。对于Mac OS,你可以下载Xcode以获取gncc。而Windows 桌面系统下安装C编译器会更复杂。

使用 %load_ext Cython 指令在 Jupyter notebook 中加载 Cython 扩展。

然后通过指令 %%cython,我们就可以像 Python 一样在 Jupyter notebook 中使用 Cython。

如果在执行 Cython 代码的时候遇到了编译错误,请检查 Jupyter 终端的完整输出信息。

大多数情况下可能都是因为在 %%cython 之后遗漏了 -+ 标签(比如当你使用 spaCy Cython 接口时)。如果编译器报出了关于 Numpy 的错误,那就是遗漏了 import numpy。

如果你要在在IPython中使用Cython

首先介绍一下IPython Magic命令。Magic命令以百分号开头,通常有2种类型:

  • 单行Magic由单个'%'表示,并且仅在一行输入上操作。
  • 单元格Magic用两个'%'表示,并在多行输入上操作。

首先运行下列语句引入Cython:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
%load_ext Cython

然后,当运行Cython代码时,我们需要加入以下Cython 代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
%%cython

然后就可以愉快地使用Cython了。

Cython中的类型

使用Cython时,变量和函数有两组不同的类型。

对于变量,我们有:

  • cdef int a, b, c
  • cdef char *s
  • cdef float x = 0.5 (single precision)
  • cdef double x = 63.4 (double precision)
  • cdef list names
  • cdef dict goals_for_each_play
  • cdef object card_deck

注意所有这些类型都来自C / C++ !

  • def - 常规Python函数,仅从Python调用。
  • cdef - 仅限Cython函数,接受Python对象或C值作为参数,并且可以返回Python对象或C值,cdef函数不能直接在Python中调用。
  • cpdef - 接受Python对象或C值作为参数,并且可以返回Python对象或C值。

我们可以方便的向C代码传递和返回结果,Cython会自动为我们做相应的类型转化。

了解了Cython类型之后,我们就可以直接实现加速了!

如何使用Cython加速代码

我们要做的第一件事是设置Python代码基准:用于计算数字阶乘的for循环。原始Python代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def test(x):
     y = 1
     for i in range(x+1):
        y *= i
     return y

Cython的实现过程看起来非常相似。首先,确保Cython代码文件具有 .pyx 扩展名。这些文件将被 Cython 编译器编译成 C 或 C++ 文件,再进一步地被 C 编译器编译成字节码文件。

你也可以使用 pyximport 将一个 .pyx 文件直接加载到 Python 程序中:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import pyximport; pyximport.install()
import my_cython_module

你也可以将自己的 Cython 代码作为 Python 包构建,然后像正常的 Python 包一样将其导入或者发布。不过这种做法需要花费更多的时间,特别是你需要让 Cython 包能够在所有的平台上运行。如果你需要一个参考样例,不妨看看 spaCy 的安装脚本:

https://github.com/explosion/spaCy/blob/master/setup.py?source=post_page---------------------------

最终 Python 解释器将能够调用这些字节码文件。对代码本身的惟一更改是,我们已经声明了每个变量和函数的类型。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
cpdef int test(int x):
     cdef int y = 1
     cdef int i
     for i in range(x+1):
         y *= i
     return y

注意函数有一个cpdef来确保我们可以从Python调用它。另外看看我们的循环变量 i 是如何具有类型的。你需要为函数中的所有变量设置类型,以便C编译器知道使用哪种类型!

接下来,创建一个 setup.py 文件,该文件将Cython代码编译为C代码:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from distutils.core import setup
from Cython.Build import cythonize

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

并执行编译:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
python setup.py build_ext --inplace

Boom!我们的C代码已经编译好,可以使用了!

你将看到,在Cython代码所在的文件夹中,拥有运行C代码所需的所有文件,包括 run_cython.c 文件。如果你感兴趣,可以查看一下Cython生成的C代码!

现在我们准备测试新的C代码!查看下面的代码,它将执行一个速度测试,将原始Python代码与Cython代码进行比较。

现在我们准备测试我们新的超快速C代码了!查看下面的代码,它执行速度测试以将原始Python代码与Cython代码进行比较。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import run_python
import run_cython
import time

number = 10

start = time.time()
run_python.test(number)
end = time.time()

py_time = end - start
print("Python time = {}".format(py_time))

start = time.time()
run_cython.test(number)
end = time.time()

cy_time = end - start
print("Cython time = {}".format(cy_time))

print("Speedup = {}".format(py_time / cy_time))

Cython可以让你在几乎所有原始Python代码上获得良好的加速,而不需要太多额外的工作。需要注意的关键是,循环次数越多,处理的数据越多,Cython可以提供的帮助就越多。

查看下表,该表显示了Cython为不同的阶乘值提供的速度我们使用Cython获得了超过 36倍 的加速!

Cython在NLP中的加速应用

当我们在操作字符串时,要如何在 Cython 中设计一个更加高效的循环呢?spaCy是个不错的选择!

spaCy 中所有的unicode字符串(the text of a token, its lower case text, its lemma form, POS tag label, parse tree dependency label, Named-Entity tags…)都被存储在一个称为 StringStore 的数据结构中,它通过一个64位哈希码进行索引,例如C类型的 uint64_t。

StringStore对象实现了Python unicode字符串与 64 位哈希码之前的查找映射。

它可以spaCy的任何地方和任意对象进行访问,例如 npl.vocab.strings、doc.vocab.strings 或者 span.doc.vocab.string。

当某模块需要在某些标记上获得更快的处理速度时,可以使用C语言类型的64位哈希码代替字符串来实现。调用StringStore查找表将返回与该哈希码相关联的Python unicode字符串。

但是spaCy能做的可不仅仅只有这些,它还允许我们访问文档和词汇表完全填充的C语言类型结构,我们可以在Cython循环中使用这些结构,而不必去构建自己的结构。

spaCy拓展:

https://spacy.io/api/cython?source=post_page---------------------------

建立一个脚本用于创建一个包含有 10 份文档的列表,每份文档都大概含有 17 万个单词,采用 spaCy 进行分析。当然我们也可以对 17 万份文档(每份文档包含 10 个单词)进行分析,但是这样做会导致创建的过程非常慢,所以我们还是选择了 10 份文档。

我们想要在这个数据集上展开某些自然语言处理任务。例如,我们可以统计数据集中单词「run」作为名词出现的次数(例如,被 spaCy 标记为「NN」词性标签)。

采用Python循环来实现上述分析过程非常简单和直观:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import urllib.request
import spacy

with urllib.request.urlopen('https://raw.githubusercontent.com/pytorch/examples/master/word_language_model/data/wikitext-2/valid.txt') as response:
 text = response.read()
nlp = spacy.load('en')
doc_list = list(nlp(text[:800000].decode('utf8')) for i in range(10))

这段代码至少需要运行 1.4 秒才能获得答案。如果我们的数据集中包含有数以百万计的文档,为了获得答案,我们也许需要花费超过一天的时间。

我们也许能够采用多线程来实现加速,但是在Python中这种做法并不是那么明智,因为你还需要处理全局解释器锁(GIL)在Cython中可以无视GIL的存在而尽情使用线程加速。但不能再使用Python中的字典和列表,因为Python中的变量都自动带了锁(GIL)。还好Cython已经封装了C++标准库中的容器:deque,list,map,pair,queue,set,stack,vector。完全可以替代Python的dict, list, set等。

我们使用Cython就可以解决这个,但不能再使用Python中的字典和列表,因为Python中的变量都自动带了锁(GIL)。还好Cython已经封装了C++标准库中的容器:deque,list,map,pair,queue,set,stack,vector。完全可以替代Python的dict, list, set等。

另外请注意,Cython也可以使用多线程!Cython在后台可以直接调用OpenMP。

https://cython.readthedocs.io/en/latest/src/userguide/parallelism.html?source=post_page---------------------------

现在让我们尝试使用spaCy和Cython来加速 Python 代码。

首先需要考虑好数据结构,我们需要一个C类型的数组来存储数据,需要指针来指向每个文档的 TokenC 数组。我们还需要将测试字符(「run」和「NN」)转成 64 位哈希码。

当所有需要处理的数据都变成了C类型对象,我们就可以以纯C语言的速度对数据集进行迭代。

以下是被转换成Cython和spaCy的实现:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
%%cython -+
import numpy
from cymem.cymem cimport Pool
from spacy.tokens.doc cimport Doc
from spacy.typedefs cimport hash_t
from spacy.structs cimport TokenC

cdef struct DocElement:
 TokenC* c
 int length

cdef int fast_loop(DocElement* docs, int n_docs, hash_t word, hash_t tag):
 cdef int n_out = 0
 for doc in docs[:n_docs]:
 for c in doc.c[:doc.length]:
 if c.lex.lower == word and c.tag == tag:
 n_out += 1
 return n_out

def main_nlp_fast(doc_list):
 cdef int i, n_out, n_docs = len(doc_list)
 cdef Pool mem = Pool()
 cdef DocElement* docs = <DocElement*>mem.alloc(n_docs, sizeof(DocElement))
 cdef Doc doc
 for i, doc in enumerate(doc_list):
 docs[i].c = doc.c
 docs[i].length = (<Doc>doc).length
 word_hash = doc.vocab.strings.add('run')
 tag_hash = doc.vocab.strings.add('NN')
 n_out = fast_loop(docs, n_docs, word_hash, tag_hash)

在Jupyter notebook上,这段Cython代码运行了大概20毫秒,比之前的纯Python循环快了大概 80倍。

使用Jupyter notebook单元编写模块的速度很可观,它可以与其它 Python 模块和函数自然地连接:在 20 毫秒内扫描大约 170 万个单词,这意味着我们每秒能够处理高达 8 千万个单词。

如果你已经了解C语言,Cython还允许访问C代码,而Cython的创建者还没有为这些代码添加现成的声明。例如,使用以下代码,可以为C函数生成Python包装器并将其添加到模块dict中。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
%%cython
cdef extern from "math.h":
     cpdef double sin(double x)

Cython注意的坑

1、.pyx中用CDEF定义的东西,除类以外对的.py都是不可见的。

2、.c中是不能操作C类型的,如果想在.py中操作C类型就要在.pyx中从python对象转成C类型或者用含有set / get方法的C类型包裹类。

3、虽然Cython能对Python的str和C的“char *”之间进行自动类型转换,但是对于“char a [n]”这种固定长度的字符串是无法自动转换的。需要使用Cython的libc.string .strcpy进行显式拷贝。

4、回调函数需要用函数包裹,再通过C的“void *”强制转换后才能传入C函数。

Cython相关资料(下载)

0、其他:

https://cython.org/?source=post_page---------------------------

1、官方文档:

2、参考书籍(文末下载):

书籍下载

后台输入(严格大小写)

Cython资料

—End—

量化投资与机器学习微信公众号,是业内垂直于QuantMFECST、AI等专业的主流量化自媒体。公众号拥有来自公募、私募、券商、银行、海外等众多圈内10W+关注者。每日发布行业前沿研究成果和最新量化资讯。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-07-28,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 量化投资与机器学习 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
教程 | 比Python快100倍,利用spaCy和Cython实现高速NLP项目
相关 Jupyter Notebook 地址:https://github.com/huggingface/100-times-faster-nlp
机器之心
2018/07/26
2.1K0
教程 | 比Python快100倍,利用spaCy和Cython实现高速NLP项目
老司机都开火箭了!Cython 助力 Python NLP 实现百倍加速
在去年我们发布了用 Python 实现的基于神经网络的相互引用解析包(Neural coreference resolution package)之后,在社区中获得了惊人数量的反馈,许多人开始将该解析包用到各种各样的应用中,有一些应用场景甚至已经超出了我们原本设计的对话框用例(Dialog use-case)。
AI研习社
2018/07/26
1.5K0
老司机都开火箭了!Cython 助力 Python NLP 实现百倍加速
Cython编译报错“numpy/arrayobject.h: No such file or directory”解决方案
Cython是用来加速Python程序性能的一个工具,其基本使用逻辑就是将类Python代码(*.pyx扩展格式)编译成
DechinPhy
2024/06/27
2800
让Python提速超过30倍的必杀技:Cython
人工智能最火的语言,自然是被誉为迄今为止最容易使用的代码之一的Python。Python代码素来以直观、高可读性著称。
CDA数据分析师
2019/08/05
1.3K0
让Python提速超过30倍的必杀技:Cython
Cython入门到放弃(二)
上一篇文章讲了Cython的使用流程,没有具体展开讲别的,例子也很简单。今天首先使用一个官方文档上使用Cython的例子,然后抛出几个连续的小demo,看看Cython是如何一步一步加速的。
钱塘小甲子
2019/01/28
8480
提速30倍!这个加速包让Python代码飞起来
Python是社区里最受喜爱的编程语言!它是目前为止最易使用的语言,因为它的代码短小精悍,符合人们的思维方式,也符合人们的阅读习惯。
大数据文摘
2019/08/03
1K0
怎样使用Cython提升Python的性能
Python是一门易学易用的编程语言,因此在许多领域都有广泛的应用。然而,Python的执行速度相比于C、C++等编译语言通常会慢一些。在某些对计算性能有高要求的领域,如科学计算、数据处理等,这个问题就变得尤为重要。为了解决这个问题,一种名为Cython的语言应运而生。
阿珍
2024/06/17
2000
怎样使用Cython提升Python的性能
如何用Python和Cython加速NumPy数组操作?
在进行科学计算或数据分析时,NumPy数组是一种常用的数据结构。然而,随着数据规模的增大和运算的复杂化,NumPy的计算性能有时无法满足高效处理的需求。在这种情况下,使用Cython可以显著提升NumPy数组的运算效率。
sergiojune
2024/10/25
2320
如何用Python和Cython加速NumPy数组操作?
使用Cython将Python代码转为C语言,从而提高代码保密性
Cython是Python编程语言和扩展 Cython 编程语言(基于Pyrex)的优化静态编译器。 它使得为 Python 编写 C 扩展就像 Python 本身一样容易。这允许编译器从 Cython 代码生成C代码。 显而易见的是,它能将python代码翻译为C代码,然后生成符合Python/C API的动态链接库。这样就能更好的保护你的python源码不被破解。例如你的代码包含了核心的量化交易策略。将其转为机器语言才能更好的保护你的核心代码。另外一方面,Cython也带来了一些扩展,使得你可以通过添加静态类型声明,将原本的python代码的性能逼近纯C语言的性能。
zy010101
2022/09/21
5.5K0
提升 Python 性能 - Numba 与 Cython
花下猫语:最近,读者微信群里又频繁聊到了 Python 的性能问题,这真是老生常谈了。我想起自己曾收藏过几篇关于如何提升性能的文章,似乎挺有帮助的,便去联系了下作者,现在已经取得转载授权啦。今天分享其中一篇,后续还会有其它相关分享,希望对读者们也有所帮助。
Python猫
2019/08/08
1.2K0
提升 Python 性能 - Numba 与 Cython
教你用 Cython 自己造轮子
我们要造的轮子是一个最简单的栈的实现,用 C/C++来编写能够减小不必要的开销,带来显著的加速。
Python中文社区
2018/07/26
1.1K0
开源贡献代码之​探索一下CPython
本篇文章将会围绕最近给Apache提的一个feature为背景,展开讲讲CPython遇到的问题,以及尝试自己从0写一个库出来,代码也已经放星球了,感兴趣的同学可以去下载学习。
公众号guangcity
2024/04/22
1270
开源贡献代码之​探索一下CPython
NumPy 秘籍中文第二版:九、使用 Cython 加速代码
Cython 是基于 Python 的相对年轻的编程语言。 它允许编码人员将 C 的速度与 Python 的功能混合在一起。 与 Python 的区别在于我们可以选择声明静态类型。 许多编程语言(例如 C)具有静态类型,这意味着我们必须告诉 C 变量的类型,函数参数和返回值类型。 另一个区别是 C 是一种编译语言,而 Python 是一种解释语言。 根据经验,可以说 C 比 Python 更快,但灵活性更低。 通过 Cython 代码,我们可以生成 C 或 C++ 代码。 之后,我们可以将生成的代码编译为 Python 扩展模块。
ApacheCN_飞龙
2023/04/17
8530
NumPy 秘籍中文第二版:九、使用 Cython 加速代码
Python中的cython介绍
Cython是一种用于将Python代码转换为C或C++代码的编译器。它是Python和C/C++之间的一种桥梁,可以提供更高的执行效率和更好的性能。Cython既是一种编程语言,也是一种编译器,它可以将Python代码转换为C或C++代码,并在编译时将其转换为机器码,以提高代码的执行速度。
大盘鸡拌面
2023/10/12
7400
Cython优化Python代码
使用 Cython 优化 Python 代码是一种常见的方式,通过将 Python 代码转译为 C 并编译,可以显著提高性能。Cython 是 Python 的超集,允许在代码中直接使用 C 类型声明,从而加速计算密集型任务。
华科云商小徐
2024/12/02
1270
Cython入门到放弃(一)
        python作为一门强大的脚本语言,优势自然不必说,目前中低频的量化投资基本都是使用python作为research和production作为语言。但是,当我们的模型较复杂,运算量较大的时候,python的短板就会出现,就是运算速度慢。
钱塘小甲子
2019/01/28
3K0
加速Python代码的秘密武器,探索Cython的秘密
首先和大家明确一下这个Cython单词的读法,这个单词Cython以前我也不知道怎么读,老后面要用到这个包的时候,老是不清楚读法,才去搜了下,这个单词是读"赛森",就是前面的cy是读"赛",后面的读法和python后一个读音thon一样。
一点sir
2024/02/26
2490
加速Python代码的秘密武器,探索Cython的秘密
用Cython加速Python代码,快到起飞!
如果您曾经用Python编写过代码,那么等待某些代码块执行的时间可能比您希望的要长。虽然有一些方法可以提高代码的效率,但它很可能仍然比C代码慢。这主要归结为一个事实:Python是一种动态编程语言,它将许多东西移动到C在编译期间负责的运行时。
Python数据科学
2019/07/30
8290
用Cython加速Python代码,快到起飞!
Cython入门
setup这一年也是遇到了很多次,随着python编程学习的不断深度对于python的了解也不断在增加,这里做一次简单的小节。
狼啸风云
2019/11/03
2.1K0
常见 Python 代码优化技巧
代码优化能够让程序运行更快,它是在不改变程序运行结果的情况下使得程序的运行效率更高,根据 80/20 原则,实现程序的重构、优化、扩展以及文档相关的事情通常需要消耗 80% 的工作量。优化通常包含两方面的内容:减小代码的体积,提高代码的运行效率。
昱良
2019/08/06
1.2K0
相关推荐
教程 | 比Python快100倍,利用spaCy和Cython实现高速NLP项目
更多 >
领券
💥开发者 MCP广场重磅上线!
精选全网热门MCP server,让你的AI更好用 🚀
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验