[pypy-commit] pypy default: (fijal, alex) Move map() from interp-level to app-level. fijal benchmarked this as between a 2x speedup and 10% slowdown. Apparently the JIT is kind of good.
alex_gaynor
noreply at buildbot.pypy.org
Tue Oct 11 23:15:38 CEST 2011
Author: Alex Gaynor <alex.gaynor at gmail.com>
Branch:
Changeset: r47944:cc47e5aeff35
Date: 2011-10-11 17:15 -0400
http://bitbucket.org/pypy/pypy/changeset/cc47e5aeff35/
Log: (fijal, alex) Move map() from interp-level to app-level. fijal
benchmarked this as between a 2x speedup and 10% slowdown.
Apparently the JIT is kind of good.
diff --git a/pypy/module/__builtin__/__init__.py b/pypy/module/__builtin__/__init__.py
--- a/pypy/module/__builtin__/__init__.py
+++ b/pypy/module/__builtin__/__init__.py
@@ -20,6 +20,7 @@
'any' : 'app_functional.any',
'all' : 'app_functional.all',
'sum' : 'app_functional.sum',
+ 'map' : 'app_functional.map',
'vars' : 'app_inspect.vars',
'dir' : 'app_inspect.dir',
@@ -86,7 +87,6 @@
'enumerate' : 'functional.W_Enumerate',
'min' : 'functional.min',
'max' : 'functional.max',
- 'map' : 'functional.map',
'zip' : 'functional.zip',
'reduce' : 'functional.reduce',
'reversed' : 'functional.reversed',
diff --git a/pypy/module/__builtin__/app_functional.py b/pypy/module/__builtin__/app_functional.py
--- a/pypy/module/__builtin__/app_functional.py
+++ b/pypy/module/__builtin__/app_functional.py
@@ -48,4 +48,53 @@
# Very intentionally *not* +=, that would have different semantics if
# start was a mutable type, such as a list
last = last + x
- return last
\ No newline at end of file
+ return last
+
+def map(func, *collections):
+ """map(function, sequence[, sequence, ...]) -> list
+
+Return a list of the results of applying the function to the items of
+the argument sequence(s). If more than one sequence is given, the
+function is called with an argument list consisting of the corresponding
+item of each sequence, substituting None for missing values when not all
+sequences have the same length. If the function is None, return a list of
+the items of the sequence (or a list of tuples if more than one sequence)."""
+ if not collections:
+ raise TypeError("map() requires at least two arguments")
+ num_collections = len(collections)
+ none_func = func is None
+ if num_collections == 1:
+ if none_func:
+ return list(collections[0])
+ else:
+ # Special case for the really common case of a single collection,
+ # this can be eliminated if we could unroll that loop that creates
+ # `args` based on whether or not len(collections) was constant
+ result = []
+ for item in collections[0]:
+ result.append(func(item))
+ return result
+ result = []
+ # Pair of (iterator, has_finished)
+ iterators = [(iter(seq), False) for seq in collections]
+ while True:
+ cont = False
+ args = []
+ for idx, (iterator, has_finished) in enumerate(iterators):
+ val = None
+ if not has_finished:
+ try:
+ val = next(iterator)
+ except StopIteration:
+ iterators[idx] = (None, True)
+ else:
+ cont = True
+ args.append(val)
+ args = tuple(args)
+ if cont:
+ if none_func:
+ result.append(args)
+ else:
+ result.append(func(*args))
+ else:
+ return result
diff --git a/pypy/module/__builtin__/functional.py b/pypy/module/__builtin__/functional.py
--- a/pypy/module/__builtin__/functional.py
+++ b/pypy/module/__builtin__/functional.py
@@ -201,118 +201,6 @@
"""
return min_max(space, __args__, "min")
- at unwrap_spec(collections_w="args_w")
-def map(space, w_func, collections_w):
- """does 3 separate things, hence this enormous docstring.
- 1. if function is None, return a list of tuples, each with one
- item from each collection. If the collections have different
- lengths, shorter ones are padded with None.
-
- 2. if function is not None, and there is only one collection,
- apply function to every item in the collection and return a
- list of the results.
-
- 3. if function is not None, and there are several collections,
- repeatedly call the function with one argument from each
- collection. If the collections have different lengths,
- shorter ones are padded with None
- """
- if not collections_w:
- msg = "map() requires at least two arguments"
- raise OperationError(space.w_TypeError, space.wrap(msg))
- none_func = space.is_w(w_func, space.w_None)
- if len(collections_w) == 1:
- w_collection = collections_w[0]
- if none_func:
- result_w = space.unpackiterable(w_collection)
- else:
- result_w = map_single_collection(space, w_func, w_collection)
- else:
- result_w = map_multiple_collections(space, w_func, collections_w,
- none_func)
- return space.newlist(result_w)
-
-def map_single_collection(space, w_func, w_collection):
- """Special case for 'map(func, coll)', where 'func' is not None and there
- is only one 'coll' argument."""
- w_iter = space.iter(w_collection)
- # xxx special hacks for speed
- from pypy.interpreter import function, pycode
- if isinstance(w_func, function.Function):
- # xxx compatibility issue: what if func_code is modified in the
- # middle of running map()?? That's far too obscure for me to care...
- code = w_func.getcode()
- fast_natural_arity = code.fast_natural_arity
- if fast_natural_arity == (1|pycode.PyCode.FLATPYCALL):
- assert isinstance(code, pycode.PyCode)
- return map_single_user_function(code, w_func, w_iter)
- # /xxx end of special hacks
- return map_single_other_callable(space, w_func, w_iter)
-
-def map_single_other_callable(space, w_func, w_iter):
- result_w = []
- while True:
- try:
- w_item = space.next(w_iter)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- result_w.append(space.call_function(w_func, w_item))
- return result_w
-map_single_other_callable._dont_inline_ = True
-
-from pypy.rlib.jit import JitDriver
-mapjitdriver = JitDriver(greens = ['code'],
- reds = ['w_func', 'w_iter', 'result_w'])
-def map_single_user_function(code, w_func, w_iter):
- result_w = []
- while True:
- mapjitdriver.can_enter_jit(code=code, w_func=w_func,
- w_iter=w_iter, result_w=result_w)
- mapjitdriver.jit_merge_point(code=code, w_func=w_func,
- w_iter=w_iter, result_w=result_w)
- space = w_func.space
- try:
- w_item = space.next(w_iter)
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- break
- new_frame = space.createframe(code, w_func.w_func_globals,
- w_func)
- new_frame.locals_stack_w[0] = w_item
- w_res = new_frame.run()
- result_w.append(w_res)
- return result_w
-
-def map_multiple_collections(space, w_func, collections_w, none_func):
- result_w = []
- iterators_w = [space.iter(w_seq) for w_seq in collections_w]
- num_iterators = len(iterators_w)
- while True:
- cont = False
- args_w = [space.w_None] * num_iterators
- for i in range(num_iterators):
- if iterators_w[i] is not None:
- try:
- args_w[i] = space.next(iterators_w[i])
- except OperationError, e:
- if not e.match(space, space.w_StopIteration):
- raise
- iterators_w[i] = None
- else:
- cont = True
- if not cont:
- break
- w_args = space.newtuple(args_w)
- if none_func:
- w_res = w_args
- else:
- w_res = space.call(w_func, w_args)
- result_w.append(w_res)
- return result_w
-
@unwrap_spec(sequences_w="args_w")
def zip(space, sequences_w):
"""Return a list of tuples, where the nth tuple contains every nth item of
diff --git a/pypy/module/__builtin__/test/test_functional.py b/pypy/module/__builtin__/test/test_functional.py
--- a/pypy/module/__builtin__/test/test_functional.py
+++ b/pypy/module/__builtin__/test/test_functional.py
@@ -147,7 +147,7 @@
assert list(xrange(A())) == [0, 1, 2, 3, 4]
assert list(xrange(0, A())) == [0, 1, 2, 3, 4]
assert list(xrange(0, 10, A())) == [0, 5]
-
+
def test_xrange_float(self):
assert list(xrange(0.1, 2.0, 1.1)) == [0, 1]
More information about the pypy-commit
mailing list