Re: [Python-ideas] [Python-Dev] GIL behaviour under Windows

Antoine Pitrou skrev:
My critisism of the GIL on python-ideas was partly motivated by this: http://blip.tv/file/2232410 However, David Beazley is not talking about Windows. Since the GIL is apparently not a mutex on Windows, it could behave differently. So I wrote a small script that contructs a GIL battle, and record how often a check-interval results in a thread-switch or not. For monitoring check intervals, I used a small C extension to read _Py_Ticker from ceval.c. It is not declared static so I could easily hack into it. With two threads and a check interval og 100, only 61 of 100000 check intervals failed to produce a thread-switch in the interpreter. I'd call that rather fair. :-) And in case someone asks, the nthreads=1 case is just for verification. S.M. D:\>test.py check interval = 1 nthreads=1, swiched=0, missed=100000 nthreads=2, swiched=57809, missed=42191 nthreads=3, swiched=91535, missed=8465 nthreads=4, swiched=99751, missed=249 nthreads=5, swiched=95839, missed=4161 nthreads=6, swiched=100000, missed=0 D:\>test.py check interval = 10 nthreads=1, swiched=0, missed=100000 nthreads=2, swiched=99858, missed=142 nthreads=3, swiched=99992, missed=8 nthreads=4, swiched=100000, missed=0 nthreads=5, swiched=100000, missed=0 nthreads=6, swiched=100000, missed=0 D:\>test.py check interval = 100 nthreads=1, swiched=0, missed=100000 nthreads=2, swiched=99939, missed=61 nthreads=3, swiched=100000, missed=0 nthreads=4, swiched=100000, missed=0 nthreads=5, swiched=100000, missed=0 nthreads=6, swiched=100000, missed=0 D:\>test.py check interval = 1000 nthreads=1, swiched=0, missed=100000 nthreads=2, swiched=99999, missed=1 nthreads=3, swiched=100000, missed=0 nthreads=4, swiched=100000, missed=0 nthreads=5, swiched=100000, missed=0 nthreads=6, swiched=100000, missed=0

Sturla Molden skrev:
Anyway, if anyone wants to run a GIL battle, here is the code I used. If it turns out the GIL is far worse with pthreads, as it is implemented with a mutex, it might be a good idea to reimplement it with an event object as it is on Windows. Sturla Molden ---------------- In python: from giltest import * from time import clock import threading import sys def thread(rank, battle, start): while not start.isSet(): if rank == 0: start.set() try: while 1: battle.record(rank) except: pass if __name__ == '__main__': sys.setcheckinterval(1000) print "check interval = %d" % sys.getcheckinterval() for nthreads in range(1,7): start = threading.Event() battle = GIL_Battle(100000) threads = [threading.Thread(target=thread, args=(i,battle,start)) for i in range(1,nthreads)] for t in threads: t.setDaemon(True) t.start() thread(0, battle, start) for t in threads: t.join() s,m = battle.report() print "nthreads=%d, swiched=%d, missed=%d" % (nthreads, s, m) In Cython or Pyrex: from exceptions import Exception cdef extern from *: ctypedef int vint "volatile int" vint _Py_Ticker class StopBattle(Exception): pass cdef class GIL_Battle: """ tests the fairness of the GIL """ cdef vint prev_tick, prev_rank, switched, missed cdef int trials def __cinit__(GIL_Battle self, int trials=100000): self.prev_tick = _Py_Ticker self.prev_rank = -1 self.missed = 0 self.switched = 0 self.trials = trials def record(GIL_Battle self, int rank): if self.trials == self.switched + self.missed: raise StopBattle if self.prev_rank == -1: self.prev_tick = _Py_Ticker self.prev_rank = rank else: if _Py_Ticker > self.prev_tick: if self.prev_rank == rank: self.missed += 1 else: self.switched += 1 self.prev_tick = _Py_Ticker self.prev_rank = rank else: self.prev_tick = _Py_Ticker def report(GIL_Battle self): return int(self.switched), int(self.missed)

Sturla Molden skrev:
Anyway, if anyone wants to run a GIL battle, here is the code I used. If it turns out the GIL is far worse with pthreads, as it is implemented with a mutex, it might be a good idea to reimplement it with an event object as it is on Windows. Sturla Molden ---------------- In python: from giltest import * from time import clock import threading import sys def thread(rank, battle, start): while not start.isSet(): if rank == 0: start.set() try: while 1: battle.record(rank) except: pass if __name__ == '__main__': sys.setcheckinterval(1000) print "check interval = %d" % sys.getcheckinterval() for nthreads in range(1,7): start = threading.Event() battle = GIL_Battle(100000) threads = [threading.Thread(target=thread, args=(i,battle,start)) for i in range(1,nthreads)] for t in threads: t.setDaemon(True) t.start() thread(0, battle, start) for t in threads: t.join() s,m = battle.report() print "nthreads=%d, swiched=%d, missed=%d" % (nthreads, s, m) In Cython or Pyrex: from exceptions import Exception cdef extern from *: ctypedef int vint "volatile int" vint _Py_Ticker class StopBattle(Exception): pass cdef class GIL_Battle: """ tests the fairness of the GIL """ cdef vint prev_tick, prev_rank, switched, missed cdef int trials def __cinit__(GIL_Battle self, int trials=100000): self.prev_tick = _Py_Ticker self.prev_rank = -1 self.missed = 0 self.switched = 0 self.trials = trials def record(GIL_Battle self, int rank): if self.trials == self.switched + self.missed: raise StopBattle if self.prev_rank == -1: self.prev_tick = _Py_Ticker self.prev_rank = rank else: if _Py_Ticker > self.prev_tick: if self.prev_rank == rank: self.missed += 1 else: self.switched += 1 self.prev_tick = _Py_Ticker self.prev_rank = rank else: self.prev_tick = _Py_Ticker def report(GIL_Battle self): return int(self.switched), int(self.missed)
participants (1)
-
Sturla Molden