用Cython编译写出更快的Python代码

原文地址: http://www.behnel.de/cython200910/talk.html以下为原文


About myself

  • Passionate Python developer since 2002
    • after Basic, Logo, Pascal, Prolog, Scheme, Java, C, ...
  • CS studies in Germany, Ireland, France
  • PhD in distributed systems in 2007
    • Language design for self-organising systems
    • Darmstadt University of Technologies, Germany
  • Current occupations:
    • http://codespeak.net/lxml/
    • IT transformations, SOA design, Java-Development, ...
    • Employed by Senacor Technologies AG, Germany
    • ?lxml? OpenSource XML toolkit for Python
    • ?Cython?

Part 1: Intro to Cython

  • Part 1: Intro to Cython
  • Part 2: Building Cython modules
  • Part 3: Writing fast code
  • Part 4: Talking to other extensions

What is Cython?

Cython is the missing link

between the simplicity of Python

and the speed of C / C++ / Fortran.

What is Cython?

Cython is the missing link

between the simplicity of Python

and the speed of C / C++ / Fortran.

What is Cython?

Cython is

Major Cython Core Developers

  • Robert Bradshaw, Stefan Behnel, Dag Sverre Seljebotn
    • lead developers
  • Lisandro Dalcín
    • C/C++ portability and various feature patches
  • Kurt Smith, Danilo Freitas
    • Google Summer of Code 2009: Fortran/C++ integration
  • Greg Ewing
    • main developer and maintainer of Pyrex
  • many, many others - see

How to use Cython

  • you write Python code
    • Cython translates it into C code
    • your C compiler builds a shared library for CPython
    • you import your module into CPython
  • Cython has support for
    • optionally compile Python code from setup.py!
    • Cython does that for its own modules :-)
    • distutils
    • embedding the CPython runtime in an executable

Example: compiling Python code

# file: worker.pyclass HardWorker(object):    u"Almost Sisyphos"    def __init__(self, task):        self.task = task    def work_hard(self, repeat=100):        for i in range(repeat):            self.task()def add_simple_stuff():
    x = 1+1HardWorker(add_simple_stuff).work_hard()

Example: compiling Python code

  • compile with

    $ cython worker.py
  • translates to ~1500 line .c file (Cython 0.11.3)
    • helps tracing your own code in generated sources
    • different C compilers, Python versions, ...
    • lots of portability #define‘s
    • tons of helpful C comments with Python code snippets
    • a lot of code that you don‘t want to write yourself

Portable Code

  • Cython compiler generates C code that compiles
    • with all major compilers (C and C++)
    • on all major platforms
    • in Python 2.3 through 3.1
  • Cython language syntax follows Python 2.6
    • get involved to get it quicker!
    • optional Python 3 syntax support is on TODO list

... the fastest way to port Python 2 code to Py3 ;-)

Python language feature support

  • most of Python 2 syntax is supported
    • top-level classes and functions
    • control structures: loops, with, try-except/finally, ...
    • object operations, arithmetic, ...
  • plus many Py3 features:
    • list/set/dict comprehensions
    • keyword-only arguments
    • extended iterable unpacking (a,b,*c,d = some_list)

Python features in work

  • Inner functions with closures

    def factory(a,b):    def closure_function(c):        return a+b+c    return closure_function
    • status: (hopefully) to be merged for 0.12

Planned Cython features

  • improved C++ integration (GSoC 2009)
    • e.g. function/operator overloading support
    • status: mostly there, to be finished and integrated
  • improved Fortran integration (GSoC 2009)
    • talking to Fortan code directly
    • status: mostly there, to be finished and integrated
  • native array data type with SIMD behaviour
    • status: large interest, implementation pending

... as usual: great ideas, little time

Currently unsupported

  • local/inner classes (~open)
  • lambda expressions (~easy)
  • generators (~needs work)
  • generator expressions (~easy)
    • with obvious optimisations, e.g.

      set( x.a for x in some_list )== { x.a for x in some_list }

... all certainly on the TODO list for 1.0.

Speed

Cython generates very efficient C code:

  • PyBench: most benchmarks run 20-80% faster
    • conditions and loops run 5-8x faster than in Py2.6.2
    • overall about 30% faster for plain Python benchmark
    • obviously, real applications are different
  • PyPy‘s richards.py benchmark:
    • heavily class based scheduler
    • 20% faster than CPython 2.6.2

Type declarations

Cython supports optional type declarations that

  • can be employed exactly where performance matters
  • let Cython generate plain C instead of C-API calls
  • make richards.py benchmark 5x faster than CPython
    • without Python code modifications :)
  • can make code 100 - 1000x faster than CPython
    • expect several 100 times in calculation loops

Part 2: Building Cython modules

  • Part 1: Intro to Cython
  • Part 2: Building Cython modules
  • Part 3: Writing fast code
  • Part 4: Talking to other extensions

Ways to build Cython code

To compile Python code (.py) or Cython code (.pyx)

  • you need:
    • Cython, Python and a C compiler
  • you can use:
    • web app that supports writing and running Cython code
    • on-the-fly build + import (for experiments)
    • setup.py script (likely required anyway)
    • distutils
    • pyximport
    • Sage notebook
    • cython source.pyx + manual C compilation

Example: distutils

  • A minimal setup.py script:

    from distutils.core import setupfrom distutils.extension import Extensionfrom Cython.Distutils import build_ext
    
    ext_modules = [Extension("worker", ["worker.py"])]
    
    setup(
      name = ‘stupid little app‘,
      cmdclass = {‘build_ext‘: build_ext},
      ext_modules = ext_modules
    )
  • Run with
    $ python setup.py build_ext --inplace

Example: pyximport

Build and import Cython code files (.pyx) on the fly

$ ls
worker.pyx$ PYTHONPATH=. python
Python 2.6.2 (r262:71600, Apr 17 2009, 11:29:30)[GCC 4.3.2] on linux2Type "help", "copyright", "credits" or "license" for more information.>>> import pyximport>>> pyximport.install()>>> import worker>>> worker<module ‘worker‘ from ‘~/.pyxbld/.../worker.so‘>>>> worker.HardWorker<class ‘worker.HardWorker‘>>>> worker.HardWorker(worker.add_simple_stuff).work_hard()

pyximporting Python modules

  • pyximport can also compile Python modules:
>>> import pyximport>>> pyximport.install(pyimport = True)>>> import shlex[lots of compiler errors from different modules ...]>>> help(shlex)
  • currently works for a few stdlib modules
  • falls back to normal Python import automatically
  • not production ready, but nice for testing :)

Writing executable programs

# file: hw.pydef hello_world():    import sys    print "Welcome to Python %d.%d!" % sys.version_info[:2]if __name__ == ‘__main__‘:
    hello_world()

Writing executable programs

# file: hw.pydef hello_world():    import sys    print "Welcome to Python %d.%d!" % sys.version_info[:2]if __name__ == ‘__main__‘:
    hello_world()

Compile, link and run:

$ cython --embed hw.py   # <- embed a main() function$ gcc $CFLAGS -I/usr/include/python2.6    -o hw hw.c -lpython2.6 -lpthread -lm -lutil -ldl$ ./hw
Welcome to Python 2.6!

Part 3: Writing fast code

  • Part 1: Intro to Cython
  • Part 2: Building Cython modules
  • Part 3: Writing fast code
  • Part 4: Talking to other extensions

A simple example

  • Plain Python code:
# integrate_py.pyfrom math import sindef f(x):    return sin(x**2)def integrate_f(a, b, N):
    dx = (b-a)/N
    s = 0    for i in range(N):
        s += f(a+i*dx)    return s * dx

Type declarations in Cython

Function arguments are easy

  • Python:

    def f(x):    return sin(x**2)
  • Cython:
    def f(double x):    return sin(x**2)

Type declarations in Cython

?cdef? keyword declares

  • variables with C or builtin types

    cdef double dx, s
  • functions with C signatures
    cdef double f(double x):    return sin(x**2)
  • classes as ‘builtin‘ extension types
    cdef class MyType:    cdef int field

Functions: def vs. cdef vs. cpdef

  • def func(int x):
    • part of the Python module API
    • Python call semantics
  • cdef int func(int x):
    • C signature
    • C call semantics
  • cpdef int func(int x):
    • Python wrapper around cdef function
    • C calls cdef function, Python calls wrapper
    • note: modified C signature!

Typed arguments and return values

  • def func(int x):
    • caller passes Python objects for x
    • function converts to int on entry
    • implicit return type always object
  • cdef int func(int x):
    • caller converts arguments as required
    • function receives C int for x
    • arbitrary return type, defaults to object
  • cpdef int func(int x):
    • wrapper converts
    • C callers convert arguments as required
    • Python callers pass and receive objects

A simple example: Python

# integrate_py.pyfrom math import sindef f(x):    return sin(x**2)def integrate_f(a, b, N):
    dx = (b-a)/N
    s = 0    for i in range(N):
        s += f(a+i*dx)    return s * dx

A simple example: Cython

# integrate_cy.pyxcdef extern from "math.h":
    double sin(double x)cdef double f(double x):    return sin(x**2)cpdef double integrate_f(double a, double b, int N):    cdef double dx, s    cdef int i

    dx = (b-a)/N
    s = 0    for i in range(N):
        s += f(a+i*dx)    return s * dx

Overriding declarations in .pxd

  • Plain Python code:
# integrate_py.pyfrom math import sindef f(x):    return sin(x**2)def integrate_f(a, b, N):
    dx = (b-a)/N
    s = 0    for i in range(N):
        s += f(a+i*dx)    return s * dx

Overriding declarations in .pxd

Python integrate_py.py Cython integrate_py.pxd
# integrate_py.pyfrom math import sindef f(x):    return sin(x**2)def integrate_f(a, b, N):

    dx = (b-a)/N
    s = 0    for i in range(N):
        s += f(a+i*dx)    return s * dx
# integrate_py.pxdcimport cythoncpdef double f(double x)@cython.locals(
    dx=double, s=double, i=int)cpdef integrate_f(
    double a, double b, int N)

The .pxd file used

# integrate_py.pxdcimport cythoncpdef double f(double x):    return sin(x**2)cpdef double integrate_f(double a, double b, int N)

Overriding declarations in .pxd

  • advantage:
    • Eclipse, pylint, 2to3, ...
    • runs unchanged in Python interpreter
    • plain Python code
    • complete Python tool-chain available
  • drawback:
    • cannot override from math import sin
    • no access to C functions

Typing in Python syntax

  • Plain Python code:
# integrate_py.pyfrom math import sindef f(x):    return sin(x**2)def integrate_f(a, b, N):
    dx = (b-a)/N
    s = 0    for i in range(N):
        s += f(a+i*dx)    return s * dx

Typing in Python syntax

from math import sinimport [email protected](x=cython.double)def f(x):    return sin(x**2)@cython.locals(a=cython.double, b=cython.double,
               N=cython.Py_ssize_t, dx=cython.double,
               s=cython.double, i=cython.Py_ssize_t)def integrate_f(a, b, N):
    dx = (b-a)/N
    s = 0    for i in range(N):
        s += f(a+i*dx)    return s * dx

Declaring Python types

  • Access to Python‘s builtins is heavily optimised
    • for ... in range()/list/tuple/dict
    • list.append(), list.reverse()
    • set([...]), tuple([...])
  • Further improvements in Cython 0.12
    • replacements for enumerate(), type()
    • dict([...]), unicode.encode(), list.sort()
  • Declaring Python types is often worth it!
  • Easy to add new optimisations
    • don‘t write prematurely optimised code, fix Cython!

Declaring Python types: dict

  • example: dict iteration
def filter_a(d):    return { key : value             for key, value in d.iteritems()             if ‘a‘ not in value }import stringd = { s:s for s in string.ascii_letters }print filter_a(d)

Declaring Python types: dict

  • simple change, ~30% faster:
def filter_a(dict d):       # <====    return { key : value             for key, value in d.iteritems()             if ‘a‘ not in value }import stringd = { s:s for s in string.ascii_letters }print filter_a(d)

Declaring Python types: dict

  • simple change, ~30% faster:
def filter_a(dict d):       # <====    return { key : value             for key, value in d.iteritems()             if ‘a‘ not in value }import stringd = { s:s for s in string.ascii_letters }print filter_a(d)
  • drawback:
    • non-dict mapping arguments raise a TypeError

Think twice before you type

  • benchmark code before adding static types!

Classes

  • class MyClass(object):
    • Python class with __dict__
    • multiple inheritance
    • arbitrary Python attributes
    • Python methods
    • monkey-patcheable etc.
  • cdef class MyClass(SomeSuperClass):
    • C-only access by default, or readonly/public
    • only from other extension types!
    • "builtin" extension type
    • single inheritance
    • fixed, typed fields
    • Python + C methods

cdef classes - when to use them?

  • Use cdef classes
    • e.g. whenever wrapping C structs/pointers/etc.
    • when C attribute types are used
    • when the need for speed beats Python‘s generality
  • Use Python classes
    • for bytes/tuple subtypes (PyVarObject)
    • for exceptions if Py<2.5 compatibility is required
    • when multiple inheritance is required
    • when users are allowed to monkey-patch

Part 4: Talking to other extensions

  • Part 1: Intro to Cython
  • Part 2: Building Cython modules
  • Part 3: Writing fast code
  • Part 4: Talking to other extensions

Talking to other extensions

  • Python 3 buffer protocol (available in Py2.6)
  • external C-APIs

Python 3 buffer protocol

  • Native support for new Python buffer protocol
    • PEP 3118
def inplace_invert_2D_buffer(                object[unsigned char, 2] image):    cdef int i, j    for i in range(image.shape[0]):        for j in range(image.shape[1]):
            image[i, j] = 255 - image[i, j]
  • can be supported for extension types in Py2.x
    • declared through .pxd files
    • Cython ships with numpy.pxd
    • array.pxd available (stdlib‘s array)

Conclusion

  • Cython is a tool for
    • translating Python code to efficient C
    • easily interfacing to external C/C++/Fortran code
  • Use it to
    • concentrate on the mapping, not the glue!
    • don‘t change the language just to get fast code!
    • concentrate on optimisations, not rewrites!
    • speed up existing Python modules
    • write C extensions for CPython
    • wrap C libraries in Python

... but Cython is also

  • a great project
  • a very open playground for great ideas!

Cython

Cython

C-Extensions in Python

... use it, and join the project!

http://cython.org/

时间: 2024-08-08 01:53:45

用Cython编译写出更快的Python代码的相关文章

如何写出更好的Java代码

Java是最流行的编程语言之一,但似乎并没有人喜欢使用它.好吧,实际上Java是一门还不错的编程语言,由于最近Java 8发布了,我决定来编辑一个如何能更好地使用Java的列表,这里面包括一些库,实践技巧以及工具. 这篇文章在GitHub上也有.你可以随时在上面贡献或者添加你自己的Java使用技巧或者最佳实践. 编码风格 结构体 builder模式 依赖注入 避免null值 不可变 避免过多的工具类 格式 文档 Stream 部署 框架 Maven 依赖收敛 持续集成 Maven仓储 配置管理

Java 11 正式发布,这 8 个逆天新特性教你写出更牛逼的代码

美国时间 09 月 25 日,Oralce 正式发布了 Java 11,这是据 Java 8 以后支持的首个长期版本. 为什么说是长期版本,看下面的官方发布的支持路线图表. Java 11 正式发布,这 8 个逆天新特性教你写出更牛逼的代码可以看出 Java 8 扩展支持到 2025 年,而 Java 11 扩展支持到 2026 年. 现在大部分都在用 Java 8,Java 9 和 10 目前很少有人在用,至少我没有发现有公司在生产环境应用的,那就是找死. 现在 Java 11 长期支持,也已

[label][翻译][JavaScript-Translation]七个步骤让你写出更好的JavaScript代码

7 steps to better JavaScript 原文:http://www.creativebloq.com/netmag/7-steps-better-javascript-51411781七个步骤让你写出更好的JavaScript代码 随着浏览器的性能提升,新的HTML5 APIS也在不断地被应用,JavaScript在web使用中不断增长.然而,一行糟糕的代码就有可能会影响到整个网站,产生糟糕的用户体验和造成潜在客户的流失. 开发者必须使用他所能使用的工具和技巧来提高代码的质量,

怎样写出更好的JavaScript代码

一.可维护性的代码 1.可读性:在函数和方法.大段代码.复杂算法和hack上加上帮助理解的注释.    2.变量的函数的命名:变量最好用名词,函数最好以动词开头.有较好的语义和逻辑性. 3.变量类型透明:以合适的命名方式来帮助记忆变量的类型 . a:初始化法. b:匈牙利标记法. c:在后面用注释说明. 4.松散耦合 松散耦合就是一部分的代码功能依赖于另外一部分.   a:html/javascript 解耦:html与js尽量分离,比如在html中添加事件处理,动态插入标签.解耦可以更快地知道

让我们一起写出更有效的CSharp代码吧,少年们!

周末空闲,选读了一下一本很不错的C#语言使用的书,特此记载下便于对项目代码进行重构和优化时查看. Standing On Shoulders of Giants,附上思维导图,其中标记的颜色越深表示在实际中的实际意义越大. 名称 内容和示例 提供API时尽量提供泛型接口 Public interface IComparable<T>{ int CompareTo(T other) } 泛型约束尽可能的严格并有效 Public delegate T FactoryFunc<T>();

Java 11正式发布,这几个逆天新特性教你写出更牛逼的代码

就在前段时间,Oracle 官方宣布 Java 11 (18.9 LTS) 正式发布,可在生产环境中使用! 这无疑对我们来说是一大好的消息.作为一名java开发者来说,虽然又要去学习和了解java11,但内心还是欣慰的.我想至少你和我一样的心情:Java在手,天下我有! 今天我们来看一下Java 11到底是什么.他有什么特别的.到底要不要升级到Java 11. Java 11有什么特别的 在Oracle官网中,进入下载页面,第一个可供下载的JDK版本已经提换成了Java SE 11 (LTS),

【整洁之道】如何写出更整洁的代码(上)

如何写出更整洁的代码 代码整洁之道不是银弹,不会立竿见影的带来收益. 没有任何犀利的武功招式,只有一些我个人异常推崇的代码整洁之道的内功心法.它不会直接有效的提高你写代码的能力与速度,但是对于程序员的整个职业生涯必然会带来意想不到的好处. 如果你还是一个在校学生,或者是刚工作没多久的"菜鸟",那么很有必要接触一些这方面的知识的.很显然,它会帮助你更快的适应企业级开发的要求. 1. 为什么需要代码更整洁? 在考虑代码整洁的时候,我们需要明确的一个前提是,这里不讨论代码的对错. 关于什么是

理解CacheLine与写出更好的JAVA

今天查了很多资料,主要是想搞清楚写JAVA和CacheLine有什么关系以及我们如何针对CacheLine写出更好的JAVA程序. CPU和内存 CPU是计算机的大脑,它负责运算,内存是数据,它为CPU提供数据.这里之所以忽略其他存储设备是为了简化模型.假设我们面对的是具有两个核心的CPU,那么我们的模型大概如下面的样子: CPU计算核心不会直接和内存打交道,它会直接从缓存拿数据,如果缓存没拿到,专业点说即缓存未命中的时候才会去内存去拿,同时会更新缓存.这个过程CPU不会仅仅读取需要的某个字节或

掌握解决问题的艺术,学会迭代开发,成为协作开发的专家,然后为写出更好的代码而担忧(转)

很多开发人员普遍犯有一个错误,认为他们的工作就是写代码.这不是一个开发人员要做的事情. 一个开发人员的工作是解决问题. 解决问题的一部分过程通常涉及到写代码,但是这部分工作是非常非常小的.开发有用的东西才花更多时间. 明白如何迭代开发,随着对问题有更好的理解,你才能给难题增加一些小功能,因为从头开发完美的产品是不可能的.不用写代码就能验证功能,因为很明显,写代码是相当昂贵的. 用于测试.评测和抛弃想法的系统也是极其重要的,因为要是没有它,整个开发组将耗费越来越多的精力,还有用来帮助他们执行得更有