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):
self.type = self.type.element_ptr_type()
if self.entry.is_pyglobal or self.entry.is_builtin:
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:
env.use_utility_code(get_name_interned_utility_code)
else:
......@@ -777,7 +780,9 @@ class NameNode(AtomicExprNode):
entry = self.entry
if entry is None:
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:
namespace = Naming.builtins_cname
else: # entry.is_pyglobal
......
......@@ -8,6 +8,7 @@
pyrex_prefix = "__pyx_"
builtin_prefix = pyrex_prefix + "builtin_"
arg_prefix = pyrex_prefix + "arg_"
funcdoc_prefix = pyrex_prefix + "doc_"
enum_prefix = pyrex_prefix + "e_"
......
......@@ -117,6 +117,14 @@ class BlockNode:
code.putln(
"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):
# doc string or None
......@@ -203,6 +211,7 @@ class ModuleNode(Node, BlockNode):
self.generate_const_definitions(env, code)
self.generate_interned_name_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.generate_interned_name_table(env, code)
self.generate_py_string_table(env, code)
......@@ -1143,6 +1152,8 @@ class ModuleNode(Node, BlockNode):
self.generate_intern_code(env, code)
#code.putln("/*--- String init 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 ---*/")
self.generate_global_init_code(env, code)
#code.putln("/*--- Type import code ---*/")
......@@ -1208,6 +1219,28 @@ class ModuleNode(Node, BlockNode):
Naming.stringtab_cname,
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):
# Generate code to initialise global PyObject *
# variables to None.
......@@ -1771,6 +1804,7 @@ class FuncDefNode(StatNode, BlockNode):
# ----- Top-level constants used by this function
self.generate_interned_name_decls(lenv, code)
self.generate_py_string_decls(lenv, code)
self.generate_cached_builtins_decls(lenv, code)
#code.putln("")
#code.put_var_declarations(lenv.const_entries, static = 1)
self.generate_const_definitions(lenv, code)
......@@ -1871,7 +1905,7 @@ class FuncDefNode(StatNode, BlockNode):
def generate_execution_code(self, code):
pass
class CFuncDefNode(FuncDefNode):
# C function definition.
......
......@@ -2,6 +2,7 @@
# 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
......@@ -187,9 +187,13 @@ class Scope:
# Return the module-level scope containing this 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):
# 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.
dict = self.entries
if name and dict.has_key(name):
......@@ -451,12 +455,24 @@ class BuiltinScope(Scope):
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)
self.add_cfunction(name, function, None, cname, False)
self.cached_entries = []
self.undeclared_cached_entries = []
def declare_builtin(self, name, 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
def builtin_scope(self):
return self
# TODO: built in functions conflict with built in types of same name...
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