12
The two multiplications, 2*i*i
and 2*(i*i)
, are equal and must generate the same result, what changes is only the order that the multiplications are made, but apparently are treated differently by the interpreter.
In the first multiplication, given the absence of parentheses and considering that all transactions have the same precedence, the interpreter will execute from left to right; that is, first will be done 2*i
and the result multiplied by i
.
In the second multiplication, given the presence of the parentheses, the order of execution is changed because the multiplication i*i
takes precedence over multiplication by 2, so the interpreter will first make i*i
and the result will multiply by 2.
Mathematically, the order of the factors does not change the product, so the result should be the same, but using the native package timeit
it is possible to see a difference between the execution times between these multiplications:
>>> print('2*i*i:', timeit("for i in range(1000): 2*i*i"))
2*i*i: 276.91411599100684
>>> print('2*(i*i):', timeit("for i in range(1000): 2*(i*i)"))
2*(i*i): 279.21798045100877
Tests were done on Repl.it
Note: important to stress that the function
timeit
will run, by default, 1,000,000 times the specified code snippet and that the exact execution time may vary due to processor and inter-computer oscillations.
Why is there this time difference? The presence of parentheses in the expression changes the executed algorithm?
Stressing that the analysis should be done in the official implementation of Python, Cpython, in version 3+. Comparisons with other versions or implementations will be welcome as a way to increase the response.
I think it’s perception, maybe you have some idea of what causes the variation in the interpreter, but in a quick test here the
2*(i*i)
it was faster, but this is the micro-optimization thing, I know that eventually they may be necessary, but the 100ms variation sometimes seems like a lot, but in a program it’s not always, what I must say is that for now can be in fact the interpreter and another time can be caused by the operating system being busy with other tasks, I know that your question is totally technical and I found it very good [...]– Guilherme Nascimento
[...] but I also believe that this may be just an impression of you. Of course you can understand something about the language interpreter that confirms this small variation, I will follow the question also.
– Guilherme Nascimento
@Guilhermenascimento The idea of the question is not about optimization, but about the same algorithms. The Python interpreter actually alters the algorithm to make the multiplication under certain conditions and the difference between these algorithms that generates this time difference - although almost imperceptible.
– Woss
As I said before, here with the tests the results were totally "random", which perhaps implies the issue of the use of the operating system and hardware affect at some level the "competition". But I understand that you may already have some idea what’s causing this specifically, so I’ll keep you looking forward :)
– Guilherme Nascimento
Hi Anderson, it might be worth posting the exact version of Python you used. The difference is fairly small and I smell random variation or small optimization of the final instructions issued to the CPU. Maybe multiplication by two is being replaced by something like
i<<1
in the first case, or perhaps the interpreter issues slightly more complicated instructions due to parentheses. This is the kind of situation where Pypy and Cython should produce significantly different results as well.– Anthony Accioly
@Anthonyaccioly Yes, so I added the Cpython tag. Any analysis should be done in the official language implementation. Comparisons with others are welcome as a way to increase the response. About the version, we can stick to version 3+. I will add the tag.
– Woss
Question was strongly inspired by Why is 2 * x * x Faster than 2 * ( x * x ) in Python 3.x, for integers?
– Woss
Hi Anderson, what I meant about specific version would be 3.X. X on platform Y. I say this because, although the Soen response gives good reasons for the initial version to be faster (different multiplication algorithms according to number size, small number cache, etc), the truth is that locally (Python 3.7.2rc1 in openSUSE 15 - Intel 64 bit) I’m seeing much more random results. It may be due to natural faults of microbenchmarks, may be that something has changed in the interpreter between versions, or, yet, something may change due to platform and CPU type.
– Anthony Accioly
@Anthonyaccioly was "almost" exactly what I said, the influence of the operating system under different hardware plus the use of other resources that can affect the competition in the processes, when I mentioned that it was micro-optimization, I did not mean that the intention was actually to optimize, but it is that in many cases micro-optimizations deceive us, when in fact the reasons that lead to certain "micro-differences" are external factors ;)
– Guilherme Nascimento
@Anthonyaccioly (cc @Guilhermenascimento) I will see if I can write the question then trying to delete these parameters and make it clear that the question is exactly about exchanging algorithms under certain conditions. Thanks for the feedback.
– Woss
The question is very interesting. I was curious to know what factors are affecting the results on my machine and "breaking" the expected difference. Breaking down, the question pointed out by @Anderson links to this other Why is 2 * (i * i) Faster than 2 * i * i in Java?. In this case I was able to confirm the difference mentioned in Java 11. The "tricks" that each compiler/interpreter uses and the effects of everything William really mentioned are fascinating.
– Anthony Accioly