Commit 940883b0 authored by Stefan Behnel's avatar Stefan Behnel

reverted latest changes: calling PyDict_GetItem inside a switch is actually...

reverted latest changes: calling PyDict_GetItem inside a switch is actually faster than always looping over the kwdict
parent 5264f2aa
...@@ -2100,14 +2100,11 @@ class DefNode(FuncDefNode): ...@@ -2100,14 +2100,11 @@ class DefNode(FuncDefNode):
kw_only_args, argtuple_error_label, code): kw_only_args, argtuple_error_label, code):
all_args = tuple(positional_args) + tuple(kw_only_args) all_args = tuple(positional_args) + tuple(kw_only_args)
max_args = len(all_args) max_args = len(all_args)
all_required = self.num_required_args == len(self.args) \
and not self.star_arg and not self.starstar_arg
code.putln("PyObject* values[%d] = {%s};" % ( code.putln("PyObject* values[%d] = {%s};" % (
max_args, ('0,'*max_args)[:-1])) max_args, ('0,'*max_args)[:-1]))
if all_required: code.putln("Py_ssize_t kw_args = PyDict_Size(%s);" %
code.putln('Py_ssize_t kw_count = PyDict_Size(%s);' % Naming.kwds_cname)
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)
...@@ -2123,15 +2120,53 @@ class DefNode(FuncDefNode): ...@@ -2123,15 +2120,53 @@ class DefNode(FuncDefNode):
code.put_goto(argtuple_error_label) code.put_goto(argtuple_error_label)
code.putln('}') code.putln('}')
# parse all keyword arguments, check for duplicates, etc. # now fill up the required arguments with values from the kw dict
if not self.num_required_kw_args: last_required_arg = -1
if all_required: for i, arg in enumerate(all_args):
code.putln('if (kw_count > 0) {') if not arg.default:
else: last_required_arg = i
code.putln('if (PyDict_Size(%s) > 0) {' % Naming.kwds_cname) if last_required_arg >= 0:
code.putln('switch (PyTuple_GET_SIZE(%s)) {' % Naming.args_cname)
for i, arg in enumerate(all_args[:last_required_arg+1]):
if i <= max_positional_args:
if self.star_arg and i == max_positional_args:
code.putln('default:')
else:
code.putln('case %2d:' % i)
if arg.default:
# handled in ParseOptionalKeywords() below
continue
code.putln('values[%d] = PyDict_GetItem(%s, %s);' % (
i, Naming.kwds_cname, arg.name_entry.pystring_cname))
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); ' % (
self.name.utf8encode(), has_fixed_positional_count,
min_positional_args, max_positional_args, i))
code.putln(code.error_goto(self.pos))
code.putln('}')
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); ' %(
self.name.utf8encode(), arg.name_entry.pystring_cname))
code.putln(code.error_goto(self.pos))
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: if self.star_arg:
if self.num_required_kw_args:
code.putln('{')
code.putln("const Py_ssize_t used_pos_args = (PyTuple_GET_SIZE(%s) < %d) ? PyTuple_GET_SIZE(%s) : %d;" % ( 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,
Naming.args_cname, max_positional_args)) Naming.args_cname, max_positional_args))
...@@ -2140,59 +2175,14 @@ class DefNode(FuncDefNode): ...@@ -2140,59 +2175,14 @@ class DefNode(FuncDefNode):
pos_arg_count = "PyTuple_GET_SIZE(%s)" % Naming.args_cname pos_arg_count = "PyTuple_GET_SIZE(%s)" % Naming.args_cname
code.globalstate.use_utility_code(parse_keywords_utility_code) code.globalstate.use_utility_code(parse_keywords_utility_code)
code.put( code.put(
'if (unlikely(__Pyx_ParseKeywordArguments(%s, %s, %s, values, %s, "%s") < 0)) ' % ( 'if (unlikely(__Pyx_ParseOptionalKeywords(%s, %s, %s, values, %s, "%s") < 0)) ' % (
Naming.kwds_cname, Naming.kwds_cname,
Naming.pykwdlist_cname, Naming.pykwdlist_cname,
self.starstar_arg and self.starstar_arg.entry.cname or '0', self.starstar_arg and self.starstar_arg.entry.cname or '0',
pos_arg_count, pos_arg_count,
self.name.utf8encode())) self.name.utf8encode()))
code.putln(code.error_goto(self.pos)) code.putln(code.error_goto(self.pos))
if self.star_arg or not self.num_required_kw_args: code.putln('}')
code.putln('}')
# make sure we found all required args
if self.num_required_args:
if all_required:
# common case: we know the exact number of arguments
required_args = all_args
if len(required_args) > 1:
code.putln('if (unlikely(PyTuple_GET_SIZE(%s) + kw_count != %d)) {' % (
Naming.args_cname, max_args))
else:
last_required_arg = -1
for i, arg in enumerate(all_args):
if not arg.default:
last_required_arg = i
required_args = all_args[:last_required_arg+1]
# avoid switch for the simple cases - it has an overhead, too
use_switch = max_positional_args > 2 and len(required_args) > 2 \
and not all_required # specialised above already
if use_switch:
code.putln('switch (PyTuple_GET_SIZE(%s)) {' %
Naming.args_cname)
for i, arg in enumerate(required_args):
if use_switch and i <= max_positional_args:
if self.star_arg and i == max_positional_args:
code.putln('default:')
else:
code.putln('case %2d:' % i)
if arg.default:
# handled in ParseKeywordArguments() above
continue
code.putln("if (unlikely(!values[%d])) {" % i)
if i < min_positional_args:
code.put('__Pyx_RaiseArgtupleInvalid("%s", %d, %d, %d, %d); ' % (
self.name.utf8encode(), has_fixed_positional_count,
min_positional_args, max_positional_args, i))
else:
code.put('__Pyx_RaiseKeywordRequired("%s", %s); ' %(
self.name.utf8encode(), arg.name_entry.pystring_cname))
code.putln(code.error_goto(self.pos))
code.putln('}')
if use_switch:
code.putln('}')
if all_required and len(required_args) > 1:
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):
...@@ -4936,9 +4926,9 @@ invalid_keyword: ...@@ -4936,9 +4926,9 @@ invalid_keyword:
#------------------------------------------------------------------------------------ #------------------------------------------------------------------------------------
# #
# __Pyx_ParseKeywordArguments copies the keyword arguments from the # __Pyx_ParseOptionalKeywords copies the optional/unknown keyword
# kwds dict into kwds2. If kwds2 is NULL, unknown keywords will # arguments from the kwds dict into kwds2. If kwds2 is NULL, unknown
# raise an invalid keyword error. # keywords will 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.
...@@ -4952,12 +4942,12 @@ invalid_keyword: ...@@ -4952,12 +4942,12 @@ invalid_keyword:
parse_keywords_utility_code = UtilityCode( parse_keywords_utility_code = UtilityCode(
proto = """ proto = """
static int __Pyx_ParseKeywordArguments(PyObject *kwds, PyObject **argnames[], \ static int __Pyx_ParseOptionalKeywords(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_ParseKeywordArguments( static int __Pyx_ParseOptionalKeywords(
PyObject *kwds, PyObject *kwds,
PyObject **argnames[], PyObject **argnames[],
PyObject *kwds2, PyObject *kwds2,
...@@ -4973,7 +4963,7 @@ static int __Pyx_ParseKeywordArguments( ...@@ -4973,7 +4963,7 @@ static int __Pyx_ParseKeywordArguments(
while (PyDict_Next(kwds, &pos, &key, &value)) { while (PyDict_Next(kwds, &pos, &key, &value)) {
name = first_kw_arg; name = first_kw_arg;
while (*name && (**name != key)) name++; while (*name && (**name != key)) name++;
if (likely(*name)) { if (*name) {
values[name-argnames] = value; values[name-argnames] = value;
} else { } else {
#if PY_MAJOR_VERSION < 3 #if PY_MAJOR_VERSION < 3
...@@ -4993,7 +4983,7 @@ static int __Pyx_ParseKeywordArguments( ...@@ -4993,7 +4983,7 @@ static int __Pyx_ParseKeywordArguments(
PyString_AS_STRING(key)) == 0) break; PyString_AS_STRING(key)) == 0) break;
#endif #endif
} }
if (likely(*name)) { if (*name) {
values[name-argnames] = value; values[name-argnames] = value;
} else { } else {
/* unexpected keyword found */ /* unexpected keyword found */
......
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