Stackless Python In Eve

1,822 views
1,733 views

Published on

Eine schöne Präsentation was mit Stackless Python so möglich ist.

0 Comments
1 Like
Statistics
Notes
  • Be the first to comment

No Downloads
Views
Total views
1,822
On SlideShare
0
From Embeds
0
Number of Embeds
14
Actions
Shares
0
Downloads
0
Comments
0
Likes
1
Embeds 0
No embeds

No notes for slide

Stackless Python In Eve

  1. 1. Stackless Python in EVE Kristján Valur Jónsson [email_address] CCP Games inc.
  2. 2. EVE <ul><li>MMORPG Space game </li></ul><ul><li>Client / server </li></ul><ul><li>Single shard massive server </li></ul><ul><li>130.000 active players, >24.000 concurrent users </li></ul><ul><li>World concurrency record on a shard </li></ul><ul><li>425,537 lines of Python code and growing. </li></ul><ul><ul><li>client: 158,656 </li></ul></ul><ul><ul><li>server: 218,645 </li></ul></ul><ul><ul><li>common: 48,236 </li></ul></ul><ul><li>Possible only because of Stackless Python </li></ul>
  3. 3. The Tranquility cluster <ul><li>400 GHz CPU / 200 Gb RAM </li></ul><ul><li>2 Routers (CISCO Alteon) </li></ul><ul><li>14 Proxy servers (IBM Blade) </li></ul><ul><li>55 Sol servers (IBM x335) </li></ul><ul><li>2 DB servers (clustered, IBM Brick x445) </li></ul><ul><li>FastT600 Fiber, 56 x FC 15k disks, DS4300 + 3*EXP700 </li></ul><ul><li>Windows 2000, MS SQL Server </li></ul><ul><li>Currently being upgraded </li></ul><ul><ul><li>AMD x64 IBM Blade </li></ul></ul><ul><ul><li>RAMSAN gizmo </li></ul></ul><ul><ul><li>64 bit binaries </li></ul></ul>
  4. 4. EVE Architecture <ul><li>COM-like basic architecture </li></ul><ul><li>Python tightly integrated at an early stage </li></ul><ul><li>Home-grown wrapping of BLUE objects </li></ul>
  5. 5. Stackless in EVE <ul><li>BLUE foundation: robust, but cumbersome </li></ul><ul><li>RAD </li></ul><ul><li>Stackless Python: Python and so much more </li></ul><ul><li>EVE is inconceivable without Stackless </li></ul><ul><li>Everyone is a programmer </li></ul><ul><li>Interactive development. </li></ul><ul><ul><li>Files reloaded automatically </li></ul></ul><ul><ul><li>Classes updated with new methods </li></ul></ul><ul><ul><li>Demo: </li></ul></ul>
  6. 6. Stackless Python <ul><li>Tasklets </li></ul><ul><ul><li>Threads of execution. Not OS threads </li></ul></ul><ul><ul><li>Lightweight </li></ul></ul><ul><ul><li>No pre-emption </li></ul></ul><ul><ul><li>No memory fragmentation due to stacks </li></ul></ul><ul><li>Channels </li></ul><ul><ul><li>Tasklet rendezvous point </li></ul></ul><ul><ul><li>Data passing </li></ul></ul><ul><ul><li>Scheduling </li></ul></ul><ul><ul><li>Synchronization </li></ul></ul>
  7. 7. Stackless? <ul><li>No C stack </li></ul><ul><ul><li>Python stack in linked frame objects </li></ul></ul><ul><ul><li>Tasklet switching by swapping frame chain </li></ul></ul><ul><li>Compromise </li></ul><ul><ul><li>stackless where possible. </li></ul></ul><ul><ul><li>C stack whisked away if necessary </li></ul></ul>
  8. 8. Channels
  9. 9. Channel semantics <ul><li>Send on a channel with no receiver blocks tasklet. </li></ul><ul><li>Send on a channel with a (blocked) receiver, suspends tasklet and runs receiver immediately. Sender runs again in due course. </li></ul><ul><li>Symmetric behaviour. </li></ul><ul><li>“ Balance”, can have a queue of readers or writers. </li></ul><ul><li>Conceptually similar to Unix pipes </li></ul>
  10. 10. Channel semantics, cont. <ul><li>Scheduling semantics are precise: </li></ul><ul><ul><li>A blocked tasklet is run immediately </li></ul></ul><ul><li>Usable as a building block: </li></ul><ul><ul><li>semaphores </li></ul></ul><ul><ul><li>mutex </li></ul></ul><ul><ul><li>critical section </li></ul></ul><ul><ul><li>condition variables </li></ul></ul>
  11. 11. The main loop <ul><li>Establish stackless context </li></ul><ul><li>int WinMain(...) { </li></ul><ul><ul><li>PyObject *myApp = new EveApp(); </li></ul></ul><ul><ul><li>PyObject *r = PyStackless_CallMethod_Main(myApp, “WinMain”, 0); </li></ul></ul><ul><ul><li>return PyInt_AsLong( r ); </li></ul></ul>
  12. 12. <ul><li>Regular Windows message loop </li></ul><ul><li>Runs in Stackless context </li></ul><ul><li>The “Main Tasklet” </li></ul>The main loop cont. <ul><li>PyObject* EveApp::WinMain(PyObject *self, PyObject *args) { </li></ul><ul><li>while(!mExit) { </li></ul><ul><ul><li>PyOS->ExecFile(&quot;script:/sys/autoexec.py&quot;); </li></ul></ul><ul><ul><li>MSG msg; </li></ul></ul><ul><ul><li>while(PeekMessage(&msg, 0, 0, 0, PM_REMOVE)){ </li></ul></ul><ul><ul><ul><li>TranslateMessage(&msg); </li></ul></ul></ul><ul><ul><ul><li>DispatchMessage(&msg); </li></ul></ul></ul><ul><ul><ul><li>} </li></ul></ul></ul><ul><ul><li>for (TickIt i = mTickers.begin(; i != mTickers.end(); i++) </li></ul></ul><ul><ul><li>i->mCb-> OnTick (mTime, (void*)taskname); </li></ul></ul><ul><ul><li>} </li></ul></ul><ul><li>} </li></ul>
  13. 13. Autoexec.py import blue def Startup(): import service srvMng = service.ServiceManager() run = [&quot;dataconfig&quot;, &quot;godma&quot;, “ui&quot;, …] srvMng.Run(run) #Start up the client in a tasklet! if CheckDXVersion(): import blue blue.pyos. CreateTasklet (Startup, (), {})
  14. 14. Tickers <ul><li>Tickers are BLUE modules: </li></ul><ul><ul><li>Trinity (the renderer) </li></ul></ul><ul><ul><li>Netclient </li></ul></ul><ul><ul><li>DB (on the server) </li></ul></ul><ul><ul><li>Audio </li></ul></ul><ul><ul><li>PyOS (special python services) </li></ul></ul><ul><ul><li>… </li></ul></ul>
  15. 15. The PyOS tick: <ul><li>Runs fresh tasklets </li></ul><ul><ul><li>(sleepers awoken elsewhere) </li></ul></ul>Tick() { … mSynchro->Tick() PyObject *watchdogResult; do { watchdogResult = PyStackless_RunWatchdog(20000000); if (!watchdogResult) PyFlushError(&quot;PumpPython::Watchdog&quot;); Py_XDECREF(watchdogresult); } while (!watchdogResult);
  16. 16. blue.pyos.synchro <ul><li>Synchro: </li></ul><ul><ul><li>Provides Thread-like tasklet utilities: </li></ul></ul><ul><ul><ul><li>Sleep(ms) </li></ul></ul></ul><ul><ul><ul><li>Yield() </li></ul></ul></ul><ul><ul><ul><li>BeNice() </li></ul></ul></ul>
  17. 17. <ul><li>Sleep: A python script makes the call blue.pyos.Sleep(200) </li></ul><ul><li>C++ code runs: </li></ul><ul><ul><li>Main tasklet check </li></ul></ul><ul><ul><li>sleeper = New Sleeper(); mSleepers.insert(sleeper); PyObject *r = PyChannel_Receive(sleeper->mChannel); </li></ul></ul><ul><ul><li>Another tasklet runs </li></ul></ul>blue.pyos.synchro cont.
  18. 18. blue.pyos.synchro, ctd. <ul><li>Main tasklet in windows loop enters PyOS::Tick() </li></ul><ul><li>mSleepers are examined for all that are due we do: </li></ul><ul><ul><li>mSleepers.remove(sleeper); PyChannel_Send(sleepers.mChannel, Py_NONE); </li></ul></ul><ul><li>Main tasklet is suspended (but runnable), sleeper runs. </li></ul>
  19. 19. Points to note: <ul><li>A tasklet goes to sleep by calling PyChannel_Receive() on a channel which has no pending sender. </li></ul><ul><li>It will sleep there (block) until someone sends </li></ul><ul><li>Typically the main tasklet does this, doing PyChannel_Send() on a channel with a reader </li></ul><ul><li>Ergo: The main tasklet may not block </li></ul>
  20. 20. Socket Receive <ul><li>Use Windows asynchronous file API </li></ul><ul><li>Provide a synchronous python API. A python script calls Read(). </li></ul><ul><li>Tasklet may be blocked for a long time, (many frames) other tasklets continue running. </li></ul><ul><li>Do this using channels. </li></ul>
  21. 21. Receive, cont. <ul><li>Python script runs: data = socket.Read() </li></ul><ul><li>C code executes the request: </li></ul><ul><ul><li>Request *r = new Request(this); WSAReceive(mSocket, …); mRequests->insert( r ); PyChannel_Receive(r->mChannel); </li></ul></ul><ul><li>Tasklet is blocked </li></ul>
  22. 22. Receive, cont. <ul><li>Socket server is ticked from main loop </li></ul><ul><li>For all requests that are marked completed, it transfers the data to the sleeping tasklets: </li></ul><ul><li>PyObject *r = PyString_FromStringAndSize(req->mData, req->mDataLen); PyChannel_Send(req->mChannel, r); Py_DECREF(data); delete req; </li></ul><ul><li>The sleeping tasklet wakes up, main tasklet is suspended (but runnable) </li></ul>
  23. 23. Receive completed
  24. 24. Main Tasklet <ul><li>The one running the windows loop </li></ul><ul><li>Can be suspended, allowing other tasklets to run </li></ul><ul><li>Can be blocked, as long as there is another tasklet to unblock it (dangerous) </li></ul><ul><li>Is responsible for waking up Sleepers, Yielders, IO tasklets, etc. therefore cannot be one of them </li></ul><ul><li>Is flagged as non-blockable ( stackless.get_current().block_trap = True ) </li></ul>
  25. 25. Channel magic <ul><li>Channels perform the stackless context switch. </li></ul><ul><li>If there is a C stack in the call chain, it will magically swap the stacks. </li></ul><ul><li>Your entire C stack (with C and python invocations) is whisked away and stored, to be replaced with a new one. </li></ul><ul><li>This allows stackless to simulate cooperative multi-threading </li></ul>
  26. 26. Co-operative multitasking <ul><li>Context is switched only at known points. </li></ul><ul><ul><li>In Stackless, this is channel.send() and channel.receive() </li></ul></ul><ul><ul><li>Also synchro.Yield(), synchro.Sleep(), BeNice(), socket and DB ops, etc. </li></ul></ul><ul><ul><ul><li>(all boil down to send() and receive() ) </li></ul></ul></ul><ul><li>No unexpected context switches </li></ul><ul><ul><li>Almost no race conditions </li></ul></ul><ul><ul><li>Program like you are single-threaded </li></ul></ul><ul><ul><li>Very few exceptions. </li></ul></ul><ul><ul><li>This extends to C state too! </li></ul></ul>
  27. 27. Tasklets <ul><li>Tasklets are cheap </li></ul><ul><ul><li>70,000 on a server node </li></ul></ul><ul><li>Used liberally to reduce perceived lag </li></ul><ul><ul><li>UI events forked out to tasklets </li></ul></ul><ul><ul><ul><li>A click can have heavy consequences. </li></ul></ul></ul><ul><ul><ul><ul><li>Heavy logic </li></ul></ul></ul></ul><ul><ul><ul><ul><li>DB Access </li></ul></ul></ul></ul><ul><ul><ul><ul><li>Networks access </li></ul></ul></ul></ul><ul><ul><li>special rendering tasks forked out to tasklets. </li></ul></ul><ul><ul><li>controlling an audio track </li></ul></ul><ul><li>“ tasklet it out” </li></ul><ul><li>Use blue.pyos.synchro.BeNice() in large loops </li></ul>
  28. 28. Example: UI Event: <ul><li>Main tasklet receives window messages such as WM_LBUTTONUP </li></ul><ul><li>Trinity invokes handler on UI elements or global handler </li></ul><ul><li>Handler “tasklets out” any action to allow main thread to continue immediately. </li></ul>def OnGlobalUp(self, *args): mo = eve.triapp.uilib.mouseOver if mo in self.children: uthread.new(mo._OnClick) class Action(xtriui.QuickDeco): def _OnClick(self, *args): pass
  29. 29. What we don’t understand: <ul><li>Why isn’t everyone using Stackless Python? </li></ul>
  30. 30. That’s all <ul><li>For more info: </li></ul><ul><ul><li>http://www.ccpgames.com </li></ul></ul><ul><ul><li>http://www.eve-online.com </li></ul></ul><ul><ul><li>http://www.stackless.com </li></ul></ul><ul><ul><li>[email_address] </li></ul></ul>

×