Por que é x**4.0
mais rápido que x**4
? Estou usando o CPython 3.5.2.
$ python -m timeit "for x in range(100):" " x**4.0"
10000 loops, best of 3: 24.2 usec per loop
$ python -m timeit "for x in range(100):" " x**4"
10000 loops, best of 3: 30.6 usec per loop
Tentei mudar o poder que levantei para ver como ele funciona e, por exemplo, se eu aumentar x para o poder de 10 ou 16, ele está saltando de 30 para 35, mas se estou aumentando em 10,0 como flutuador, está apenas se movendo em torno de 24,1 ~ 4.
Eu acho que tem algo a ver com conversão de float e potências de 2, talvez, mas eu realmente não sei.
Notei que, em ambos os casos, potências de 2 são mais rápidas, acho que, já que esses cálculos são mais nativos / fáceis para o intérprete / computador. Mas ainda assim, com carros alegóricos quase não está se movendo. 2.0 => 24.1~4 & 128.0 => 24.1~4
mas 2 => 29 & 128 => 62
TigerhawkT3 apontou que isso não acontece fora do circuito. Eu verifiquei e a situação só ocorre (pelo que vi) quando a base está aumentando. Alguma ideia sobre isso?
python
performance
python-3.x
python-3.5
python-internals
arieljannai
fonte
fonte
x**4.0
e 3,9 parax**4
.Respostas:
Os
int
objetos Python 3 são um objeto completo desenvolvido para suportar um tamanho arbitrário; devido a esse fato, elas são tratadas como tal no nível C (veja como todas as variáveis são declaradas comoPyLongObject *
tipolong_pow
). Isso também torna a exponenciação muito mais complicada e tediosa, pois você precisa brincar com oob_digit
array que ele usa para representar seu valor para executá-lo. ( Fonte para os corajosos. - Consulte: Entendendo a alocação de memória para números inteiros grandes em Python para obter mais informações sobrePyLongObject
s.)Os
float
objetos Python , pelo contrário, podem ser transformados em umdouble
tipo C (usandoPyFloat_AsDouble
) e as operações podem ser executadas usando esses tipos nativos . Isso é ótimo , porque, após a verificação de borda casos relevantes, permite Python para usar as plataformaspow
( de Cpow
, que é ) para lidar com a exponenciação real:Onde
iv
e quaisiw
são os nossosPyFloatObject
s originais como Cdouble
s.O fato anterior também explica a discrepância entre Python 2 e 3, então, pensei em abordar esse comentário também porque é interessante.
No Python 2, você está usando o
int
objeto antigo que difere doint
objeto no Python 3 (todos osint
objetos no 3.x são doPyLongObject
tipo). No Python 2, há uma distinção que depende do valor do objeto (ou, se você usar o sufixoL/l
):O
<type 'int'>
que você vê aqui faz a mesma coisafloat
s fazer , ele é convertido com segurança em um Clong
quando exponenciação é realizada sobre ele (oint_pow
também sugere que o compilador para colocá-los num registo se ele pode fazê-lo, de modo que poderia fazer a diferença) :isso permite um bom ganho de velocidade.
Para ver como
<type 'long'>
s são lentos em comparação com<type 'int'>
s, se você colocar ox
nome em umalong
chamada no Python 2 (essencialmente forçando-o a usarlong_pow
como no Python 3), o ganho de velocidade desaparece:Observe que, embora um trecho transforme o
int
tolong
enquanto o outro não (como apontado por @pydsinger), esse elenco não é a força que contribui por trás da desaceleração. A implementação delong_pow
é. (Cronometre as declarações apenas comlong(x)
para ver).Este é o otimizador de olho mágico do CPython, dobrando as constantes para você. Você obtém os mesmos tempos exatos em ambos os casos, pois não há computação real para encontrar o resultado da exponenciação, apenas o carregamento de valores:
O código de byte idêntico é gerado,
'4 ** 4.'
com a única diferença: oLOAD_CONST
carregamento do float em256.0
vez do int256
:Então os tempos são idênticos.
* Todas as opções acima se aplicam apenas ao CPython, a implementação de referência do Python. Outras implementações podem ter um desempenho diferente.
fonte
range
, pois somente a temporização da**
operação não produz diferença entre números inteiros e flutuantes.4**4
é tão rápida quanto4**4.0
), e essa resposta não afeta nada disso.dis(compile('4 ** 4', '', 'exec'))
), portanto o horário deve ser exatamente o mesmo.long(x)**2.
ainda é mais rápido do quelong(x)**2
por um fator de 4-5. (<type 'long'>
tipo no Python 3 é provavelmente explicada pelos esforços feitos para simplificar a linguagem. Se você pode ter um tipo para representar números inteiros, é mais gerenciável que dois (e se preocupa em converter de um para outro quando necessário, usuários ficando confusos etc.). O ganho de velocidade é secundário a isso. A seção de justificativa do PEP 237 também oferece mais algumas dicas.Se olharmos para o bytecode, podemos ver que as expressões são puramente idênticas. A única diferença é um tipo de constante que será um argumento de
BINARY_POWER
. Portanto, é certamente devido a umaint
conversão para um número de ponto flutuante na linha.Atualização: vamos dar uma olhada em Objects / abstract.c no código fonte do CPython:
PyNumber_Power
chamadasternary_op
, que é muito longo para colar aqui, então aqui está o link .Ele chama o
nb_power
slot dex
, passandoy
como argumento.Finalmente, na
float_pow()
linha 686 de Objects / floatobject.c , vemos que os argumentos são convertidos em um Cdouble
antes da operação real:fonte
float_pow
quando isso nem corre para o caso lento?4**4
e4**4.0
fique dobrado constantemente. Esse é um efeito totalmente separado.Porque um está correto, outro é aproximação.
fonte