root / logilab.pylintinstaller / logilab / common / bind.py

Revision 202:d67e86292521, 8.6 kB (checked in by tziade@…, 9 months ago)

added logilab.pylintinstaller

Line 
1# This program is free software; you can redistribute it and/or modify it under
2# the terms of the GNU General Public License as published by the Free Software
3# Foundation; either version 2 of the License, or (at your option) any later
4# version.
5#
6# This program is distributed in the hope that it will be useful, but WITHOUT
7# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
8# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
9#
10# You should have received a copy of the GNU General Public License along with
11# this program; if not, write to the Free Software Foundation, Inc.,
12# 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
13
14""" Copyright (c) 2002-2003 LOGILAB S.A. (Paris, FRANCE).
15 http://www.logilab.fr/ -- mailto:contact@logilab.fr
16
17 This module provides a way to optimize globals in certain functions by binding
18 their names to values provided in a dictionnary
19"""
20
21from warnings import warn
22warn('bind module is deprecated and will disappear in a near release',
23     DeprecationWarning, stacklevel=1)
24
25__revision__ = '$Id: bind.py,v 1.8 2005-11-22 13:12:59 syt Exp $'
26
27# TODO: unit tests
28# * this module provide a function bind(func,vars) which replaces every
29#   global variable 'm' by the value vars['m'] if such value exists in dict
30
31from dis import HAVE_ARGUMENT
32from new import code as make_code, function as make_function
33import inspect
34
35LOAD_GLOBAL = 116
36LOAD_CONST = 100
37EXTENDED_ARG = 143
38STORE_GLOBAL = 97
39
40def bind_code(co, globals):
41    """
42    Take a code object and a dictionnary and returns a new code object where
43    the opcodes LOAD_GLOBAL are replaced by LOAD_CONST whenever the global's
44    name appear in the dictionnary
45    """
46    consts = list(co.co_consts)
47    assigned = {}
48   
49    code = co.co_code
50    new_code = ""
51    n = len(code)
52    i = 0
53    while i < n:
54        c = code[i]
55        op = ord(c)
56        i += 1
57        if op >= HAVE_ARGUMENT:
58            oparg = ord(code[i]) + ord(code[i+1]) * 256
59            i += 2
60        else:
61            oparg = None
62        if op == LOAD_GLOBAL:
63            name = co.co_names[oparg]
64            if globals.has_key(name):
65                k = assigned.get(name, None)
66                if k == None:
67                    k = len(consts)
68                    assigned[name] = len(consts)
69                    consts.append(globals[name])
70                op = LOAD_CONST
71                oparg = k
72        new_code += chr(op)
73        if oparg is not None:
74            new_code += chr(oparg & 255)
75            new_code += chr( (oparg>>8) & 255 )
76           
77    return make_code(co.co_argcount,
78                     co.co_nlocals,
79                     co.co_stacksize,
80                     co.co_flags,
81                     new_code,
82                     tuple(consts),
83                     co.co_names,
84                     co.co_varnames,
85                     co.co_filename,
86                     co.co_name,
87                     co.co_firstlineno,
88                     co.co_lnotab )
89
90
91def bind(f, globals):
92    """Returns a new function whose code object has been
93    bound by bind_code()"""
94    newcode = bind_code(f.func_code, globals)
95    defaults = f.func_defaults or ()
96    return make_function(newcode, f.func_globals, f.func_name, defaults)
97
98if type(__builtins__) == dict:
99    builtins = __builtins__
100else:
101    builtins = __builtins__.__dict__
102   
103bind_code_opt = bind(bind_code, builtins )
104bind_code_opt = bind(bind_code_opt, globals() )
105
106
107def optimize_module(m, global_consts):
108    if not inspect.ismodule(m):
109        raise TypeError
110    d = {}
111    for i in global_consts:
112        v = m.__dict__.get(i)
113        d[i] = v
114    builtins = m.__builtins__
115    for name, f in m.__dict__.items():
116        if inspect.isfunction(f):
117            f = bind(f, builtins)
118            if d:
119                f = bind(f, d)
120            m.__dict__[name] = f
121           
122
123
124
125def analyze_code(co, globals, consts_dict, consts_list):
126    """Take a code object and a dictionnary and returns a
127    new code object where the opcodes LOAD_GLOBAL are replaced
128    by LOAD_CONST whenever the global's name appear in the
129    dictionnary"""
130    modified_globals = []
131    for c in co.co_consts:
132        if c not in consts_list:
133            consts_list.append(c)
134    modified = []
135    code = co.co_code
136    new_code = ""
137    n = len(code)
138    i = 0
139    extended_arg = 0
140    while i < n:
141        c = code[i]
142        op = ord(c)
143        i += 1
144        if op >= HAVE_ARGUMENT:
145            oparg = ord(code[i]) + ord(code[i+1])*256 + extended_arg
146            extended_arg = 0
147            i += 2
148        else:
149            oparg = None
150        if op == EXTENDED_ARG:
151            extended_arg = oparg*65536L
152
153        if op == LOAD_GLOBAL:
154            name = co.co_names[oparg]
155            if globals.has_key(name):
156                k = consts_dict.get(name, None)
157                if k == None:
158                    k = len(consts_list)
159                    consts_dict[name] = k
160                    consts_list.append(globals[name])
161        if op == STORE_GLOBAL:
162            name = co.co_names[oparg]
163            if globals.has_key(name):
164                modified_globals.append(name)
165    return modified_globals
166
167def rewrite_code(co, consts_dict, consts_tuple):
168    """Take a code object and a dictionnary and returns a
169    new code object where the opcodes LOAD_GLOBAL are replaced
170    by LOAD_CONST whenever the global's name appear in the
171    dictionnary"""
172    code = co.co_code
173    new_code = ""
174    n = len(code)
175    i = 0
176    consts_list = list(consts_tuple)
177    while i < n:
178        c = code[i]
179        op = ord(c)
180        i += 1
181        extended_arg = 0
182        if op >= HAVE_ARGUMENT:
183            oparg = ord(code[i]) + ord(code[i+1])*256+extended_arg
184            extended_arg = 0
185            i += 2
186        else:
187            oparg = None
188        if op == EXTENDED_ARG:
189            extended_arg = oparg*65536L
190        elif op == LOAD_GLOBAL:
191            name = co.co_names[oparg]
192            k = consts_dict.get(name)
193            if k is not None:
194                op = LOAD_CONST
195                oparg = k
196        elif op == LOAD_CONST:
197            val = co.co_consts[oparg]
198            oparg = consts_list.index(val)
199        new_code += chr(op)
200        if oparg is not None:
201            new_code += chr(oparg & 255)
202            new_code += chr( (oparg>>8) & 255 )
203           
204    return make_code(co.co_argcount,
205                     co.co_nlocals,
206                     co.co_stacksize,
207                     co.co_flags,
208                     new_code,
209                     consts_tuple,
210                     co.co_names,
211                     co.co_varnames,
212                     co.co_filename,
213                     co.co_name,
214                     co.co_firstlineno,
215                     co.co_lnotab )
216
217def optimize_module_2(m, globals_consts, bind_builtins=1):
218    if not inspect.ismodule(m):
219        raise TypeError
220    consts_dict = {}
221    consts_list = []
222    if type(globals_consts) == list or type(globals_consts) == tuple:
223        globals = {}
224        for i in globals_consts:
225            v = m.__dict__.get(i)
226            globals[i] = v
227    else:
228        globals = globals_consts
229    if bind_builtins:
230        for builtin_name, builtin_value in m.__builtins__.items():
231            # this way it is possible to redefine a builtin in globals_consts
232            globals.setdefault(builtin_name, builtin_value)
233    functions = {}
234    for name, f in m.__dict__.items():
235        if inspect.isfunction(f):
236            functions[name] = f
237            analyze_code(f.func_code, globals, consts_dict, consts_list)
238    consts_list = tuple(consts_list)
239    for name, f in functions.items():
240        newcode = rewrite_code(f.func_code, consts_dict, consts_list)
241        defaults = f.func_defaults or ()
242        m.__dict__[name] = make_function(newcode, f.func_globals, f.func_name,
243                                         defaults)
244       
245
246def run_bench(n):
247    from time import time
248    t = time()
249    g = globals()
250    for i in range(n):
251        test = bind(bind_code, g)
252    t1 = time()-t
253    bind2 = bind(bind, {'bind_code':bind_code_opt})
254    t = time()
255    for i in range(n):
256        test=bind2(bind_code, g)
257    t2 = time()-t
258    print "1 regular version", t1
259    print "2 optimized version", t2
260    print "ratio (1-2)/1 : %f %%" % (100.*(t1-t2)/t1)
261   
262
263def test_pystone():
264    from test import pystone
265    for _ in range(5):
266        pystone.main()
267    optimize_module(pystone, ('TRUE','FALSE','Proc0','Proc1','Proc2','Proc3',
268                              'Proc4','Proc5','Proc6','Proc7','Proc8','Func1',
269                             ' Func2','Func3'))
270    optimize_module(pystone, builtins.keys())
271    for _ in range(5):
272        pystone.main()
273
274
275if __name__ == "__main__":
276    run_bench(1000)
Note: See TracBrowser for help on using the browser.