I have come up with something rather odd. The goal is to evaluate the length of an iterable.
python -m timeit --setup="x = range(1000)" "x=list(x);len(x)"
1000000 loops, best of 3: 1.82 usec per loop
python -m timeit --setup="x = range(1000)" "len(list(x))"
100000 loops, best of 3: 9.92 usec per loop
Can anyone explain the reason the first method is quicker ?
I tried to look at assembly instructions, but it does not help understanding this behavior.
With: x=list(x);len(x)
>>> dis.dis(meth1)
2 0 LOAD_GLOBAL 0 (list)
2 LOAD_FAST 0 (it)
4 CALL_FUNCTION 1
6 STORE_FAST 1 (x)
3 8 LOAD_GLOBAL 1 (len)
10 LOAD_FAST 1 (x)
12 CALL_FUNCTION 1
14 RETURN_VALUE
With len(list(x))
:
>>> dis.dis(meth2)
2 0 LOAD_GLOBAL 0 (len)
2 LOAD_GLOBAL 1 (list)
4 LOAD_FAST 0 (it)
6 CALL_FUNCTION 1
8 CALL_FUNCTION 1
10 RETURN_VALUE
CodePudding user response:
You have to consider the fact that setup is executed only once.
The test
--setup="x = range(1000)" "x=list(x);len(x)"
converts the range into a list and stores it back to x
. Next iteration, it is already a list, so there's nothing more to do.
If you change that to a different variable name, the difference should be gone:
--setup="x = range(1000)" "something=list(x);len(something)"
In the other test
--setup="x = range(1000)" "len(list(x))"
x
remains a range and needs to be converted into a list 100000 times.