Merged pull request #12 from bhy/T423.
[cython.git] / docs / src / userguide / early_binding_for_speed.rst
1 .. highlight:: cython
2
3 .. _early-binding-for-speed:
4
5 **************************
6 Early Binding for Speed
7 **************************
8
9 As a dynamic language, Python encourages a programming style of considering
10 classes and objects in terms of their methods and attributes, more than where
11 they fit into the class hierarchy.
12
13 This can make Python a very relaxed and comfortable language for rapid
14 development, but with a price - the 'red tape' of managing data types is
15 dumped onto the interpreter. At run time, the interpreter does a lot of work
16 searching namespaces, fetching attributes and parsing argument and keyword
17 tuples. This run-time 'late binding' is a major cause of Python's relative
18 slowness compared to 'early binding' languages such as C++.
19
20 However with Cython it is possible to gain significant speed-ups through the
21 use of 'early binding' programming techniques.
22
23 For example, consider the following (silly) code example:
24
25 .. sourcecode:: cython
26
27     cdef class Rectangle:
28         cdef int x0, y0
29         cdef int x1, y1
30         def __init__(self, int x0, int y0, int x1, int y1):
31             self.x0 = x0; self.y0 = y0; self.x1 = x1; self.y1 = y1
32         def area(self):
33             area = (self.x1 - self.x0) * (self.y1 - self.y0)
34             if area < 0:
35                 area = -area
36             return area
37
38     def rectArea(x0, y0, x1, y1):
39         rect = Rectangle(x0, y0, x1, y1)
40         return rect.area()
41
42 In the :func:`rectArea` method, the call to :meth:`rect.area` and the
43 :meth:`.area` method contain a lot of Python overhead.
44
45 However, in Cython, it is possible to eliminate a lot of this overhead in cases
46 where calls occur within Cython code. For example:
47
48 .. sourcecode:: cython
49
50     cdef class Rectangle:
51         cdef int x0, y0
52         cdef int x1, y1
53         def __init__(self, int x0, int y0, int x1, int y1):
54             self.x0 = x0; self.y0 = y0; self.x1 = x1; self.y1 = y1
55         cdef int _area(self):
56             cdef int area
57             area = (self.x1 - self.x0) * (self.y1 - self.y0)
58             if area < 0:
59                 area = -area
60             return area
61         def area(self):
62             return self._area()
63
64     def rectArea(x0, y0, x1, y1):
65         cdef Rectangle rect
66         rect = Rectangle(x0, y0, x1, y1)
67         return rect._area()
68
69 Here, in the Rectangle extension class, we have defined two different area
70 calculation methods, the efficient :meth:`_area` C method, and the
71 Python-callable :meth:`area` method which serves as a thin wrapper around
72 :meth:`_area`. Note also in the function :func:`rectArea` how we 'early bind'
73 by declaring the local variable ``rect`` which is explicitly given the type
74 Rectangle. By using this declaration, instead of just dynamically assigning to
75 ``rect``, we gain the ability to access the much more efficient C-callable
76 :meth:`_rect` method.
77
78 But Cython offers us more simplicity again, by allowing us to declare
79 dual-access methods - methods that can be efficiently called at C level, but
80 can also be accessed from pure Python code at the cost of the Python access
81 overheads. Consider this code:
82
83 .. sourcecode:: cython
84
85     cdef class Rectangle:
86         cdef int x0, y0
87         cdef int x1, y1
88         def __init__(self, int x0, int y0, int x1, int y1):
89             self.x0 = x0; self.y0 = y0; self.x1 = x1; self.y1 = y1
90         cpdef int area(self):
91             cdef int area
92             area = (self.x1 - self.x0) * (self.y1 - self.y0)
93             if area < 0:
94                 area = -area
95             return area
96
97     def rectArea(x0, y0, x1, y1):
98         cdef Rectangle rect
99         rect = Rectangle(x0, y0, x1, y1)
100         return rect.area()
101
102 .. note:: 
103
104     in earlier versions of Cython, the :keyword:`cpdef` keyword is
105     :keyword:`rdef` - but has the same effect).
106
107 Here, we just have a single area method, declared as :keyword:`cpdef` to make it
108 efficiently callable as a C function, but still accessible from pure Python
109 (or late-binding Cython) code.
110
111 If within Cython code, we have a variable already 'early-bound' (ie, declared
112 explicitly as type Rectangle, (or cast to type Rectangle), then invoking its
113 area method will use the efficient C code path and skip the Python overhead.
114 But if in Pyrex or regular Python code we have a regular object variable
115 storing a Rectangle object, then invoking the area method will require:
116
117 * an attribute lookup for the area method
118 * packing a tuple for arguments and a dict for keywords (both empty in this case)
119 * using the Python API to call the method 
120
121 and within the area method itself:
122
123 * parsing the tuple and keywords
124 * executing the calculation code
125 * converting the result to a python object and returning it 
126
127 So within Cython, it is possible to achieve massive optimisations by
128 using strong typing in declaration and casting of variables. For tight loops
129 which use method calls, and where these methods are pure C, the difference can
130 be huge.
131