Author: Alex Gaynor <alex.gay...@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") -@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] _______________________________________________ pypy-commit mailing list pypy-commit@python.org http://mail.python.org/mailman/listinfo/pypy-commit