Por que 2*i*i tende a ser mais rápido que 2*(i*i) quando i é inteiro?

As duas multiplicações, 2*i*i e 2*(i*i), são iguais e devem gerar o mesmo resultado, o que muda apenas é a ordem que as multiplicações são feitas, mas aparentemente são tratadas de forma diferente pelo interpretador.

Na primeira multiplicação, dada a ausência dos parenteses e considerando que todas as operações possuem a mesma precedência, o interpretador executará da esquerda para a direita; isto é, primeiro será feito 2*i e o resultado multiplicado por i.

Na segunda multiplicação, dada a presença dos parênteses, a ordem de execução é alterada pois a multiplicação i*i passa a ter maior precedência perante a multiplicação por 2, então o interpretador primeiro fará i*i e o resultado multiplicará por 2.

Matematicamente, a ordem dos fatores não altera o produto, então o resultado deve ser o mesmo, porém utilizando o pacote nativo timeit é possível ver uma diferença entre os tempos de execução entre essas multiplicações:

>>> 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  >>> print('(2*i)*i:', timeit("for i in range(1000): 2*(i*i)")) (2*i)*i: 279.29717569499917 

Testes foram feitos no Repl.it

Nota: importante salientar que a função timeit executará, por padrão, 1.000.000 vezes o trecho de código indicado e que o tempo exato de execução pode variar devido a oscilações do processador e entre computadores.

Por que existe essa diferença de tempo? A presença dos parenteses na expressão altera o algoritmo executado?