Index: psyco_win32/psyco/profiler.py |
=================================================================== |
--- psyco_win32/psyco/profiler.py (revision 0) |
+++ psyco_win32/psyco/profiler.py (revision 0) |
@@ -0,0 +1,379 @@ |
+########################################################################### |
+# |
+# Psyco profiler (Python part). |
+# Copyright (C) 2001-2002 Armin Rigo et.al. |
+ |
+"""Psyco profiler (Python part). |
+ |
+The implementation of the non-time-critical parts of the profiler. |
+See profile() and full() in core.py for the easy interface. |
+""" |
+########################################################################### |
+ |
+import _psyco |
+from support import * |
+import math, time, types, atexit |
+now = time.time |
+try: |
+ import thread |
+except ImportError: |
+ import dummy_thread as thread |
+ |
+ |
+# current profiler instance |
+current = None |
+ |
+# enabled profilers, in order of priority |
+profilers = [] |
+ |
+# logger module (when enabled by core.log()) |
+logger = None |
+ |
+# a lock for a thread-safe go() |
+go_lock = thread.allocate_lock() |
+ |
+def go(stop=0): |
+ # run the highest-priority profiler in 'profilers' |
+ global current |
+ go_lock.acquire() |
+ try: |
+ prev = current |
+ if stop: |
+ del profilers[:] |
+ if prev: |
+ if profilers and profilers[0] is prev: |
+ return # best profiler already running |
+ prev.stop() |
+ current = None |
+ for p in profilers[:]: |
+ if p.start(): |
+ current = p |
+ if logger: # and p is not prev: |
+ logger.write("%s: starting" % p.__class__.__name__, 5) |
+ return |
+ finally: |
+ go_lock.release() |
+ # no profiler is running now |
+ if stop: |
+ if logger: |
+ logger.writefinalstats() |
+ else: |
+ tag2bind() |
+ |
+atexit.register(go, 1) |
+ |
+ |
+def buildfncache(globals, cache): |
+ if hasattr(types.IntType, '__dict__'): |
+ clstypes = (types.ClassType, types.TypeType) |
+ else: |
+ clstypes = types.ClassType |
+ for x in globals.values(): |
+ if isinstance(x, types.MethodType): |
+ x = x.im_func |
+ if isinstance(x, types.FunctionType): |
+ cache[x.func_code] = x, '' |
+ elif isinstance(x, clstypes): |
+ for y in x.__dict__.values(): |
+ if isinstance(y, types.MethodType): |
+ y = y.im_func |
+ if isinstance(y, types.FunctionType): |
+ cache[y.func_code] = y, x.__name__ |
+ |
+# code-to-function mapping (cache) |
+function_cache = {} |
+ |
+def trytobind(co, globals, log=1): |
+ try: |
+ f, clsname = function_cache[co] |
+ except KeyError: |
+ buildfncache(globals, function_cache) |
+ try: |
+ f, clsname = function_cache[co] |
+ except KeyError: |
+ if logger: |
+ logger.write('warning: cannot find function %s in %s' % |
+ (co.co_name, globals.get('__name__', '?')), 3) |
+ return # give up |
+ if logger and log: |
+ modulename = globals.get('__name__', '?') |
+ if clsname: |
+ modulename += '.' + clsname |
+ logger.write('bind function: %s.%s' % (modulename, co.co_name), 1) |
+ f.func_code = _psyco.proxycode(f) |
+ |
+ |
+# the list of code objects that have been tagged |
+tagged_codes = [] |
+ |
+def tag(co, globals): |
+ if logger: |
+ try: |
+ f, clsname = function_cache[co] |
+ except KeyError: |
+ buildfncache(globals, function_cache) |
+ try: |
+ f, clsname = function_cache[co] |
+ except KeyError: |
+ clsname = '' # give up |
+ modulename = globals.get('__name__', '?') |
+ if clsname: |
+ modulename += '.' + clsname |
+ logger.write('tag function: %s.%s' % (modulename, co.co_name), 1) |
+ tagged_codes.append((co, globals)) |
+ _psyco.turbo_frame(co) |
+ _psyco.turbo_code(co) |
+ |
+def tag2bind(): |
+ if tagged_codes: |
+ if logger: |
+ logger.write('profiling stopped, binding %d functions' % |
+ len(tagged_codes), 2) |
+ for co, globals in tagged_codes: |
+ trytobind(co, globals, 0) |
+ function_cache.clear() |
+ del tagged_codes[:] |
+ |
+ |
+class Profiler: |
+ MemoryTimerResolution = 0.103 |
+ |
+ def run(self, memory, time, memorymax, timemax): |
+ self.memory = memory |
+ self.memorymax = memorymax |
+ self.time = time |
+ if timemax is None: |
+ self.endtime = None |
+ else: |
+ self.endtime = now() + timemax |
+ self.alarms = [] |
+ profilers.append(self) |
+ go() |
+ |
+ def start(self): |
+ curmem = _psyco.memory() |
+ memlimits = [] |
+ if self.memorymax is not None: |
+ if curmem >= self.memorymax: |
+ if logger: |
+ logger.writememory() |
+ return self.limitreached('memorymax') |
+ memlimits.append(self.memorymax) |
+ if self.memory is not None: |
+ if self.memory <= 0: |
+ if logger: |
+ logger.writememory() |
+ return self.limitreached('memory') |
+ memlimits.append(curmem + self.memory) |
+ self.memory_at_start = curmem |
+ |
+ curtime = now() |
+ timelimits = [] |
+ if self.endtime is not None: |
+ if curtime >= self.endtime: |
+ return self.limitreached('timemax') |
+ timelimits.append(self.endtime - curtime) |
+ if self.time is not None: |
+ if self.time <= 0.0: |
+ return self.limitreached('time') |
+ timelimits.append(self.time) |
+ self.time_at_start = curtime |
+ |
+ try: |
+ self.do_start() |
+ except error, e: |
+ if logger: |
+ logger.write('%s: disabled by psyco.error:' % ( |
+ self.__class__.__name__), 4) |
+ logger.write(' %s' % str(e), 3) |
+ return 0 |
+ |
+ if memlimits: |
+ self.memlimits_args = (time.sleep, (self.MemoryTimerResolution,), |
+ self.check_memory, (min(memlimits),)) |
+ self.alarms.append(_psyco.alarm(*self.memlimits_args)) |
+ if timelimits: |
+ self.alarms.append(_psyco.alarm(time.sleep, (min(timelimits),), |
+ self.time_out)) |
+ return 1 |
+ |
+ def stop(self): |
+ for alarm in self.alarms: |
+ alarm.stop(0) |
+ for alarm in self.alarms: |
+ alarm.stop(1) # wait for parallel threads to stop |
+ del self.alarms[:] |
+ if self.time is not None: |
+ self.time -= now() - self.time_at_start |
+ if self.memory is not None: |
+ self.memory -= _psyco.memory() - self.memory_at_start |
+ |
+ try: |
+ self.do_stop() |
+ except error: |
+ return 0 |
+ return 1 |
+ |
+ def check_memory(self, limit): |
+ if _psyco.memory() < limit: |
+ return self.memlimits_args |
+ go() |
+ |
+ def time_out(self): |
+ self.time = 0.0 |
+ go() |
+ |
+ def limitreached(self, limitname): |
+ try: |
+ profilers.remove(self) |
+ except ValueError: |
+ pass |
+ if logger: |
+ logger.write('%s: disabled (%s limit reached)' % ( |
+ self.__class__.__name__, limitname), 4) |
+ return 0 |
+ |
+ |
+class FullCompiler(Profiler): |
+ |
+ def do_start(self): |
+ _psyco.profiling('f') |
+ |
+ def do_stop(self): |
+ _psyco.profiling('.') |
+ |
+ |
+class RunOnly(Profiler): |
+ |
+ def do_start(self): |
+ _psyco.profiling('n') |
+ |
+ def do_stop(self): |
+ _psyco.profiling('.') |
+ |
+ |
+class ChargeProfiler(Profiler): |
+ |
+ def __init__(self, watermark, parentframe): |
+ self.watermark = watermark |
+ self.parent2 = parentframe * 2.0 |
+ self.lock = thread.allocate_lock() |
+ |
+ def init_charges(self): |
+ _psyco.statwrite(watermark = self.watermark, |
+ parent2 = self.parent2) |
+ |
+ def do_stop(self): |
+ _psyco.profiling('.') |
+ _psyco.statwrite(callback = None) |
+ |
+ |
+class ActiveProfiler(ChargeProfiler): |
+ |
+ def active_start(self): |
+ _psyco.profiling('p') |
+ |
+ def do_start(self): |
+ self.init_charges() |
+ self.active_start() |
+ _psyco.statwrite(callback = self.charge_callback) |
+ |
+ def charge_callback(self, frame, charge): |
+ tag(frame.f_code, frame.f_globals) |
+ |
+ |
+class PassiveProfiler(ChargeProfiler): |
+ |
+ initial_charge_unit = _psyco.statread('unit') |
+ reset_stats_after = 120 # half-lives (maximum 200!) |
+ reset_limit = initial_charge_unit * (2.0 ** reset_stats_after) |
+ |
+ def __init__(self, watermark, halflife, pollfreq, parentframe): |
+ ChargeProfiler.__init__(self, watermark, parentframe) |
+ self.pollfreq = pollfreq |
+ # self.progress is slightly more than 1.0, and computed so that |
+ # do_profile() will double the change_unit every 'halflife' seconds. |
+ self.progress = 2.0 ** (1.0 / (halflife * pollfreq)) |
+ |
+ def reset(self): |
+ _psyco.statwrite(unit = self.initial_charge_unit, callback = None) |
+ _psyco.statreset() |
+ if logger: |
+ logger.write("%s: resetting stats" % self.__class__.__name__, 1) |
+ |
+ def passive_start(self): |
+ self.passivealarm_args = (time.sleep, (1.0 / self.pollfreq,), |
+ self.do_profile) |
+ self.alarms.append(_psyco.alarm(*self.passivealarm_args)) |
+ |
+ def do_start(self): |
+ tag2bind() |
+ self.init_charges() |
+ self.passive_start() |
+ |
+ def do_profile(self): |
+ _psyco.statcollect() |
+ if logger: |
+ logger.dumpcharges() |
+ nunit = _psyco.statread('unit') * self.progress |
+ if nunit > self.reset_limit: |
+ self.reset() |
+ else: |
+ _psyco.statwrite(unit = nunit, callback = self.charge_callback) |
+ return self.passivealarm_args |
+ |
+ def charge_callback(self, frame, charge): |
+ trytobind(frame.f_code, frame.f_globals) |
+ |
+ |
+class ActivePassiveProfiler(PassiveProfiler, ActiveProfiler): |
+ |
+ def do_start(self): |
+ self.init_charges() |
+ self.active_start() |
+ self.passive_start() |
+ |
+ def charge_callback(self, frame, charge): |
+ tag(frame.f_code, frame.f_globals) |
+ |
+ |
+ |
+# |
+# we register our own version of sys.settrace(), sys.setprofile() |
+# and thread.start_new_thread(). |
+# |
+ |
+def psyco_settrace(*args, **kw): |
+ "This is the Psyco-aware version of sys.settrace()." |
+ result = original_settrace(*args, **kw) |
+ go() |
+ return result |
+ |
+def psyco_setprofile(*args, **kw): |
+ "This is the Psyco-aware version of sys.setprofile()." |
+ result = original_setprofile(*args, **kw) |
+ go() |
+ return result |
+ |
+def psyco_thread_stub(callable, args, kw): |
+ _psyco.statcollect() |
+ if kw is None: |
+ return callable(*args) |
+ else: |
+ return callable(*args, **kw) |
+ |
+def psyco_start_new_thread(callable, args, kw=None): |
+ "This is the Psyco-aware version of thread.start_new_thread()." |
+ return original_start_new_thread(psyco_thread_stub, (callable, args, kw)) |
+ |
+original_settrace = sys.settrace |
+original_setprofile = sys.setprofile |
+original_start_new_thread = thread.start_new_thread |
+sys.settrace = psyco_settrace |
+sys.setprofile = psyco_setprofile |
+thread.start_new_thread = psyco_start_new_thread |
+# hack to patch threading._start_new_thread if the module is |
+# already loaded |
+if ('threading' in sys.modules and |
+ hasattr(sys.modules['threading'], '_start_new_thread')): |
+ sys.modules['threading']._start_new_thread = psyco_start_new_thread |
Property changes on: psyco_win32\psyco\profiler.py |
___________________________________________________________________ |
Added: svn:eol-style |
+ LF |