Cython最佳实践、惯例和知识#
本文档包含在scikit-learn中开发Cython代码的技巧。
在scikit-learn中使用Cython进行开发的技巧#
简化开发的提示#
时间花在阅读 Cython's documentation 不是失去的时间。
如果您打算使用BEP:在MacOS上,系统的分布
clang
不实现BEP。您可以安装compilers
套餐可在conda-forge
它随BEP的实现而附带。激活 checks 可能会有所帮助。例如,用于激活boundscheck用途:
export SKLEARN_ENABLE_DEBUG_CYTHON_DIRECTIVES=1
Start from scratch in a notebook 了解如何使用Cython并快速获取工作反馈。如果您计划在Deliveryter Notebook中使用BEP进行实现,请在Cython魔法中添加额外的编译器和链接器参数。
# For GCC and for clang %%cython --compile-args=-fopenmp --link-args=-fopenmp # For Microsoft's compilers %%cython --compile-args=/openmp --link-args=/openmp
要调试C代码(例如,segfault),请使用
gdb
与:gdb --ex r --args python ./entrypoint_to_bug_reproducer.py
要访问某些可用的值来调试
cdef (nogil)
上下文,用途:with gil: print(state_to_print)
请注意,Cython无法解析f字符串
{var=}
表达,例如print(f"{test_val=}")
scikit-learn代码库有很多非统一(融合)类型(重新)定义。目前有 ongoing work to simplify and unify that across the codebase .目前,请确保您了解最终使用哪些具体类型。
您可能会发现这个别名可以很方便地编译各个Cython扩展:
# You might want to add this alias to your shell script config. alias cythonX="cython -X language_level=3 -X boundscheck=False -X wraparound=False -X initializedcheck=False -X nonecheck=False -X cdivision=True" # This generates `source.c` as if you had recompiled scikit-learn entirely. cythonX --annotate source.pyx
使用
--annotate
带有此标志的选项允许生成代码注释的HTML报告。此报告逐行指示与CPython解释器的交互。在算法的计算密集型部分,必须尽可能避免与CPython解释器的交互。欲了解更多信息,请参阅 this section of Cython's tutorial# This generates a HTML report (`source.html`) for `source.c`. cythonX --annotate source.pyx
性能提示#
在CPython的上下文中了解GIL(它解决了哪些问题,它的局限性有哪些),并很好地了解Cython何时会映射到不与CPython交互的C代码,何时不会,以及何时不能(例如,与Python对象(包括函数)的交互)。在这方面, PEP073 提供了很好的概述、上下文和删除途径。
确保您已停用 checks .
总是更喜欢记忆视图,而不是
cnp.ndarray
如果可能的话:记忆视图是轻量级的。避免内存视图切片:在某些情况下,记忆视图切片可能成本高昂或具有误导性,我们最好不要使用它,即使在某些上下文中处理更少的维度会更好。
装饰最终课程或方法
@final
(this允许在需要时删除虚拟表)当有意义时,内联方法和函数
如果有疑问,请阅读生成的C或C++代码:“一行Cython代码的C指令和间接越少,越好”是一个很好的经验法则。
nogil
声明只是提示:当声明cdef
充当nogil,这意味着可以在不持有GIL的情况下调用它们,但在输入它们时不会释放GIL。你必须亲自完成,要么通过nogil=True
到cython.parallel.prange
显式地,或通过使用显式上下文管理器:cdef inline void my_func(self) nogil: # Some logic interacting with CPython, e.g. allocating arrays via NumPy. with nogil: # The code here is run as if it were written in C. return 0
可以通过中定义的接口直接调用BLAS例程
sklearn.utils._cython_blas
.
使用OpenMP#
由于scikit-learn可以在不使用BEP的情况下构建,因此有必要保护对BEP的每次直接调用。
的 _openmp_helpers
module, available in sklearn/utils/_openmp_helpers.pyx 提供受保护版本的OpenMP例程。要使用OpenMP例程,它们必须 cimported
来自此模块,而不是直接来自BEP库:
from sklearn.utils._openmp_helpers cimport omp_get_max_threads
max_threads = omp_get_max_threads()
平行循环, prange
,已经受到cython保护,可以直接从 cython.parallel
.
类型#
Cython代码需要使用显式类型。这是您获得性能提升的原因之一。为了避免代码重复,我们将中最常用的类型放在了中心位置 sklearn/utils/_typedefs.pyd .理想情况下,你从那里开始看, cimport
您需要的类型,例如
from sklearn.utils._typedefs cimport float32, float64