Download - Stack switching for fun and profit
![Page 1: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/1.jpg)
Stack switchingfor fun & profitSaúl Ibarra Corretgé - @saghul
FOSDEM 2014
![Page 2: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/2.jpg)
Hi!
@saghul
FOSDEM
Open Source
![Page 3: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/3.jpg)
import open_source
github.com/saghul
![Page 4: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/4.jpg)
Some background
Who knows what greenlet is?
Who has used it (standalone)?
Who understands how it works?
![Page 5: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/5.jpg)
Greenlet
Micro-threads with no implicit scheduling
Lightweight
Only one can run at a time
Spin-off Stackless
![Page 6: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/6.jpg)
Greenlet APIgreenlet(func, parent=None): creates a greenlet to run ‘func’
greenlet.switch(*args, **kw): switches execution to the target greenlet, the first timefunc(*args, **kw) will be executed
greenlet.throw([typ, [val, [tb]]]): switches execution to the target greenlet and raises the specified exception (GreenletExit by default)
![Page 7: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/7.jpg)
Exampleimport greenlet
main = greenlet.getcurrent()
def foo(n): main.switch(n)
def bar(n): foo(n) return 'hello'
g1 = greenlet.greenlet(bar)print g1.switch(42) # 42print g1.switch() # 'hello'print g1.dead # True
![Page 8: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/8.jpg)
How does it work?‘Stack switching’
Non portable asm code
Copy stack slices on the heap
State is saved and restored when switching
CPU registers
Current Python frame, recursion depth and exception state
![Page 9: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/9.jpg)
![Page 10: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/10.jpg)
![Page 11: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/11.jpg)
![Page 12: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/12.jpg)
How does it work? (II)
Organized in a tree structure
Each greenlet has a ‘parent’, except main
Execution order isn’t always obvious
![Page 13: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/13.jpg)
Example (II)import greenlet
main = greenlet.getcurrent()
def foo_async(cb): # Will call cb(result, error) eventually pass
def foo_sync(): current = greenlet.getcurrent() def cb(result, error): if error is not None: current.throw(Exception(error)) else: current.switch(result) foo_async(cb) main.switch()
![Page 14: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/14.jpg)
Stackless Python
Fork of CPython, first release in 1999
Provides tasklets and channels
Builtin scheduler
Different approaches to switching
![Page 15: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/15.jpg)
Stackless Python (II)Different ways of switching: soft and hard
Soft switching
“Move some pointers around”
Hard switching
Platform dependent assembly code
When soft-switching is not possible
![Page 16: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/16.jpg)
Enter PyPy
New shiny and fast implementation of Python
Vast amount of fairy-dust covered unicorns included
Includes implementations of both greenlet and Stackless
Implemented on top of “continulet” objects
![Page 17: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/17.jpg)
import _continuation
Continulets are one-shot continuations
Switching code is a standalone C library: stacklet
rpython/translator/c/src/stacklet/
![Page 18: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/18.jpg)
Continulet APIcontinulet(func, *args, **kw): create a continulet object which will call fun(cont, *args, **kw)
continulet.switch(value=None, to=None): start the continulet or activate the previously suspended one. If to is specified a ‘double switch’ is performed
continulet.throw(type, value=None, tb=None, to=None): similar to switch, but raise the given exception after the switch is done
![Page 19: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/19.jpg)
Stacklet
Tiny library implementing one-shot continuations for C
Single C file (~400 lines) + per-platform asm
Supports x86, x86_64 and ARM
Nice and simple API
![Page 20: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/20.jpg)
Stacklet API
stacklet_newthread(): creates a new thread handle
stacklet_new(thread_handle, run_func, run_arg): calls run(arg) in a new stacklet, starts immediately
stacklet_switch(target): switches execution to target stacklet
![Page 21: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/21.jpg)
Example#include <assert.h>#include "stacklet.h"
static stacklet_thread_handle thrd;
stacklet_handle empty_callback(stacklet_handle h, void *arg){ assert(arg == (void *)123); return h;}
void test_new(void){ stacklet_handle h = stacklet_new(thrd, empty_callback, (void *)123); assert(h == EMPTY_STACKLET_HANDLE);}
int main(int argc, char **argv){ thrd = stacklet_newthread(); test_new(); stacklet_deletethread(thrd); return 0;}
![Page 22: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/22.jpg)
import fibersMicro-threadling library API inspired by Python threads and greenlet
Uses stacklet underneath
Works on CPython and PyPy
On PyPy it uses continulets
github.com/saghul/python-fibers
Or pip install fibers
![Page 23: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/23.jpg)
fibers APIFiber(target=None, args=(), kwargs={}, parent=None): creates a new fiber which will run “target(*args, **kwargs)”
Fiber.switch(value=None): switch execution to the target fiber, value can only be passed after the fiber is running
Fiber.throw(typ, [value, [tb]]): switch execution to the target fiber and raise the specified exception
![Page 24: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/24.jpg)
Motivation
Couldn’t build the API I wanted on top of greenlet
Early binding
More exceptions in expected places
No magic exceptions (GreenletExit)
No switching on GC
![Page 25: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/25.jpg)
Exampleimport fibers
main = fibers.current()
def foo(n): main.switch(n)
def bar(n): foo(n) return 'hello'
g1 = fibers.Fiber(target=bar, args=(42,))print g1.switch() # 42print g1.switch() # 'hello'print g1.is_alive() # False
![Page 26: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/26.jpg)
Projects using fibers
github.com/saghul/evergreen
github.com/geertj/gruvi
github.com/benoitc/offset
Should I add yours here?
![Page 27: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/27.jpg)
The Stacklet Sandwich (TM)
stacklet
continulet
greenlet / stackless / fibers
![Page 28: Stack switching for fun and profit](https://reader034.vdocument.in/reader034/viewer/2022052505/555a2219d8b42ab3088b4724/html5/thumbnails/28.jpg)
Questions?
bettercallsaghul.com@saghul