Cython 튜토리얼 : 파이썬 속도를 높이는 방법

Python은 배우기 쉽고 작업하기 쉬운 강력한 프로그래밍 언어이지만 실행하기가 항상 빠르지는 않습니다. 특히 수학이나 통계를 다룰 때 더욱 그렇습니다. C 라이브러리를 래핑하는 NumPy와 같은 타사 라이브러리는 일부 작업의 성능을 크게 향상시킬 수 있지만 때로는 Python에서 직접 C의 원시 속도와 성능이 필요합니다.

Cython은 Python 용 C 확장을보다 쉽게 ​​작성하고 기존 Python 코드를 C로 변환 할 수 있도록 개발되었습니다. 게다가 Cython을 사용하면 최적화 된 코드를 외부 종속성없이 Python 애플리케이션과 함께 제공 할 수 있습니다.

이 튜토리얼에서는 기존 Python 코드를 Cython으로 변환하고 프로덕션 애플리케이션에서 사용하는 데 필요한 단계를 안내합니다.

관련 비디오 : Cython을 사용하여 Python 속도 향상

Cython 예제

적분 함수의 그다지 효율적이지 않은 구현 인 Cython의 문서에서 가져온 간단한 예제부터 시작하겠습니다.

정의 f (x) :

    x ** 2-x 반환

def integration_f (a, b, N) :

    s = 0

    dx = (ba) / N

    범위 (N)에있는 i의 경우 :

        s + = f (a + i * dx)

    s * dx 반환

코드는 읽고 이해하기 쉽지만 느리게 실행됩니다. 이는 Python이 자체 객체 유형과 기계의 원시 숫자 유형간에 지속적으로 앞뒤로 변환해야하기 때문입니다.

이제 Cython의 추가 사항이 강조 표시된 동일한 코드의 Cython 버전을 고려하십시오.

 cdef f (더블 x) :

    x ** 2-x 반환

def integration_f (double a, double b, int N) :

    cdef int i

    cdef double s, x, dx

    s = 0

    dx = (ba) / N

    범위 (N)에있는 i의 경우 :

        s + = f (a + i * dx)

    s * dx 반환

이러한 추가를 통해 코드 전체에서 변수 유형을 명시 적으로 선언 할 수 있으므로 Cython 컴파일러는 이러한 "장식 된"추가를 C로 변환 할 수 있습니다. 

관련 비디오 : Python이 프로그래밍을 더 쉽게 만드는 방법

IT에 완벽한 Python은 시스템 자동화에서 기계 학습과 같은 최첨단 분야에서의 작업에 이르기까지 다양한 종류의 작업을 단순화합니다.

Cython 구문

Cython 코드를 장식하는 데 사용되는 키워드는 기존 Python 구문에서 찾을 수 없습니다. Cython을 위해 특별히 개발 되었기 때문에이 코드로 장식 된 코드는 기존의 Python 프로그램으로 실행되지 않습니다.

다음은 Cython 구문의 가장 일반적인 요소입니다.

변수 유형

사이 썬에 사용 된 변수 유형 중 일부는 다음과 같은 파이썬의 자신의 종류의 메아리이다  int, float하고 long. 다른 사이 썬 변수 유형도 C에서 발견되는, 같은 charstruct같은 같은 선언입니다 unsigned long. 그리고 다른 것들은 bintPython True/False값 의 C 레벨 표현 과 같이 Cython에 고유 합니다.

cdefcpdef기능 유형

cdef키워드는 사이 썬 또는 C 형의 사용을 나타냅니다. 또한 Python에서와 같이 함수를 정의하는데도 사용됩니다.

Python의 def키워드를 사용하여 Cython으로 작성된 함수 는 다른 Python 코드에 표시되지만 성능이 저하됩니다. cdef키워드 를 사용하는 함수는 다른 Cython 또는 C 코드에서만 볼 수 있지만 훨씬 빠르게 실행됩니다. Cython 모듈 내에서 내부적으로 만 호출되는 함수가있는 경우 cdef.

세 번째 키워드 cpdef는 C 코드가 선언 된 함수에 최고 속도로 액세스 할 수 있도록 Python 코드 및 C 코드와의 호환성을 제공합니다. 하지만이 편리함은 비용이 듭니다.  cpdef함수는 더 많은 코드를 생성하고 cdef.

다른 Cython 키워드

Cython의 다른 키워드는 Python에서 사용할 수없는 프로그램 흐름 및 동작의 측면을 제어합니다.

  • gilnogil. 이들은 Python의 Global Interpreter Lock 또는 GIL이 with gil:필요하거나 ( with nogil:) 필요하지 않은 ( ) 코드 섹션을 설명하는 데 사용되는 컨텍스트 관리자 입니다. Python API를 호출하지 않는 C 코드는 nogil특히 네트워크 연결에서 읽는 것과 같은 장기 실행 작업을 수행하는 경우 블록 에서 더 빠르게 실행될 수 있습니다 .
  • cimport이것은 Cython이 C 데이터 유형, 함수, 변수 및 확장 유형을 가져 오도록 지시합니다. 예를 들어 NumPy의 네이티브 C 모듈을 사용하는 Cython 앱은 cimport이러한 기능에 액세스하는 데 사용합니다.
  • include. 이것은 C에서와 거의 같은 방식으로 한 Cython 파일의 소스 코드를 다른 파일 안에 배치합니다 include.
  • ctypedef. Used to refer to type definitions in external C header files.
  • extern. Used with cdef to refer to C functions or variables found in other modules.
  • public/api. Used to make declarations in Cython modules that will be visible to other C code.
  • inline. Used to indicate a given function should be inlined, or have its code placed in the body of the calling function whenever it is used, for the sake of speed. For instance, the f function in the above code example could be decorated with inline to reduce its function call overhead, because it is only used in one place. (Note that the C compiler might perform its own inlining automatically, but inline lets you specify explicitly if something should be inlined.)

It is not necessary to know all of the Cython keywords in advance. Cython code tends to be written incrementally—first you write valid Python code, then you add Cython decoration to speed it up. Thus you can pick up Cython’s extended keyword syntax piecemeal, as you need it.

Compile Cython

Now that we have some idea of what a simple Cython program looks like and why it looks the way it does, let’s walk through the steps needed to compile Cython into a working binary.

To build a working Cython program, we will need three things:

  1. The Python interpreter. Use the most recent release version, if you can.
  2. The Cython package. You can add Cython to Python by way of the pip package manager: pip install cython
  3. A C compiler.

Item #3 can be tricky if you’re using Microsoft Windows as your development platform. Unlike Linux, Windows doesn’t come with a C compiler as a standard component. To address this, grab a copy of Microsoft Visual Studio Community Edition, which includes Microsoft’s C compiler and costs nothing. 

Note that, as of this writing, the most recent release version of Cython is 0.29.16, but a beta version of Cython 3.0 is available for use. If you use pip install cython, the most current non-beta version will be installed. If you want to try out the beta, use pip install cython>=3.0a1 to install the most recent edition of the Cython 3.0 branch. Cython’s developers recommend trying the Cython 3.0 branch whenever possible, because in some cases it generates significantly faster code.

Cython programs use the .pyx file extension. In a new directory, create a file named num.pyx that contains the Cython code example shown above (the second code sample under “A Cython example”) and a file named main.py that contains the following code:

from num import integrate_f

print (integrate_f(1.0, 10.0, 2000))

This is a regular Python progam that will call the integrate_f function found in num.pyx. Python code “sees” Cython code as just another module, so you don’t need to do anything special other than import the compiled module and run its functions.

Finally, add a file named setup.py with the following code:

from distutils.core import setup from distutils.extension import Extension from Cython.Build import cythonize ext_modules = [ Extension( r'num', [r'num.pyx'] ), ] setup( name="num", ext_modules=cythonize(ext_modules),

)

setup.py is normally used by Python to install the module it’s associated with, and can also be used to direct Python to compile C extensions for that module. Here we’re using setup.py to compile Cython code.

If you’re on Linux, and you have a C compiler installed (typically the case), you can compile the .pyx file to C by running the command: 

python setup.py build_ext --inplace

If you’re using Microsoft Windows and Microsoft Visual Studio 2017 or better, you’ll need to make sure you have the most recent version of setuptools installed in Python (version 46.1.3 as of this writing) before that command will work. This ensures that Python’s build tools will be able to auto-detect and use the version of Visual Studio you have installed.

If the compilation is successful, you should see new files appear in the directory: num.c (the C file generated by Cython) and a file with either a .o extension (on Linux) or a .pyd extension (on Windows). That’s the binary that the C file has been compiled into. You may also see a \build subdirectory, which contains the artifacts from the build process.

Run python main.py, and you should see something like the following returned as a response:

283.297530375

That’s the output from the compiled integral function, as invoked by our pure Python code. Try playing with the parameters passed to the function in main.py to see how the output changes.

Note that whenever you make changes to the .pyx file, you will need to recompile it. (Any changes you make to conventional Python code will take effect immediately.)

The resulting compiled file has no dependencies except the version of Python it was compiled for, and so can be bundled into a binary wheel. Note that if you refer to other libraries in your code, like NumPy (see below), you will need to provide those as part of the application’s requirements.

How to use Cython

Now that you know how to “Cythonize” a piece of code, the next step is to determine how your Python application can benefit from Cython. Where exactly should you apply it?

For best results, use Cython to optimize these kinds of Python functions:

  1. Functions that run in tight loops, or require long amounts of processing time in a single “hot spot” of code.
  2. Functions that perform numerical manipulations.
  3. Functions that work with objects that can be represented in pure C, such as basic numerical types, arrays, or structures, rather than Python object types like lists, dictionaries, or tuples.

Python has traditionally been less efficient at loops and numerical manipulations than other, non-interpreted languages. The more you decorate your code to indicate it should use base numerical types that can be turned into C, the faster it will do number-crunching.

Using Python object types in Cython isn’t itself a problem. Cython functions that use Python objects will still compile, and Python objects may be preferable when performance isn’t the top consideration. But any code that makes use of Python objects will be limited by the performance of the Python runtime, as Cython will generate code to directly address Python’s APIs and ABIs.

Another worthy target of Cython optimization is Python code that interacts directly with a C library. You can skip the Python “wrapper” code and interface with the libraries directly.

However, Cython does not automatically generate the proper call interfaces for those libraries. You will need to have Cython refer to the function signatures in the library’s header files, by way of a cdef extern from declaration. Note that if you don’t have the header files, Cython is forgiving enough to let you declare external function signatures that approximate the original headers. But use the originals whenever possible to be safe.

One external C library that Cython can use right out of the box is NumPy. To take advantage of Cython’s fast access to NumPy arrays, use cimport numpy (optionally with as np to keep its namespace distinct), and then use cdef statements to declare NumPy variables, such as cdef np.array or np.ndarray.

Cython profiling

The first step to improving an application’s performance is to profile it—to generate a detailed report of where the time is being spent during execution. Python provides built-in mechanisms for generating code profiles. Cython not only hooks into those mechanisms but has profiling tools of its own.

Python’s own profiler, cProfile, generates reports that show which functions take up the most amount of time in a given Python program. By default, Cython code doesn’t show up in those reports, but you can enable profiling on Cython code by inserting a compiler directive at the top of the .pyx file with functions you want to include in the profiling:

# cython: profile=True

You can also enable line-by-line tracing on the C code generated by Cython, but this imposes a lot of overhead, and so is turned off by default.

Note that profiling imposes a performance hit, so be sure to toggle profiling off for code that is being shipped into production.

Cython can also generate code reports that indicate how much of a given .pyx file is being converted to C, and how much of it remains Python code. To see this in action, edit the setup.py file in our example and add the following two lines at the top:

import Cython.Compiler.Options

Cython.Compiler.Options.annotate = True

(Alternatively, you can use a directive in setup.py to enable annotations, but the above method is often easier to work with.)

.c프로젝트에서 생성 된 파일을 삭제하고 setup.py스크립트를 다시 실행하여 모든 것을 다시 컴파일하십시오. 완료되면 .pyx 파일의 이름을 공유하는 동일한 디렉토리에 HTML 파일이 표시됩니다 (이 경우  num.html. HTML 파일을 열면 여전히 Python에 의존하는 코드 부분이 노란색으로 강조 표시됩니다. 노란색 영역을 클릭하면 Cython에서 생성 한 기본 C 코드를 볼 수 있습니다.