使用Python/C接口与使用Cython相比有哪些优势?

49
我想通过使用BLAS和LAPACK编写一些C或C++模块来扩展Python和NumPy。我还想将代码作为独立的C/C++库分发。我希望这些库能够同时使用单精度和双精度浮点数。我将编写的一些函数示例包括用于解决线性系统的共轭梯度或加速的一阶方法。有些函数需要从C/C++代码中调用Python函数。
在使用Python/C API和Numpy/C API进行一些尝试后,我发现许多人支持使用Cython代替(例如请参见this questionthis one)。我不是Cython的专家,但似乎对于some cases,您仍然需要使用Numpy/C API并了解它的工作原理。考虑到我已经(略微)了解Python/C API而没有关于Cython的任何知识,我想知道继续使用Python/C API是否有意义,并且如果使用此API是否具有一些优势。未来,我肯定会开发一些与数值计算无关的东西,因此这个问题不仅涉及NumPy。我喜欢Python/C API的一件事是我可以了解一些关于Python解释器如何工作的东西。
谢谢。

如果我没记错的话,BLAS和LAPACK已经集成在scipy中了。 - fabrizioM
注意:您提供的链接 some cases 指向使用 Cython 中的 Numpy/C API 的代码,而不是 C 语言。 - jfs
1
@fabrizioM:是的,numpy和scipy中使用了BLAS和LAPACK。但我想开发一些不在numpy或scipy中的数值算法,并且出于效率原因,我想用C/C++来实现它们。 - Edouard
@J.F. Sebastian:没错。我的观点是,这段代码非常接近使用Numpy/C API编写的C代码:必须了解底层行为(如引用计数)。 - Edouard
3个回答

92

我认为当前的“最佳答案”听起来有点像是FUD。首先,很明显普通开发人员在C语言中编写比NumPy+Cython更快的代码不是显而易见的。相反,将时间投入到在Python环境下正确编写必要的C代码通常比直接在Cython中编写快速原型并进行基准测试、优化、重写和再次基准测试更值得。然后,决定是否需要将手动调优的C代码替换2%的现有代码,并从Cython代码中调用它以获得5-10%的性能提升。

我正在使用Cython编写一个库,目前包含约18K行的Cython代码,这相当于将近200K行的C代码。通过在适当的位置注入大约20行手动调优的C代码,我曾经成功地为一些非常重要的内部基础级函数实现了近25%的加速。这个小部分的改进只花费了我几个小时来重新编写和优化。这与我节省的大量时间相比微不足道,因为我不需要(也不必维护)使用纯C语言编写这个库。

即使你比Cython更擅长C语言,如果你同时掌握Python和C语言,你会很快学会Cython,这样做肯定是值得的,特别是当你要进行数值计算时。你编写的80-95%的代码从在高级语言中编写中受益,你可以放心地将节省下来的一半时间投入到使你的代码与直接在低级语言中编写一样快的优化中。

话虽如此,你提到想要“能够将代码作为独立的C/C++库分发”是坚持使用纯C/C++的一个有效理由。Cython总是依赖于CPython,这是相当大的一个依赖关系。然而,仅使用纯C/C++(除了Python接口)也无法利用NumPy,因为它也依赖于CPython。因此,像通常在C中编写某些东西一样,在实际功能之前,您必须做很多基础工作。在开始这项工作之前,请认真考虑两次。


11

首先,我不太明白您问题中的一个点:

[...] 还希望能够将代码作为独立的C/C++库进行分发。[...] 一些函数需要从C/C++代码调用Python函数。

这应该怎么工作?

接下来,关于您实际的问题,直接使用Python/C API肯定有优势:

  • 大多数情况下,您比编写Cython代码更熟悉编写C代码。

  • 用C编写代码可以给您最大的控制权。要获得与等效C代码相同的性能,您必须非常小心地编写Cython代码。您不仅需要确保声明所有变量的类型,还需要设置一些标志-例如边界检查。您需要对Cython的工作方式有深入了解才能获得最佳性能。

  • Cython代码依赖于Python。编写应该作为独立C库分发的代码似乎不是一个好主意。


1
"这个应该如何工作?"--通常的方法是嵌入Python解释器,对吗?" - Nicholas Knight
@Nicholas:我不知道这是否是OP计划要做的事情。也许需要调用Python函数的函数根本不会成为C库的一部分?希望能有些澄清。(我知道这第一部分更像是评论而不是答案,但既然我正在写答案,为了更好的格式,我把它放在那里了。) - Sven Marnach
你提出的问题确实不是很清楚。我的意思是,我库中的一些函数将以用户定义的函数为参数。Python 用户将在 Python 中定义这些函数,而 C / C++ 用户将在 C / C++ 中使用函数对象代替。这样更清楚吗? - Edouard
此外,与 C 相比,Cython 的类型系统相对贫乏,因此似乎无法提供完全的安全性/可移植性(尽管在 0.11 中终于添加了 size_t)。 - Fred Foo

0
Python/C API 的主要缺点是,如果在内部循环中使用它,它可能非常慢。我发现调用 Python 函数比调用等效的 C++ 函数慢了 80-160 倍。
如果这不影响您的代码,那么您可以从能够编写一些 Python 代码块、访问 Python 库、支持直接编写 Python 回调函数中受益。这也意味着您可以进行一些更改而无需重新编译,使原型设计更加容易。

25
在Python中,你不编写紧凑的循环。你会在C语言中编写紧凑的循环,然后将它们与Python进行接口对接。 - Alexandre C.

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接