Author: Maciej Fijalkowski <fij...@gmail.com> Branch: extradoc Changeset: r5540:ea1511561764 Date: 2015-07-15 12:51 +0200 http://bitbucket.org/pypy/extradoc/changeset/ea1511561764/
Log: merge diff --git a/talk/ep2015/pypy-abstract.rst b/talk/ep2015/pypy-abstract.rst new file mode 100644 --- /dev/null +++ b/talk/ep2015/pypy-abstract.rst @@ -0,0 +1,22 @@ +============================= +PyPy for mediocre programmers +============================= + +This is a talk for mediocre Python programmers by a mediocre programmer. PyPy +is an alternative implementation of Python. It is notorious for being fast, but +also for using clever algorithms pertaining to advanced concepts such as type +inference, garbage collection, just-in-time compilation, etc. So, can we, +mediocre programmers, realistically use PyPy? + +Yes, absolutely. In fact, PyPy developers did all that hard work so that we +wouldn't have to. As we'll see, it runs most Python code exactly like CPython +does, save that it magically makes it faster. + +Porting existing applications is always more involved than running a simple +script, so we'll also examine likely difficulties such as code relying on +CPython implementation details, and dependencies on C extensions, and explore +simple principles to let PyPy run your code even faster. + +Finally, we'll have a glimpse of the future by looking at what's brewing in +the PyPy lair, such as software transactional memory, new speed optimisations, +better support for Python 3 and NumPy, ... diff --git a/talk/pycon-italy-2015/Makefile b/talk/pycon-italy-2015/Makefile new file mode 100644 --- /dev/null +++ b/talk/pycon-italy-2015/Makefile @@ -0,0 +1,18 @@ +# you can find rst2beamer.py here: +# http://codespeak.net/svn/user/antocuni/bin/rst2beamer.py + +# WARNING: to work, it needs this patch for docutils +# https://sourceforge.net/tracker/?func=detail&atid=422032&aid=1459707&group_id=38414 + +talk.pdf: talk.rst author.latex title.latex stylesheet.latex + python `which rst2beamer.py` --stylesheet=stylesheet.latex --documentoptions=14pt talk.rst talk.latex || exit + #/home/antocuni/.virtualenvs/rst2beamer/bin/python `which rst2beamer.py` --stylesheet=stylesheet.latex --documentoptions=14pt talk.rst talk.latex || exit + sed 's/\\date{}/\\input{author.latex}/' -i talk.latex || exit + #sed 's/\\maketitle/\\input{title.latex}/' -i talk.latex || exit + pdflatex talk.latex || exit + +view: talk.pdf + evince talk.pdf & + +xpdf: talk.pdf + xpdf talk.pdf & diff --git a/talk/pycon-italy-2015/author.latex b/talk/pycon-italy-2015/author.latex new file mode 100644 --- /dev/null +++ b/talk/pycon-italy-2015/author.latex @@ -0,0 +1,8 @@ +\definecolor{rrblitbackground}{rgb}{0.0, 0.0, 0.0} + +\title[PyPy JIT]{PyPy JIT (not) for dummies} +\author[antocuni] +{Antonio Cuni} + +\institute{PyCon Sei} +\date{April 17, 2015} diff --git a/talk/pycon-italy-2015/beamerdefs.txt b/talk/pycon-italy-2015/beamerdefs.txt new file mode 100644 --- /dev/null +++ b/talk/pycon-italy-2015/beamerdefs.txt @@ -0,0 +1,108 @@ +.. colors +.. =========================== + +.. role:: green +.. role:: red + + +.. general useful commands +.. =========================== + +.. |pause| raw:: latex + + \pause + +.. |small| raw:: latex + + {\small + +.. |end_small| raw:: latex + + } + +.. |scriptsize| raw:: latex + + {\scriptsize + +.. |end_scriptsize| raw:: latex + + } + +.. |strike<| raw:: latex + + \sout{ + +.. closed bracket +.. =========================== + +.. |>| raw:: latex + + } + + +.. example block +.. =========================== + +.. |example<| raw:: latex + + \begin{exampleblock}{ + + +.. |end_example| raw:: latex + + \end{exampleblock} + + + +.. alert block +.. =========================== + +.. |alert<| raw:: latex + + \begin{alertblock}{ + + +.. |end_alert| raw:: latex + + \end{alertblock} + + + +.. columns +.. =========================== + +.. |column1| raw:: latex + + \begin{columns} + \begin{column}{0.45\textwidth} + +.. |column2| raw:: latex + + \end{column} + \begin{column}{0.45\textwidth} + + +.. |end_columns| raw:: latex + + \end{column} + \end{columns} + + + +.. |snake| image:: ../../img/py-web-new.png + :scale: 15% + + + +.. nested blocks +.. =========================== + +.. |nested| raw:: latex + + \begin{columns} + \begin{column}{0.85\textwidth} + +.. |end_nested| raw:: latex + + \end{column} + \end{columns} diff --git a/talk/pycon-italy-2015/jit-overview.odg b/talk/pycon-italy-2015/jit-overview.odg new file mode 100644 index 0000000000000000000000000000000000000000..43aec7c16da3c72bec87a6be52ce41969f4db8c7 GIT binary patch [cut] diff --git a/talk/pycon-italy-2015/jit-overview1.pdf b/talk/pycon-italy-2015/jit-overview1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..6b511590edd06e9deb157eb02cecb7cf64c61926 GIT binary patch [cut] diff --git a/talk/pycon-italy-2015/jit-overview2.pdf b/talk/pycon-italy-2015/jit-overview2.pdf new file mode 100644 index 0000000000000000000000000000000000000000..c07d0e3948403a7c42951a65ba023311d4739ac8 GIT binary patch [cut] diff --git a/talk/pycon-italy-2015/jit-overview3.pdf b/talk/pycon-italy-2015/jit-overview3.pdf new file mode 100644 index 0000000000000000000000000000000000000000..277b2067bc2daf79f7696edaabd22614182e6bfa GIT binary patch [cut] diff --git a/talk/pycon-italy-2015/speed.png b/talk/pycon-italy-2015/speed.png new file mode 100644 index 0000000000000000000000000000000000000000..e2372b9e0d5fc3fe7711b27d641d2d9706185fd3 GIT binary patch [cut] diff --git a/talk/pycon-italy-2015/src/decode0.py b/talk/pycon-italy-2015/src/decode0.py new file mode 100644 --- /dev/null +++ b/talk/pycon-italy-2015/src/decode0.py @@ -0,0 +1,25 @@ +import sys +import struct + +P1 = '\x0c\x00\x00\x00"\x00\x00\x00\x07\x00' +P2 = '\x15\x00\x00\x00+\x00\x00\x00\x08\x00' + +PLIST = [P1, P2] * 2000 + +def read_x(p): + return struct.unpack_from('l', p, 0)[0] + +def read_y(p): + return struct.unpack_from('l', p, 4)[0] + +def read_color(p): + return struct.unpack_from('i', p, 8)[0] + +def main(): + res = 0 + for p in PLIST: + x = read_x(p) + res += x + print res + +main() diff --git a/talk/pycon-italy-2015/src/decode1.py b/talk/pycon-italy-2015/src/decode1.py new file mode 100644 --- /dev/null +++ b/talk/pycon-italy-2015/src/decode1.py @@ -0,0 +1,41 @@ +import sys +import struct + +P1 = '\x0c\x00\x00\x00"\x00\x00\x00\x07\x00' +P2 = '\x15\x00\x00\x00+\x00\x00\x00\x08\x00' + +PLIST = [P1, P2] * 2000 + +class Field(object): + + def __init__(self, fmt, offset): + self.fmt = fmt + self.offset = offset + + +class Message(object): + + def __init__(self, name, fields): + self._name = name + self._fields = fields + + def read(self, buf, name): + f = self._fields[name] + return struct.unpack_from(f.fmt, buf, f.offset)[0] + + +Point = Message('Point', { + 'x': Field('l', 0), + 'y': Field('l', 4), + 'color': Field('i', 8) + }) + + +def main(): + res = 0 + for p in PLIST: + x = Point.read(p, 'x') + res += x + print res + +main() diff --git a/talk/pycon-italy-2015/src/decode2.py b/talk/pycon-italy-2015/src/decode2.py new file mode 100644 --- /dev/null +++ b/talk/pycon-italy-2015/src/decode2.py @@ -0,0 +1,42 @@ +import sys +import struct + +P1 = '\x0c\x00\x00\x00"\x00\x00\x00\x07\x00' +P2 = '\x15\x00\x00\x00+\x00\x00\x00\x08\x00' + +PLIST = [P1, P2] * 2000 + +class Field(object): + + def __init__(self, fmt, offset): + self.fmt = fmt + self.offset = offset + +def Message(name, fields): + class M(object): + def read(self, buf, name): + f = getattr(self, name) + return struct.unpack_from(f.fmt, buf, f.offset)[0] + + for fname, f in fields.iteritems(): + setattr(M, fname, f) + + M.__name__ = name + return M() + + +Point = Message('Point', { + 'x': Field('l', 0), + 'y': Field('l', 4), + 'color': Field('i', 8) + }) + + +def main(): + res = 0 + for p in PLIST: + x = Point.read(p, 'x') + res += x + print res + +main() diff --git a/talk/pycon-italy-2015/src/decode3.py b/talk/pycon-italy-2015/src/decode3.py new file mode 100644 --- /dev/null +++ b/talk/pycon-italy-2015/src/decode3.py @@ -0,0 +1,32 @@ +import sys +import struct + +P1 = '\x0c\x00\x00\x00"\x00\x00\x00\x07\x00' +P2 = '\x15\x00\x00\x00+\x00\x00\x00\x08\x00' + +PLIST = [P1, P2] * 2000 + +class Field(object): + def __init__(self, fmt, offset): + self.fmt = fmt + self.offset = offset + + def __get__(self, obj, cls): + return struct.unpack_from(self.fmt, obj._buf, self.offset)[0] + +class Point(object): + def __init__(self, buf): + self._buf = buf + + x = Field('l', 0) + y = Field('l', 4) + color = Field('h', 8) + +def main(): + res = 0 + for p in PLIST: + p = Point(p) + res += p.x + print res + +main() diff --git a/talk/pycon-italy-2015/stylesheet.latex b/talk/pycon-italy-2015/stylesheet.latex new file mode 100644 --- /dev/null +++ b/talk/pycon-italy-2015/stylesheet.latex @@ -0,0 +1,10 @@ +\usetheme{Boadilla} +\setbeamercovered{transparent} +\setbeamertemplate{navigation symbols}{} + +\definecolor{darkgreen}{rgb}{0, 0.5, 0.0} +\newcommand{\docutilsrolegreen}[1]{\color{darkgreen}#1\normalcolor} +\newcommand{\docutilsrolered}[1]{\color{red}#1\normalcolor} + +\newcommand{\green}[1]{\color{darkgreen}#1\normalcolor} +\newcommand{\red}[1]{\color{red}#1\normalcolor} diff --git a/talk/pycon-italy-2015/talk.pdf b/talk/pycon-italy-2015/talk.pdf new file mode 100644 index 0000000000000000000000000000000000000000..1492bcaa865783996c4f06a4afc7eabcea54249b GIT binary patch [cut] diff --git a/talk/pycon-italy-2015/talk.pdf.info b/talk/pycon-italy-2015/talk.pdf.info new file mode 100644 --- /dev/null +++ b/talk/pycon-italy-2015/talk.pdf.info @@ -0,0 +1,11 @@ +AvailableTransitions=[Crossfade] +TransitionDuration = 100 +EstimatedDuration = 45*60 # in seconds +MinutesOnly = True + +PageProps = { + 1: { + 'reset': FirstTimeOnly, + 'progress': False, + }, +} diff --git a/talk/pycon-italy-2015/talk.rst b/talk/pycon-italy-2015/talk.rst new file mode 100644 --- /dev/null +++ b/talk/pycon-italy-2015/talk.rst @@ -0,0 +1,580 @@ +.. include:: beamerdefs.txt + +================================ +PyPy JIT (not) for dummies +================================ + +About me +--------- + +- PyPy core dev + +- ``pdb++``, ``fancycompleter``, ... + +- Consultant, trainer + +- http://antocuni.eu + + +What is PyPy +--------------- + +- Alternative, fast Python implementation + +- Performance: JIT compiler, advanced GC + +- STM: goodbye GIL + +- PyPy 2.5.1 (2.7.8) + +- Py3k as usual in progress (3.2.5 out, 3.3 in development) + +- http://pypy.org + + +STM +--- + +- pypy-stm-2.5.1 is out + + * 64 bit Linux only + +- no GIL! + +- 25-40% slowdown for single core programs + + * still ``7*0.75 = 5.25x`` faster than CPython :) + +- parallelism up to 4 threads + +- concurrency slow-but-correct by default + + * compared to fast-but-buggy by using threads + +- conflict detection + +- TransactionQueue: parallelize your program without using threads! + + +Extension modules +------------------ + +- CFFI: stable, mature and widely used + + * psycopg2cffi, lxml-cffi, pysdl2-cffi, etc. + + * should be used even for CPython-only projects! + +- numpy: + + * support for linalg + + * support for pure Python, JIT friendly ufuncs + + * object dtype in-progress + +- scipy: see next slide :) + + +Pymetabiosis +-------------- + +- embed CPython in PyPy + +- import and use CPython modules in PyPy + +- ALPHA status + +- slow when passing arbitrary objects + +- but fast for numpy arrays + +- matplotlib and scipy works + +- https://github.com/rguillebert/pymetabiosis + + + +Speed: 7x faster than CPython +------------------------------- + +.. image:: speed.png + :scale: 47% + + +The JIT +-------- + +.. image:: jit-overview1.pdf + :scale: 50% + + +The JIT +-------- + +.. image:: jit-overview2.pdf + :scale: 50% + + +The JIT +-------- + +.. image:: jit-overview3.pdf + :scale: 50% + + +JIT overview +------------- + +- Tracing JIT + + * detect and compile "hot" loops + + * (although not only loops) + +- **Specialization** + +- Precompute as much as possible + +- Constant propagation + +- Aggressive inlining + + +Specialization (1) +------------------- + +- ``obj.foo()`` + +- which code is executed? (SIMPLIFIED) + + * lookup ``foo`` in obj.__dict__ + + * lookup ``foo`` in obj.__class__ + + * lookup ``foo`` in obj.__bases__[0], etc. + + * finally, execute ``foo`` + +- without JIT, you need to do these steps again and again + +- Precompute the lookup? + + +Specialization (2) +-------------------- + +- pretend and assume that ``obj.__class__`` IS constant + + * "promotion" + +- guard + + * check our assumption: if it's false, bail out + +- now we can directly jump to ``foo`` code + + * ...unless ``foo`` is in ``obj.__dict__``: GUARD! + + * ...unless ``foo.__class__.__dict__`` changed: GUARD! + +- Too many guard failures? + + * Compile some more assembler! + +- guards are cheap + + * out-of-line guards even more + + +Specialization (3) +--------------------- + +- who decides what to promote/specialize for? + + * we, the PyPy devs :) + + * heuristics + +- instance attributes are never promoted + +- class attributes are promoted by default (with some exceptions) + +- module attributes (i.e., globals) as well + +- bytecode constants + + +Specialization trade-offs +-------------------------- + +- Too much specialization + + * guards fails often + + * explosion of assembler + +- Not enough specialization + + * inefficient code + + +Virtuals +-------- + +- Remove unnecessary allocations + +- Remove unnecessary load/store + +|small| +|example<| |small| virtuals.py |end_small| |>| + +.. sourcecode:: python + + res = 0 + while res < 10000: + obj = Foo(x, y, z) + res += obj.x + +|end_example| +|end_small| + + +Example +-------- + +- Real world example + +- Decoding binary messages + +- Messages: strings of bytes + +|small| +|example<| |small| Point |end_small| |>| + +.. sourcecode:: C + + struct Point { + int x; + int y; + short color; + } + +|end_example| +|end_small| + + + +Example: low-level solution +---------------------------- + +|scriptsize| +|example<| |small| decode0.py |end_small| |>| + +.. sourcecode:: python + + P1 = '\x0c\x00\x00\x00"\x00\x00\x00\x07\x00\x00\x00' + P2 = '\x15\x00\x00\x00+\x00\x00\x00\x08\x00\x00\x00' + + PLIST = [P1, P2] * 2000 + + def read_x(p): + return struct.unpack_from('l', p, 0)[0] + + def main(): + res = 0 + for p in PLIST: + x = read_x(p) + res += x + print res + +|end_example| +|end_scriptsize| + +Example: low-level solution +---------------------------- + +|scriptsize| +|example<| |small| decode0.py trace |end_small| |>| + +.. sourcecode:: python + + debug_merge_point(1, 1, '<code object read_x> #0 LOAD_GLOBAL') + debug_merge_point(1, 1, '<code object read_x> #3 LOOKUP_METHOD') + debug_merge_point(1, 1, '<code object read_x> #6 LOAD_CONST') + debug_merge_point(1, 1, '<code object read_x> #9 LOAD_FAST') + debug_merge_point(1, 1, '<code object read_x> #12 LOAD_CONST') + debug_merge_point(1, 1, '<code object read_x> #15 CALL_METHOD') + +606: i91 = strlen(p88) + +609: i92 = int_lt(i91, 4) + guard_false(i92, descr=<Guard0xb3a14b20>) + +618: i93 = strgetitem(p88, 0) + +622: i94 = strgetitem(p88, 1) + +632: i95 = int_lshift(i94, 8) + +635: i96 = int_or(i93, i95) + +637: i97 = strgetitem(p88, 2) + +653: i98 = int_lshift(i97, 16) + +656: i99 = int_or(i96, i98) + +658: i100 = strgetitem(p88, 3) + +662: i101 = int_ge(i100, 128) + guard_false(i101, descr=<Guard0xb3a14ac0>) + +674: i102 = int_lshift(i100, 24) + +677: i103 = int_or(i99, i102) + +|end_example| +|end_scriptsize| + +Example: better API +--------------------- + +|scriptsize| +|example<| |small| decode1.py |end_small| |>| + +.. sourcecode:: python + + class Field(object): + def __init__(self, fmt, offset): + self.fmt = fmt; self.offset = offset + + class Message(object): + def __init__(self, name, fields): + self._name = name; self._fields = fields + + def read(self, buf, name): + f = self._fields[name] + return struct.unpack_from(f.fmt, buf, f.offset)[0] + + Point = Message('Point', {'x': Field('l', 0), + 'y': Field('l', 8), + 'color': Field('i', 16)}) + + def main(): + res = 0 + for p in PLIST: + x = Point.read(p, 'x') + res += x + print res + +|end_example| +|end_scriptsize| + +Example: better API +---------------------------- + +|scriptsize| +|example<| |small| decode1.py trace (1) |end_small| |>| + +.. sourcecode:: python + + debug_merge_point(1, 1, '<code object read> #34 CALL_METHOD') + p156 = getfield_gc_pure(p154, descr=<W_BytesObject.inst__value 8>) + i157 = getfield_gc_pure(p155, descr=<W_IntObject.inst_intval 8>) + p158 = new_with_vtable(-1228074336) + setfield_gc(p158, 0, descr=<CalcSizeFormatIterator.inst_totalsize 8>) + call(interpret_trampoline__v238___simple_call__function_i, p158, p156, ...) + guard_no_exception(descr=<Guard0xb3a8cd00>) + i159 = getfield_gc(p158, descr=<CalcSizeFormatIterator.inst_totalsize 8>) + i160 = int_lt(i157, 0) + guard_false(i160, descr=<Guard0xb3a8ccd0>) + i161 = strlen(p141) + i162 = int_sub(i161, i157) + i163 = int_lt(i162, i159) + guard_false(i163, descr=<Guard0xb3a8cca0>) + i164 = int_ge(i159, 0) + guard_true(i164, descr=<Guard0xb3a8cc70>) + p165 = force_token() + p166 = new_with_vtable(-1228077368) + p167 = new_with_vtable(-1228077280) + p168 = new_with_vtable(-1228267680) + setfield_gc(p167, 1, descr=<FieldU rpython.rlib.buffer.Buffer.inst_readonly 8>) +|end_example| +|end_scriptsize| + +Example: better API +---------------------------- + +|scriptsize| +|example<| |small| decode1.py trace (2) |end_small| |>| + +.. sourcecode:: python + + p169 = new(descr=<SizeDescr 12>) + p170 = new_array_clear(0, descr=<ArrayP 4>) + p171 = new_with_vtable(-1229823908) + setfield_gc(p171, p145, descr=<JitVirtualRef.virtual_token 8>) + setfield_gc(p171, p145, descr=<JitVirtualRef.virtual_token 8>) + setfield_gc(p171, ConstPtr(null), descr=<FieldP JitVirtualRef.forced 12>) + setfield_gc(p51, p171, descr=<ExecutionContext.inst_topframeref 40>) + setfield_gc(p0, p165, descr=<PyFrame.vable_token 8>) + setfield_gc(p168, 1, descr=<Buffer.inst_readonly 8>) + setfield_gc(p168, p141, descr=<StringBuffer.inst_value 12>) + setfield_gc(p167, p168, descr=<SubBuffer.inst_buffer 12>) + setfield_gc(p167, i157, descr=<SubBuffer.inst_offset 16>) + setfield_gc(p167, i159, descr=<SubBuffer.inst_size 20>) + setfield_gc(p166, p167, descr=<UnpackFormatIterator.inst_buf 8>) + setfield_gc(p166, i159, descr=<UnpackFormatIterator.inst_length 12>) + setfield_gc(p166, 0, descr=<UnpackFormatIterator.inst_pos 16>) + setfield_gc(p169, 0, descr=<list.length 4>) + setfield_gc(p169, p170, descr=<list.items 8>) + setfield_gc(p166, p169, descr=<UnpackFormatIterator.inst_result_w 20>) + +|end_example| +|end_scriptsize| + + +Example: better API +---------------------------- + +|scriptsize| +|example<| |small| decode1.py trace (3) |end_small| |>| + +.. sourcecode:: python + + call_may_force(interpret_trampoline__v628___simple_call__function_i), p166, p156, descr=<Callv 0 rr EF=6>) + guard_not_forced(descr=<Guard0xb3a8b9d0>) + guard_no_exception(descr=<Guard0xb3a8cc40>) + p172 = getfield_gc(p166, descr=<UnpackFormatIterator.inst_result_w 20>) + i173 = getfield_gc(p172, descr=<list.length 4>) + p174 = new_array_clear(i173, descr=<ArrayP 4>) + p175 = getfield_gc(p172, descr=<list.items 8>) + call(ll_arraycopy__arrayPtr_arrayPtr_Signed_Signed_Signed), p175, p174, 0, 0, i173, descr=<Callv 0 rriii EF=2 OS=1>) + i176 = int_eq(i173, 2) + guard_false(i176, descr=<Guard0xb3a8cc10>) + +|end_example| +|end_scriptsize| + +Example: faster API +--------------------- + +|scriptsize| +|example<| |small| decode2.py |end_small| |>| + +.. sourcecode:: python + + def Message(name, fields): + class M(object): + def read(self, buf, name): + f = getattr(self, name) + return struct.unpack_from(f.fmt, buf, f.offset)[0] + + for fname, f in fields.iteritems(): + setattr(M, fname, f) + + M.__name__ = name + return M() + + Point = Message('Point', { + 'x': Field('l', 0), + 'y': Field('l', 4), + 'color': Field('i', 8) + }) + + ... + x = Point.read(p, 'x') + ... + +|end_example| +|end_scriptsize| + +Example: faster API +---------------------------- + +|scriptsize| +|example<| |small| decode2.py trace (3) |end_small| |>| + +.. sourcecode:: python + + debug_merge_point(1, 1, '<code object read> #36 CALL_METHOD') + +670: i104 = strlen(p101) + +673: i105 = int_lt(i104, 4) + guard_false(i105, descr=<Guard0xb3afac10>) + +682: i106 = strgetitem(p101, 0) + +686: i107 = strgetitem(p101, 1) + +696: i108 = int_lshift(i107, 8) + +699: i109 = int_or(i106, i108) + +701: i110 = strgetitem(p101, 2) + +717: i111 = int_lshift(i110, 16) + +720: i112 = int_or(i109, i111) + +722: i113 = strgetitem(p101, 3) + +726: i114 = int_ge(i113, 128) + guard_false(i114, descr=<Guard0xb3afabe0>) + +738: i115 = int_lshift(i113, 24) + +741: i116 = int_or(i112, i115) + +|end_example| +|end_scriptsize| + +What happened? +--------------- + +- dict lookups inside classes are specialized + +- decode1.py + + * ``fields`` is "normal data" and expected to change + + * one JIT code for **all** possible messages + +- decode2.py + + * ``fields`` is expected to be constant + + * one JIT code for **each** message + +- Behaviour is the same, different performance + + +Example: even better API :) +----------------------------- + +|scriptsize| +|example<| |small| decode3.py |end_small| |>| + +.. sourcecode:: python + + class Field(object): + def __init__(self, fmt, offset): + self.fmt = fmt + self.offset = offset + + def __get__(self, obj, cls): + return struct.unpack_from(self.fmt, obj._buf, self.offset)[0] + + class Point(object): + def __init__(self, buf): + self._buf = buf + + x = Field('l', 0) + y = Field('l', 4) + color = Field('h', 8) + + def main(): + res = 0 + for p in PLIST: + p = Point(p) + res += p.x + print res + +|end_example| +|end_scriptsize| + + + +Contacts, Q&A +-------------- + +- http://pypy.org + +- http://morepypy.blogspot.com/ + +- twitter: @antocuni + +- Available for consultancy & training: + + * http://antocuni.eu + + * i...@antocuni.eu + +- Any question? + diff --git a/talk/pycon-italy-2015/title.latex b/talk/pycon-italy-2015/title.latex new file mode 100644 --- /dev/null +++ b/talk/pycon-italy-2015/title.latex @@ -0,0 +1,5 @@ +\begin{titlepage} +\begin{figure}[h] +\includegraphics[width=80px]{../img/py-web.png} +\end{figure} +\end{titlepage} _______________________________________________ pypy-commit mailing list pypy-commit@python.org https://mail.python.org/mailman/listinfo/pypy-commit