Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Replacing map(lambda()) calls with list compression #9078

Merged
merged 1 commit into from
Mar 2, 2015
Merged

Replacing map(lambda()) calls with list compression #9078

merged 1 commit into from
Mar 2, 2015

Conversation

mihir-w
Copy link
Contributor

@mihir-w mihir-w commented Mar 1, 2015

map(lambda(....)) is slower than list compression. Replaced about 40 such instances with list compression.

Partially Fixes #4940.

@asmeurer please review.

else:
if MV.coords is not None:
base_lst = str_combinations(base, MV.coords, rank=2, mode='__')
else:
base_lst = str_combinations(base, MV.subscripts, rank=2, mode='__')
fct_lst = fct_sym_array(base_lst, None)
self.obj = reduce(operator.add, tuple(map(lambda x: x[0] * x[1], zip(fct_lst, MV.blades[2]))))
self.obj = reduce(operator.add, tuple([x[0] * x[1] for x in zip(fct_lst, MV.blades[2])]))
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we get rid of the zip call as well?
My knowledge of list comprehensions is more theoretical than from practical use, but I dimly recall that it's possible write something like x0 * x1 for x0, x1 in fct_list, MV.blades[2].

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I had tried the above option along with few simple variations but kept on getting syntax errors. Hence just used zip. Does zip slower the code ? Or is to keep the code simple ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I dimly recall that it's possible write something like x0 * x1 for x0, x1 in fct_list, MV.blades[2].

This is not true, that doesn't work. What you can do though is index out into x0, x1. So the expression could read:

tuple(x0 * x1 for x0, x1 in zip(fct_lst, MV.blades[2]))

@toolforger
Copy link
Contributor

Looks good to me. However, I'm not familiar enough with list comprehensions to be 100% sure that lambda expression and list comprehension are always equivalent, so somebody else will have to verify that.

I have made a few proposals for further improvement, but there's always the case of "do not fix more than one thing at a time in a PR, so it remains reviewable"; so maybe it's better to postpone these to a subsequent PR.

@@ -1299,7 +1299,7 @@ def _eval_subs(self, old, new):
if match:
variables = self_vars_front + self_vars
return Derivative(new, *variables)
return Derivative(*map(lambda x: x._subs(old, new), self.args))
return Derivative(*[ x._subs(old, new) for x in self.args])
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extra space between [ and x.

@jcrist
Copy link
Member

jcrist commented Mar 2, 2015

Looks good, besides my one last comment. Please fix, and squash your commits into one. Will merge after that.

@smichr
Copy link
Member

smichr commented Mar 2, 2015

A few lines were missed in boolalg:

1328:    return int(''.join(list(map(str, list(term)))), 2)
1355:    return list(map(int, s))

@mihir-w
Copy link
Contributor Author

mihir-w commented Mar 2, 2015

There are many instances of type list(map()) in the code. A grep shows nearly 175 such instances. Changing all of them at once seemed too big for a PR. Hence I only replaced map(lambda()) calls which are the slowest. Maybe this should be a new issue ? Or perhaps a part of #4940 or #9079 ?

@debugger22
Copy link
Member

There are many instances of type list(map()) in the code. A grep shows nearly 175 such instances. Changing all of them at once seemed too big for a PR.

Keeping PR short is a good habit. But here you are just replacing one thing with other. I think it won't be a problem to review as test suite will take care of it.

@debugger22
Copy link
Member

Hence I only replaced map(lambda()) calls which are the slowest.

I'm just curious to know. How did you filter?

@mihir-w
Copy link
Contributor Author

mihir-w commented Mar 2, 2015

What should I replace the list(map()) calls with? In python < 3, map() returns a list and hence adding an additional conversion to list does not make sense. However in python versions > 3, map() returns a map object and hence an explicit conversion to list is needed.

Or should I just go ahead and replace every map() instance with a list compression ? In issue #4940, asmeurer asked to test each instance before replacing. That would be a big task ( in terms of time) if each instance has to be checked.

I'm just curious to know. How did you filter?

Ran git grep "map(lambda".

@toolforger
Copy link
Contributor

Actually I think doing PRs à la "do only one thing at a time" is a good idea, even in this case.
PRs with many and more-or-less-subtly different changes tend to get hard to review, simply because concentration will slack off quickly.
Also, I do not think we should rely on the tests to find all problems. They are a supplement to human attention, not a replacement.

@debugger22
Copy link
Member

Or should I just go ahead and replace every map() instance with a list compression ?

This would be terrible as map() IS faster in some cases.

Perhaps you are right about the intensity of the task here at play.

@jcrist
Copy link
Member

jcrist commented Mar 2, 2015

Calls to list(map(...)) can be changed later. We could add map to the compatibility file, so that python < 3 defines map = functools.imap. Then all calls to map that require a list will need a list(map(...)) around them.

This would be terrible as map() IS faster in some cases.

Can you give an example? It's always been my experience that map(builtin_function, data), and (builtin_function(i) for i in data) have had the same speed, and I'd imagine that map`s performance would be highest for builtin functions only.

I'm going to merge this now, as all map(lambda ...) have been replaced. A new PR can be opened for additional fixes.

jcrist added a commit that referenced this pull request Mar 2, 2015
Replacing map(lambda()) calls with list compression
@jcrist jcrist merged commit 0844595 into sympy:master Mar 2, 2015
@debugger22
Copy link
Member

Here's an example from Aaron's comment on #4940.

In [1]: def f(x):
   ...:     return x**2
   ...: 

In [2]: a = range(100)

In [4]: %timeit map(f, a)
10000 loops, best of 3: 128 us per loop

In [5]: %timeit [f(x) for x in a]
10000 loops, best of 3: 162 us per loop

@smichr
Copy link
Member

smichr commented Mar 2, 2015

Partially Fixes #4940.

Since the "Fixes #4940" follows the auto-close syntax, the issue was closed when this was committed. If you don't want that to happen you should slip the adverb between the two as fixes (partially) #4940 or simply don't use the close/fix/resolve words at all and use something like partially addresses #4940. I'll let someone else open the issue again if they feel that is necessary.

@mihir-w
Copy link
Contributor Author

mihir-w commented Mar 2, 2015

Sorry for that. Will be careful next time.

@asmeurer
Copy link
Member

asmeurer commented Mar 2, 2015

I think the performance difference is negligible and a list comprehension is much more readable when the map would use a lambda.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Replace map() with a list compression in most cases
6 participants