Commit 6fd9a195 authored by Robert Bradshaw's avatar Robert Bradshaw

Cache __builtin__ name lookups so they are performed on module load rather than at every use.

The code "__Pyx_GetName(__pyx_b, __pyx_n_[string])" is performed in several
thousand places throughout the sage library, and can be quite expensive
(a dictionary lookup, possibly raising an error, etc.) This is redundant
as the result will always be the same. I perform the lookup once
(on loading the module), then have a pointer to the result for all subsequent use.

The most common examples are bool/str/int (both as function calls and
in isinstance), True/False, and raisign errors.

A side feature is that on loading a module with an illegal __builtin__ name,
it will complain at load time rather than at run time.
parent a72407b4
...@@ -714,7 +714,10 @@ class NameNode(AtomicExprNode): ...@@ -714,7 +714,10 @@ class NameNode(AtomicExprNode):
self.type = self.type.element_ptr_type() self.type = self.type.element_ptr_type()
if self.entry.is_pyglobal or self.entry.is_builtin: if self.entry.is_pyglobal or self.entry.is_builtin:
assert self.type.is_pyobject, "Python global or builtin not a Python object" assert self.type.is_pyobject, "Python global or builtin not a Python object"
self.is_temp = 1 if Options.intern_names and self.entry.is_builtin:
self.is_temp = 0
else:
self.is_temp = 1
if Options.intern_names: if Options.intern_names:
env.use_utility_code(get_name_interned_utility_code) env.use_utility_code(get_name_interned_utility_code)
else: else:
...@@ -777,7 +780,9 @@ class NameNode(AtomicExprNode): ...@@ -777,7 +780,9 @@ class NameNode(AtomicExprNode):
entry = self.entry entry = self.entry
if entry is None: if entry is None:
return # There was an error earlier return # There was an error earlier
if entry.is_pyglobal or entry.is_builtin: if entry.is_builtin and Options.cache_builtins:
return # Lookup already cached
elif entry.is_pyglobal or entry.is_builtin:
if entry.is_builtin: if entry.is_builtin:
namespace = Naming.builtins_cname namespace = Naming.builtins_cname
else: # entry.is_pyglobal else: # entry.is_pyglobal
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
pyrex_prefix = "__pyx_" pyrex_prefix = "__pyx_"
builtin_prefix = pyrex_prefix + "builtin_"
arg_prefix = pyrex_prefix + "arg_" arg_prefix = pyrex_prefix + "arg_"
funcdoc_prefix = pyrex_prefix + "doc_" funcdoc_prefix = pyrex_prefix + "doc_"
enum_prefix = pyrex_prefix + "e_" enum_prefix = pyrex_prefix + "e_"
......
...@@ -117,6 +117,14 @@ class BlockNode: ...@@ -117,6 +117,14 @@ class BlockNode:
code.putln( code.putln(
"static PyObject *%s;" % entry.pystring_cname) "static PyObject *%s;" % entry.pystring_cname)
def generate_cached_builtins_decls(self, env, code):
entries = env.builtin_scope().undeclared_cached_entries
if len(entries) > 0:
code.putln("")
for entry in entries:
code.putln("static PyObject *%s;" % entry.cname)
del entries[:]
class ModuleNode(Node, BlockNode): class ModuleNode(Node, BlockNode):
# doc string or None # doc string or None
...@@ -203,6 +211,7 @@ class ModuleNode(Node, BlockNode): ...@@ -203,6 +211,7 @@ class ModuleNode(Node, BlockNode):
self.generate_const_definitions(env, code) self.generate_const_definitions(env, code)
self.generate_interned_name_decls(env, code) self.generate_interned_name_decls(env, code)
self.generate_py_string_decls(env, code) self.generate_py_string_decls(env, code)
self.generate_cached_builtins_decls(env, code)
self.body.generate_function_definitions(env, code) self.body.generate_function_definitions(env, code)
self.generate_interned_name_table(env, code) self.generate_interned_name_table(env, code)
self.generate_py_string_table(env, code) self.generate_py_string_table(env, code)
...@@ -1143,6 +1152,8 @@ class ModuleNode(Node, BlockNode): ...@@ -1143,6 +1152,8 @@ class ModuleNode(Node, BlockNode):
self.generate_intern_code(env, code) self.generate_intern_code(env, code)
#code.putln("/*--- String init code ---*/") #code.putln("/*--- String init code ---*/")
self.generate_string_init_code(env, code) self.generate_string_init_code(env, code)
#code.putln("/*--- Builtin init code ---*/")
self.generate_builtin_init_code(env, code)
#code.putln("/*--- Global init code ---*/") #code.putln("/*--- Global init code ---*/")
self.generate_global_init_code(env, code) self.generate_global_init_code(env, code)
#code.putln("/*--- Type import code ---*/") #code.putln("/*--- Type import code ---*/")
...@@ -1208,6 +1219,28 @@ class ModuleNode(Node, BlockNode): ...@@ -1208,6 +1219,28 @@ class ModuleNode(Node, BlockNode):
Naming.stringtab_cname, Naming.stringtab_cname,
code.error_goto(self.pos))) code.error_goto(self.pos)))
def generate_builtin_init_code(self, env, code):
# Lookup and cache builtin objects.
if Options.cache_builtins:
for entry in env.builtin_scope().cached_entries:
if Options.intern_names:
#assert entry.interned_cname is not None
code.putln(
'%s = __Pyx_GetName(%s, %s); if (!%s) %s' % (
entry.cname,
Naming.builtins_cname,
entry.interned_cname,
entry.cname,
code.error_goto(entry.pos)))
else:
code.putln(
'%s = __Pyx_GetName(%s, "%s"); if (!%s) %s' % (
entry.cname,
Naming.builtins_cname,
self.entry.name,
entry.cname,
code.error_goto(entry.pos)))
def generate_global_init_code(self, env, code): def generate_global_init_code(self, env, code):
# Generate code to initialise global PyObject * # Generate code to initialise global PyObject *
# variables to None. # variables to None.
...@@ -1771,6 +1804,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1771,6 +1804,7 @@ class FuncDefNode(StatNode, BlockNode):
# ----- Top-level constants used by this function # ----- Top-level constants used by this function
self.generate_interned_name_decls(lenv, code) self.generate_interned_name_decls(lenv, code)
self.generate_py_string_decls(lenv, code) self.generate_py_string_decls(lenv, code)
self.generate_cached_builtins_decls(lenv, code)
#code.putln("") #code.putln("")
#code.put_var_declarations(lenv.const_entries, static = 1) #code.put_var_declarations(lenv.const_entries, static = 1)
self.generate_const_definitions(lenv, code) self.generate_const_definitions(lenv, code)
...@@ -1871,7 +1905,7 @@ class FuncDefNode(StatNode, BlockNode): ...@@ -1871,7 +1905,7 @@ class FuncDefNode(StatNode, BlockNode):
def generate_execution_code(self, code): def generate_execution_code(self, code):
pass pass
class CFuncDefNode(FuncDefNode): class CFuncDefNode(FuncDefNode):
# C function definition. # C function definition.
......
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
# Pyrex - Compilation-wide options # Pyrex - Compilation-wide options
# #
intern_names = 1 # Intern global variable and attribute names intern_names = 1 # Intern global variable and attribute names
cache_builtins = 1 # Perform lookups on builtin names only once
embed_pos_in_docstring = 0 embed_pos_in_docstring = 0
...@@ -187,9 +187,13 @@ class Scope: ...@@ -187,9 +187,13 @@ class Scope:
# Return the module-level scope containing this scope. # Return the module-level scope containing this scope.
return self.outer_scope.global_scope() return self.outer_scope.global_scope()
def builtin_scope(self):
# Return the module-level scope containing this scope.
return self.outer_scope.builtin_scope()
def declare(self, name, cname, type, pos): def declare(self, name, cname, type, pos):
# Create new entry, and add to dictionary if # Create new entry, and add to dictionary if
# name is not None. Reports an error if already # name is not None. Reports a warning if already
# declared. # declared.
dict = self.entries dict = self.entries
if name and dict.has_key(name): if name and dict.has_key(name):
...@@ -451,12 +455,24 @@ class BuiltinScope(Scope): ...@@ -451,12 +455,24 @@ class BuiltinScope(Scope):
cname, type, arg_types, exception_value, exception_check = definition cname, type, arg_types, exception_value, exception_check = definition
function = CFuncType(type, [CFuncTypeArg("", t, None) for t in arg_types], False, exception_value, exception_check) function = CFuncType(type, [CFuncTypeArg("", t, None) for t in arg_types], False, exception_value, exception_check)
self.add_cfunction(name, function, None, cname, False) self.add_cfunction(name, function, None, cname, False)
self.cached_entries = []
self.undeclared_cached_entries = []
def declare_builtin(self, name, pos): def declare_builtin(self, name, pos):
entry = self.declare(name, name, py_object_type, pos) entry = self.declare(name, name, py_object_type, pos)
entry.is_builtin = 1 if Options.cache_builtins:
entry.is_builtin = 1
entry.is_const = 1
entry.cname = Naming.builtin_prefix + name
self.cached_entries.append(entry)
self.undeclared_cached_entries.append(entry)
else:
entry.is_builtin = 1
return entry return entry
def builtin_scope(self):
return self
# TODO: built in functions conflict with built in types of same name... # TODO: built in functions conflict with built in types of same name...
builtin_functions = { builtin_functions = {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment