Cython 中如何调用 c++ 的模板库?

2020-12-02 01:06:39 +08:00
 black11black

如题,最近有一个大量用到 python 中字典和列表寻址特性的科学计算函数,初步估算了一下循环次数在十亿级,纯 python 跑的非常慢,换到 pypy 以后加速快了三倍左右,但还是要等一分钟才能出结果。

于是想到能不能用 cython 加速,我以前 cython 都是了解皮毛,只跑过 helloworld 。 今天初步研究了一下感觉可行,按我的想法,因为要大量使用 python 列表,其加速方式大概就是要映射到 cpp 的 vector 了吧

在引入 stl 时遇到了无法引入的问题,有没有带佬指点一下哪错了

# 按我的理解安装 cython 时应该已经自带转换好的 stl 文件了吧
# 以下这段 C 库的引入是可以正常编译的
import cython
from libc.stdlib cimport atoi
from libc.string cimport memset

# 但是这里引入 CPP 的话却会报错
from libcpp.vector cimport vector

另外求问一下关于 python 字典的映射方式,在 cython 中应该怎么处理。比如现在有一个长度不确定的字典 name_dict,估测长度在一百万左右, 我需要大量使用诸如 name_dict['tom'] , name_dict['sam'] 这类字符串寻址来搜寻具体对象,对象内容不复杂,可以映射为结构体,但是这个字典该怎么搞?

谢谢大家

3016 次点击
所在节点    Python
25 条回复
Tony042
2020-12-02 03:44:27 +08:00
考虑下 pybind11 来进行 python 和 C++交互?
lovestudykid
2020-12-02 04:40:20 +08:00
应该是 cimport cython 吧,报错也不说什么错这怎么 debug
lovestudykid
2020-12-02 04:42:50 +08:00
我记得直接用 Python 原生的 dict 是比较快的,用 cpp 的 map 不一定快,也可能是我哪里没弄对
black11black
2020-12-02 05:21:48 +08:00
@lovestudykid 兄,一共就四行 import,这还用再贴一下报错报了什么么。。。再说 cython 咋看报错信息啊,又没有解释器,我都是编译过程中看报错。
black11black
2020-12-02 05:23:22 +08:00
@Tony042 是这样,现在情况是有一段 python 代码,预研一下 cython 如果合适的话准备改成 cython,看了一下你说的这个项目似乎是设计用来在已经有 cpp 代码的情况下接入 py 的
lovestudykid
2020-12-02 05:58:09 +08:00
@black11black 因为 code 并没有问题,编译器的报错也是报错。你没设置 language = c++?
black11black
2020-12-02 06:02:22 +08:00
@lovestudykid 感谢,是 c++声明的问题,一楼贴条里写了。另外出现了新的问题,带佬看看
lovestudykid
2020-12-02 06:23:01 +08:00
@black11black 你可以试试 long long
lovestudykid
2020-12-02 06:24:42 +08:00
BTW, 这是 c 和 gcc 环境的问题,跟 cython 没关系。在我这里没问题
black11black
2020-12-02 06:59:00 +08:00
@lovestudykid OK,可能是 64 位 python 用了 32 位编译器问题,大概吧。应该怎么修正呢? cython 通过 pip 装的,我不知道他用什么方法调用的编译器
lovestudykid
2020-12-02 07:05:44 +08:00
@black11black 直接从你 PATH 调用的,也可以在 setup.py 制定,看 cython 文档
xuboying
2020-12-02 12:19:18 +08:00
楼主能不能顺带测测 numba 库的效率?

个人感觉纯计算用了 jit 技术以后应该不会有太大的差别了吧?
black11black
2020-12-02 12:25:28 +08:00
@lovestudykid 带佬指点一下怎么调,我在文档里搜索 compiler 相关的内容没看见能设置的选项,主要是这篇 https://cython.readthedocs.io/en/latest/src/userguide/source_files_and_compilation.html
black11black
2020-12-02 12:36:22 +08:00
@xuboying numba 有些黑魔法,不太喜欢用,以前测试过一些场景比纯 C 语言运行还快,不明原因。我这个环境里 jit 和 C 还是有比较大速度差距的,pypy 感觉在一些结构的实现上效率跟原生没啥区别,比如这种大字典寻址的
black11black
2020-12-02 12:38:14 +08:00
另外按照楼上老哥说的用 long long 类型以后确实程序能正常运行了。我测了一下,cython 里是有 sizeof 这个 bif 的,我测 void *p 是 8bit,而 long 是 4bit,整个人都是懵的。
black11black
2020-12-02 13:16:35 +08:00
遍地采坑啊。

又遇到一个问题,比如 python 当中,在循环过程中新建对象是个很正常的操作,比如下面这样

class A:

pass

lst = list()

for i in range(10):

lst.append(A())

但是在 cython 里并不能在循环内部进行 cdef,所以现在又卡了,不会循环新建对象。
mckelvin
2020-12-02 13:27:07 +08:00
不要用写 Python 的思想去写 C++, 先想办法定义好胶水层的接口参数类型,一般不建议 vector 直接对应 list. 因为 list 里的内存无法直接变成 vector 可用的内存,不得不有内存拷贝发生。建议用朴素一些的数据结构,比如 double* . 数值计算大部分情况下 numpy 和 scipy 已经够用了,他们已经封装好了一些底层 C/C++实现的功能。

数值不对这个问题大概率是溢出了。有些 64 位操作系统里 long 是 32bit 的。建议用 int64_t 这样的类型,这样明确它是 64bit 长度。

```
In [16]: (1 + 1000000) / 2 * 1000000
Out[16]: 500000000000

In [17]: 1 << 32
Out[17]: 4294967296
```
qbqbqbqb
2020-12-02 13:34:27 +08:00
@black11black long 这个确实是个坑,64 位 Linux 里是 8 字节的,但 64 位 Windows 里是 4 字节的
xuboying
2020-12-02 17:26:16 +08:00
@black11black #14 我以前遇到过 numba 的坑是依赖了他的类型推测导致执行不稳定,不知道这个是不是你提到的“黑魔法”,后来我去官网研究了他的形参类型声明以后,问题就完全解决了。如果这些你已经知道了,就忽略我的留言好了。btw,毕竟我没有看过汇编代码,了解程度就到这一层了。在我的图形计算的应用里效果还是相当让我满意的。
wevsty
2020-12-02 17:30:13 +08:00
cpp 的 std::vector 是模板,模板本身只是个源码不使用的话本身并没有实例,你当然不可能用 python 直接调模板的源码。
想要用 std::vector 的话,只能你自己用 C 封装一套函数出来。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/731186

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX