投入时间阅读 Cython 的文档并不是浪费。它将帮助更好地理解如何使用 Cython 以及如何优化代码。如果打算在 MacOS 上使用 OpenMP,请注意系统自带的 clang 编译器并不支持 OpenMP。可以通过 conda-forge 安装支持 OpenMP 的编译器包。激活检查可能有所帮助,例如激活 boundscheck,可以使用以下命令:
export SKLEARN_ENABLE_DEBUG_CYTHON_DIRECTIVES=1
从零开始的笔记本
在 Jupyter Notebook 中从零开始,可以帮助理解如何使用 Cython 并快速获得对工作的反馈。如果计划在 Jupyter Notebook 中使用OpenMP,需要在 Cython magic 中添加额外的编译器和链接器参数。例如,对于 GCC 和 clang,可以使用以下命令:
%%cython --compile-args=-fopenmp --link-args=-fopenmp
对于 Microsoft 的编译器,可以使用:
%%cython --compile-args=/openmp --link-args=/openmp
调试 C 代码
调试 C 代码(例如段错误)时,可以使用 gdb。例如:
gdb --ex r --args python ./entrypoint_to_bug_reproducer.py
在 cdef(nogil)上下文中,如果需要访问某些值以进行调试,可以使用:
with gil: print(state_to_print)
请注意,Cython 不能解析带有 {var=} 表达式的 f-strings,例如:
print(f"{test_val=}")
scikit-learn代码库
scikit-learn的代码库中有许多非统一(融合)类型的(重新)定义。目前,正在进行工作以简化和统一整个代码库中的类型定义。目前,请确保理解最终使用的是哪些具体类型。
性能优化
理解 CPython 上下文中的 GIL(全局解释器锁),以及何时 Cython 将映射到没有与 CPython 交互的 C 代码,何时不会,以及何时不能(例如,存在与 Python 对象的交互,包括函数)。在这种情况下,PEP 073 提供了一个很好的概览和上下文以及移除的途径。确保已经停用了检查。
使用 OpenMP
由于scikit-learn可以不使用 OpenMP 构建,因此需要保护每个直接调用 OpenMP 的操作。在 sklearn/utils/_openmp_helpers.pyx 中提供的 _openmp_helpers 模块提供了受保护的 OpenMP 例程版本。要使用 OpenMP 例程,必须从这个模块而不是直接从 OpenMP 库中 cimport:
from sklearn.utils._openmp_helpers cimport omp_get_max_threads
max_threads = omp_get_max_threads()
并行循环 prange 已经由 Cython 保护,可以直接从 cython.parallel 中使用。
from sklearn.utils._typedefs cimport float32, float64