Лучшие практики, соглашения и знания Cython#
Этот документ содержит советы по разработке кода на Cython в scikit-learn.
Советы по разработке с использованием Cython в scikit-learn#
Советы для облегчения разработки#
Время, затраченное на чтение Документация Cython не является потерянным временем.
Если вы планируете использовать OpenMP: В MacOS, системное распространение
clangне реализует OpenMP. Вы можете установитьcompilersпакет доступен наconda-forgeкоторый поставляется с реализацией OpenMP.Активация проверки может помочь. Например, для активации boundscheck используйте:
export SKLEARN_ENABLE_DEBUG_CYTHON_DIRECTIVES=1
Начните с нуля в блокноте чтобы понять, как использовать Cython и быстро получить обратную связь по вашей работе. Если вы планируете использовать OpenMP для ваших реализаций в вашем Jupyter Notebook, добавьте дополнительные аргументы компилятора и линкера в магии 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-кода (например, segmentation fault) используйте
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 содержит множество не унифицированных (слитых) определений типов. В настоящее время существует текущая работа по упрощению и унификации этого в кодовой базе. Пока убедитесь, что вы понимаете, какие конкретные типы используются в конечном итоге.
Этот псевдоним может быть удобен для компиляции отдельных расширений 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 должны быть максимально избегаемы в вычислительно интенсивных разделах алгоритмов. Для получения дополнительной информации обратитесь к этот раздел руководства по Cython# This generates a HTML report (`source.html`) for `source.c`. cythonX --annotate source.pyx
Советы по производительности#
Понять GIL в контексте CPython (какие проблемы он решает, каковы его ограничения) и получить хорошее понимание того, когда Cython будет преобразован в код C без взаимодействия с CPython, когда не будет, и когда не может (например, наличие взаимодействий с объектами Python, которые включают функции). В этом отношении, PEP073 предоставляет хороший обзор, контекст и пути для удаления.
Убедитесь, что вы отключили проверки.
Всегда предпочитайте memoryviews вместо
cnp.ndarrayкогда возможно: memoryviews являются легковесными.Избегайте срезов memoryview: срезы memoryview могут быть затратными или вводящими в заблуждение в некоторых случаях, и лучше не использовать их, даже если обработка меньшего количества измерений в некоторых контекстах была бы предпочтительнее.
Декорируйте финальные классы или методы с помощью
@final(это позволяет удалять виртуальные таблицы при необходимости)Встраивайте методы и функции, когда это имеет смысл
В сомнении, прочитайте сгенерированный код на C или C++, если можете: "Чем меньше инструкций C и косвенных обращений для строки кода на Cython, тем лучше" — хорошее правило.
nogilобъявления являются лишь подсказками: при объявленииcdefфункции как nogil означают, что их можно вызывать без удержания GIL, но они не освобождают GIL при входе в них. Вы должны сделать это самостоятельно, либо передавnogil=Truetocython.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
Этот пункт основан на этот комментарий от Stéfan’s Benhel
Прямые вызовы подпрограмм BLAS возможны через интерфейсы, определенные в
sklearn.utils._cython_blas.
Использование OpenMP#
Поскольку scikit-learn может быть собран без OpenMP, необходимо защищать каждый прямой вызов OpenMP.
The _openmp_helpers модуль, доступный в
sklearn/utils/_openmp_helpers.pyx
предоставляет защищенные версии процедур OpenMP. Чтобы использовать процедуры OpenMP, они
должны быть cimported из этого модуля, а не из библиотеки OpenMP напрямую:
from sklearn.utils._openmp_helpers cimport omp_get_max_threads
max_threads = omp_get_max_threads()
Параллельный цикл, prange, уже защищена cython и может использоваться напрямую
из cython.parallel.
Типы#
Код на Cython требует использования явных типов. Это одна из причин, по которой вы получаете прирост производительности. Чтобы избежать дублирования кода, у нас есть центральное место для наиболее часто используемых типов в
sklearn/utils/_typedefs.pxd. В идеале вы начинаете с просмотра там и cimport типы, которые вам нужны, например
from sklearn.utils._typedefs cimport float32, float64