summaryrefslogtreecommitdiff
path: root/bind.py
diff options
context:
space:
mode:
authorroot <devnull@localhost>2006-04-26 10:48:09 +0000
committerroot <devnull@localhost>2006-04-26 10:48:09 +0000
commit8b1e1c104bdff504b3e775b450432e6462b8d09b (patch)
tree0367359f6a18f318741f387d82dc3dcfd8139950 /bind.py
downloadlogilab-common-8b1e1c104bdff504b3e775b450432e6462b8d09b.tar.gz
forget the past.
forget the past.
Diffstat (limited to 'bind.py')
-rw-r--r--bind.py272
1 files changed, 272 insertions, 0 deletions
diff --git a/bind.py b/bind.py
new file mode 100644
index 0000000..9f2b40f
--- /dev/null
+++ b/bind.py
@@ -0,0 +1,272 @@
+# This program is free software; you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation; either version 2 of the License, or (at your option) any later
+# version.
+#
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License along with
+# this program; if not, write to the Free Software Foundation, Inc.,
+# 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+""" Copyright (c) 2002-2003 LOGILAB S.A. (Paris, FRANCE).
+ http://www.logilab.fr/ -- mailto:contact@logilab.fr
+
+ This module provides a way to optimize globals in certain functions by binding
+ their names to values provided in a dictionnary
+"""
+
+__revision__ = '$Id: bind.py,v 1.8 2005-11-22 13:12:59 syt Exp $'
+
+# TODO: unit tests
+# * this module provide a function bind(func,vars) which replaces every
+# global variable 'm' by the value vars['m'] if such value exists in dict
+
+from dis import HAVE_ARGUMENT
+from new import code as make_code, function as make_function
+import inspect
+
+LOAD_GLOBAL = 116
+LOAD_CONST = 100
+EXTENDED_ARG = 143
+STORE_GLOBAL = 97
+
+def bind_code(co, globals):
+ """
+ Take a code object and a dictionnary and returns a new code object where
+ the opcodes LOAD_GLOBAL are replaced by LOAD_CONST whenever the global's
+ name appear in the dictionnary
+ """
+ consts = list(co.co_consts)
+ assigned = {}
+
+ code = co.co_code
+ new_code = ""
+ n = len(code)
+ i = 0
+ while i < n:
+ c = code[i]
+ op = ord(c)
+ i += 1
+ if op >= HAVE_ARGUMENT:
+ oparg = ord(code[i]) + ord(code[i+1]) * 256
+ i += 2
+ else:
+ oparg = None
+ if op == LOAD_GLOBAL:
+ name = co.co_names[oparg]
+ if globals.has_key(name):
+ k = assigned.get(name, None)
+ if k == None:
+ k = len(consts)
+ assigned[name] = len(consts)
+ consts.append(globals[name])
+ op = LOAD_CONST
+ oparg = k
+ new_code += chr(op)
+ if oparg is not None:
+ new_code += chr(oparg & 255)
+ new_code += chr( (oparg>>8) & 255 )
+
+ return make_code(co.co_argcount,
+ co.co_nlocals,
+ co.co_stacksize,
+ co.co_flags,
+ new_code,
+ tuple(consts),
+ co.co_names,
+ co.co_varnames,
+ co.co_filename,
+ co.co_name,
+ co.co_firstlineno,
+ co.co_lnotab )
+
+
+def bind(f, globals):
+ """Returns a new function whose code object has been
+ bound by bind_code()"""
+ newcode = bind_code(f.func_code, globals)
+ defaults = f.func_defaults or ()
+ return make_function(newcode, f.func_globals, f.func_name, defaults)
+
+if type(__builtins__) == dict:
+ builtins = __builtins__
+else:
+ builtins = __builtins__.__dict__
+
+bind_code_opt = bind(bind_code, builtins )
+bind_code_opt = bind(bind_code_opt, globals() )
+
+
+def optimize_module(m, global_consts):
+ if not inspect.ismodule(m):
+ raise TypeError
+ d = {}
+ for i in global_consts:
+ v = m.__dict__.get(i)
+ d[i] = v
+ builtins = m.__builtins__
+ for name, f in m.__dict__.items():
+ if inspect.isfunction(f):
+ f = bind(f, builtins)
+ if d:
+ f = bind(f, d)
+ m.__dict__[name] = f
+
+
+
+
+def analyze_code(co, globals, consts_dict, consts_list):
+ """Take a code object and a dictionnary and returns a
+ new code object where the opcodes LOAD_GLOBAL are replaced
+ by LOAD_CONST whenever the global's name appear in the
+ dictionnary"""
+ modified_globals = []
+ for c in co.co_consts:
+ if c not in consts_list:
+ consts_list.append(c)
+ modified = []
+ code = co.co_code
+ new_code = ""
+ n = len(code)
+ i = 0
+ extended_arg = 0
+ while i < n:
+ c = code[i]
+ op = ord(c)
+ i += 1
+ if op >= HAVE_ARGUMENT:
+ oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg
+ extended_arg = 0
+ i += 2
+ else:
+ oparg = None
+ if op == EXTENDED_ARG:
+ extended_arg = oparg*65536L
+
+ if op == LOAD_GLOBAL:
+ name = co.co_names[oparg]
+ if globals.has_key(name):
+ k = consts_dict.get(name, None)
+ if k == None:
+ k = len(consts_list)
+ consts_dict[name] = k
+ consts_list.append(globals[name])
+ if op == STORE_GLOBAL:
+ name = co.co_names[oparg]
+ if globals.has_key(name):
+ modified_globals.append(name)
+ return modified_globals
+
+def rewrite_code(co, consts_dict, consts_tuple):
+ """Take a code object and a dictionnary and returns a
+ new code object where the opcodes LOAD_GLOBAL are replaced
+ by LOAD_CONST whenever the global's name appear in the
+ dictionnary"""
+ code = co.co_code
+ new_code = ""
+ n = len(code)
+ i = 0
+ consts_list = list(consts_tuple)
+ while i < n:
+ c = code[i]
+ op = ord(c)
+ i += 1
+ extended_arg = 0
+ if op >= HAVE_ARGUMENT:
+ oparg = ord(code[i]) + ord(code[i+1])*256+extended_arg
+ extended_arg = 0
+ i += 2
+ else:
+ oparg = None
+ if op == EXTENDED_ARG:
+ extended_arg = oparg*65536L
+ elif op == LOAD_GLOBAL:
+ name = co.co_names[oparg]
+ k = consts_dict.get(name)
+ if k is not None:
+ op = LOAD_CONST
+ oparg = k
+ elif op == LOAD_CONST:
+ val = co.co_consts[oparg]
+ oparg = consts_list.index(val)
+ new_code += chr(op)
+ if oparg is not None:
+ new_code += chr(oparg & 255)
+ new_code += chr( (oparg>>8) & 255 )
+
+ return make_code(co.co_argcount,
+ co.co_nlocals,
+ co.co_stacksize,
+ co.co_flags,
+ new_code,
+ consts_tuple,
+ co.co_names,
+ co.co_varnames,
+ co.co_filename,
+ co.co_name,
+ co.co_firstlineno,
+ co.co_lnotab )
+
+def optimize_module_2(m, globals_consts, bind_builtins=1):
+ if not inspect.ismodule(m):
+ raise TypeError
+ consts_dict = {}
+ consts_list = []
+ if type(globals_consts) == list or type(globals_consts) == tuple:
+ globals = {}
+ for i in globals_consts:
+ v = m.__dict__.get(i)
+ globals[i] = v
+ else:
+ globals = globals_consts
+ if bind_builtins:
+ for builtin_name, builtin_value in m.__builtins__.items():
+ # this way it is possible to redefine a builtin in globals_consts
+ globals.setdefault(builtin_name, builtin_value)
+ functions = {}
+ for name, f in m.__dict__.items():
+ if inspect.isfunction(f):
+ functions[name] = f
+ analyze_code(f.func_code, globals, consts_dict, consts_list)
+ consts_list = tuple(consts_list)
+ for name, f in functions.items():
+ newcode = rewrite_code(f.func_code, consts_dict, consts_list)
+ defaults = f.func_defaults or ()
+ m.__dict__[name] = make_function(newcode, f.func_globals, f.func_name,
+ defaults)
+
+
+def run_bench(n):
+ from time import time
+ t = time()
+ g = globals()
+ for i in range(n):
+ test = bind(bind_code, g)
+ t1 = time()-t
+ bind2 = bind(bind, {'bind_code':bind_code_opt})
+ t = time()
+ for i in range(n):
+ test=bind2(bind_code, g)
+ t2 = time()-t
+ print "1 regular version", t1
+ print "2 optimized version", t2
+ print "ratio (1-2)/1 : %f %%" % (100.*(t1-t2)/t1)
+
+
+def test_pystone():
+ from test import pystone
+ for _ in range(5):
+ pystone.main()
+ optimize_module(pystone, ('TRUE','FALSE','Proc0','Proc1','Proc2','Proc3',
+ 'Proc4','Proc5','Proc6','Proc7','Proc8','Func1',
+ ' Func2','Func3'))
+ optimize_module(pystone, builtins.keys())
+ for _ in range(5):
+ pystone.main()
+
+
+if __name__ == "__main__":
+ run_bench(1000)