Commit 33fa345d authored by Stefan Behnel's avatar Stefan Behnel

always parse keyword arguments in a PyDict_Next() loop - almost always faster...

always parse keyword arguments in a PyDict_Next() loop - almost always faster than multiple calls to PyDict_GetItem()
parent 8839d75a
...@@ -2103,8 +2103,6 @@ class DefNode(FuncDefNode): ...@@ -2103,8 +2103,6 @@ class DefNode(FuncDefNode):
code.putln("PyObject* values[%d] = {%s};" % ( code.putln("PyObject* values[%d] = {%s};" % (
max_args, ('0,'*max_args)[:-1])) max_args, ('0,'*max_args)[:-1]))
code.putln("Py_ssize_t kw_args = PyDict_Size(%s);" %
Naming.kwds_cname)
# parse the tuple and check that it's not too long # parse the tuple and check that it's not too long
code.putln('switch (PyTuple_GET_SIZE(%s)) {' % Naming.args_cname) code.putln('switch (PyTuple_GET_SIZE(%s)) {' % Naming.args_cname)
...@@ -2125,6 +2123,29 @@ class DefNode(FuncDefNode): ...@@ -2125,6 +2123,29 @@ class DefNode(FuncDefNode):
for i, arg in enumerate(all_args): for i, arg in enumerate(all_args):
if not arg.default: if not arg.default:
last_required_arg = i last_required_arg = i
if not self.num_required_kw_args:
code.putln('if (PyDict_Size(%s) > 0) {' % Naming.kwds_cname)
# non-positional kw args left in dict: default args, **kwargs or error
if self.star_arg:
code.putln("const Py_ssize_t used_pos_args = (PyTuple_GET_SIZE(%s) < %d) ? PyTuple_GET_SIZE(%s) : %d;" % (
Naming.args_cname, max_positional_args,
Naming.args_cname, max_positional_args))
pos_arg_count = "used_pos_args"
else:
pos_arg_count = "PyTuple_GET_SIZE(%s)" % Naming.args_cname
code.globalstate.use_utility_code(parse_keywords_utility_code)
code.put(
'if (unlikely(__Pyx_ParseKeywordArguments(%s, %s, %s, values, %s, "%s") < 0)) ' % (
Naming.kwds_cname,
Naming.pykwdlist_cname,
self.starstar_arg and self.starstar_arg.entry.cname or '0',
pos_arg_count,
self.name.utf8encode()))
code.putln(code.error_goto(self.pos))
if not self.num_required_kw_args:
code.putln('}')
if last_required_arg >= 0: if last_required_arg >= 0:
code.putln('switch (PyTuple_GET_SIZE(%s)) {' % Naming.args_cname) code.putln('switch (PyTuple_GET_SIZE(%s)) {' % Naming.args_cname)
for i, arg in enumerate(all_args[:last_required_arg+1]): for i, arg in enumerate(all_args[:last_required_arg+1]):
...@@ -2134,56 +2155,20 @@ class DefNode(FuncDefNode): ...@@ -2134,56 +2155,20 @@ class DefNode(FuncDefNode):
else: else:
code.putln('case %2d:' % i) code.putln('case %2d:' % i)
if arg.default: if arg.default:
# handled in ParseOptionalKeywords() below # handled in ParseKeywordArguments() above
continue continue
code.putln('values[%d] = PyDict_GetItem(%s, %s);' % ( code.putln("if (unlikely(!values[%d])) {" % i)
i, Naming.kwds_cname, arg.name_entry.pystring_cname))
if i < min_positional_args: if i < min_positional_args:
code.putln('if (likely(values[%d])) kw_args--;' % i);
if i == 0:
# special case: we know arg 0 is missing
code.put('else ')
code.put_goto(argtuple_error_label)
else:
# print the correct number of values (args or
# kwargs) that were passed into positional
# arguments up to this point
code.putln('else {')
code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %d, %d); ' % ( code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %d, %d); ' % (
self.name.utf8encode(), has_fixed_positional_count, self.name.utf8encode(), has_fixed_positional_count,
min_positional_args, max_positional_args, i)) min_positional_args, max_positional_args, i))
code.putln(code.error_goto(self.pos))
code.putln('}')
else: else:
code.putln('if (values[%d]) kw_args--;' % i);
if arg.kw_only and not arg.default:
code.putln('else {')
code.put('__Pyx_RaiseKeywordRequired("%s", %s); ' %( code.put('__Pyx_RaiseKeywordRequired("%s", %s); ' %(
self.name.utf8encode(), arg.name_entry.pystring_cname)) self.name.utf8encode(), arg.name_entry.pystring_cname))
code.putln(code.error_goto(self.pos)) code.putln(code.error_goto(self.pos))
code.putln('}') code.putln('}')
code.putln('}') code.putln('}')
code.putln('if (unlikely(kw_args > 0)) {')
# non-positional kw args left in dict: default args, **kwargs or error
if self.star_arg:
code.putln("const Py_ssize_t used_pos_args = (PyTuple_GET_SIZE(%s) < %d) ? PyTuple_GET_SIZE(%s) : %d;" % (
Naming.args_cname, max_positional_args,
Naming.args_cname, max_positional_args))
pos_arg_count = "used_pos_args"
else:
pos_arg_count = "PyTuple_GET_SIZE(%s)" % Naming.args_cname
code.globalstate.use_utility_code(parse_keywords_utility_code)
code.put(
'if (unlikely(__Pyx_ParseOptionalKeywords(%s, %s, %s, values, %s, "%s") < 0)) ' % (
Naming.kwds_cname,
Naming.pykwdlist_cname,
self.starstar_arg and self.starstar_arg.entry.cname or '0',
pos_arg_count,
self.name.utf8encode()))
code.putln(code.error_goto(self.pos))
code.putln('}')
# convert arg values to their final type and assign them # convert arg values to their final type and assign them
for i, arg in enumerate(all_args): for i, arg in enumerate(all_args):
if arg.default: if arg.default:
...@@ -4926,9 +4911,9 @@ invalid_keyword: ...@@ -4926,9 +4911,9 @@ invalid_keyword:
#------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------
# #
# __Pyx_ParseOptionalKeywords copies the optional/unknown keyword # __Pyx_ParseKeywordArguments copies the keyword arguments from the
# arguments from the kwds dict into kwds2. If kwds2 is NULL, unknown # kwds dict into kwds2. If kwds2 is NULL, unknown keywords will
# keywords will raise an invalid keyword error. # raise an invalid keyword error.
# #
# Three kinds of errors are checked: 1) non-string keywords, 2) # Three kinds of errors are checked: 1) non-string keywords, 2)
# unexpected keywords and 3) overlap with positional arguments. # unexpected keywords and 3) overlap with positional arguments.
...@@ -4942,12 +4927,12 @@ invalid_keyword: ...@@ -4942,12 +4927,12 @@ invalid_keyword:
parse_keywords_utility_code = UtilityCode( parse_keywords_utility_code = UtilityCode(
proto = """ proto = """
static int __Pyx_ParseOptionalKeywords(PyObject *kwds, PyObject **argnames[], \ static int __Pyx_ParseKeywordArguments(PyObject *kwds, PyObject **argnames[], \
PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args, \ PyObject *kwds2, PyObject *values[], Py_ssize_t num_pos_args, \
const char* function_name); /*proto*/ const char* function_name); /*proto*/
""", """,
impl = """ impl = """
static int __Pyx_ParseOptionalKeywords( static int __Pyx_ParseKeywordArguments(
PyObject *kwds, PyObject *kwds,
PyObject **argnames[], PyObject **argnames[],
PyObject *kwds2, PyObject *kwds2,
......
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