Holy Cow, Galcon runs in Flash!
Phil Hassey, noted Python/Pygame enthusiast has ported his Award Winning Galcon game to Flash! Awesome!
I'd recommend limiting yourself to 1 match per day, else you'll be joining me at Galcon Anonymous meetings.
The blog of Simon Wittber.
Phil Hassey, noted Python/Pygame enthusiast has ported his Award Winning Galcon game to Flash! Awesome!
I'd recommend limiting yourself to 1 match per day, else you'll be joining me at Galcon Anonymous meetings.
Posted by
Simon Wittber
at
Wednesday, June 24, 2009
2
comments
I decided to double check my assumptions about Stackless and the GIL on a multicore machine, using the same example provided by David Beazley in his slides, which highlights the GIL contention issue. It's probably a better example than my previous test.
import stacklessA normal sequential execution takes about 17 seconds.
import time
import threading
def count(n):
while n > 0:
n -= 1
T = time.clock()
count(100000000)
count(100000000)
print time.clock() - T
>>> 17.37
T = time.clock()This confirms David's experiment. It takes much longer due to threads fighting over the GIL.
t1 = threading.Thread(target=count,args=(100000000,))
t1.start()
t2 = threading.Thread(target=count,args=(100000000,))
t2.start()
t1.join(); t2.join()
print time.clock() - T
>>> 53.22
T = time.clock()Twice as fast as the threaded solution, and roughly 50% slower than than the sequential solution. Cool.
stackless.tasklet(count)(100000000)
stackless.tasklet(count)(100000000)
while stackless.getruncount() > 1:
task = stackless.run(100)
if task:
task.insert()
print time.clock() - T
>>> 25.34
Posted by
Simon Wittber
at
Tuesday, June 16, 2009
5
comments
Labels: Python
Following on from my last post, I decided I should check my assertion that "Stackless will outperform CPython, even with CPU bound tasks." I'm not saying the GIL is bad, I'm just pointing out how the same behavior can be achieved with Stackless. If these tasks called some random C function which blocked, but still yielded the GIL, it is likely that CPython would come out on top.
import timeOn my dual core machine, Stackless performs around 30%-40% faster than regular threads. This is usually not a suprise, we all know that IO bound threads always come with a penalty in CPython. However, these Stackless tasklets are being pre-empted in the same way that the GIL works. This is something my Fibra framework and other similar frameworks which are based on generators, can never achieve.
from threading import Thread
import stackless
def factorial(c):
T = 1
for i in xrange(1, c):
T *= i
return T
#Benchmark stackless using 500 tasklets
T = time.clock()
for i in xrange(500):
stackless.tasklet(factorial)(1024)
while stackless.getruncount() > 1:
task = stackless.run(100)
if task:
task.insert()
print time.clock() - T
#Benchmark OS threads using 500 threads
T = time.clock()
threads = []
for i in xrange(500):
thread = Thread(target=factorial, args=(1024,))
thread.start()
threads.append(thread)
for thread in threads: thread.join()
print time.clock() - T
>>> 0.5
>>> 0.77
Posted by
Simon Wittber
at
Sunday, June 14, 2009
1 comments
Labels: Python
After thinking some more about David Beazley's research into the GIL, I realized the gravity of his comment that the GIL provides "Basically a kind of "cooperative" multitasking". I've spent 10 minutes knocking up some code in Stackless that demonstrates something similar to what David describes. I doubt many people knew Stackless has this capability.
import stackless
#A contrived CPU bound task
def factorial(c):
T = 1
for i in xrange(1, c):
T *= i
return T
#create two tasklets
stackless.tasklet(factorial)(512)
stackless.tasklet(factorial)(1024)
#used to track of how many task switches happen
switches = {}
#while there is more than the main task running...
while stackless.getruncount() > 1:
#run the schedule for 100 ticks
task = stackless.run(100)
#if we have a pre-empted task
if task:
#increment it's switch counter
C = switches.setdefault(task, 0)
switches[task] += 1
#insert it at the end of the schedule
task.insert()
print switches.values()
>>> [13, 26]
Posted by
Simon Wittber
at
Sunday, June 14, 2009
2
comments
Labels: Python
Most Python coders know that the Global Interpreter Lock (GIL) in CPython effectively serializes execution of threaded programs, even on systems with multiple CPU cores.
Despite this, Python threads are still useful in some situations, especially when working with blocking IO. Or so we thought.
The GIL problem is much, much worse than I expected.
David Beazley has published some slides which summarize his research into the GIL, which demonstrate how the implementation is fundamentally broken. The slides are clear and easy to understand and it is definitely worth taking the time to read and digest.
To summarize, the implementation of the GIL causes threads (on multicore machines) to compete with each other as they attempt to acquire the GIL. The scary part is, as you add more cores and more threads, the problem will grow exponentially, causing a massive degradation in performance when compared to serial execution. This isn't the only issue. Read the slides if you ever intend to use threads in CPython.
Posted by
Simon Wittber
at
Saturday, June 13, 2009
14
comments
Labels: Python