Aqui está uma pergunta boba e divertida:
Digamos que tenhamos que realizar uma operação simples em que precisamos da metade do valor de uma variável. Normalmente, existem duas maneiras de fazer isso:
y = x / 2.0;
// or...
y = x * 0.5;
Supondo que estejamos usando os operadores padrão fornecidos com a linguagem, qual deles tem melhor desempenho?
Suponho que a multiplicação normalmente é melhor, então tento mantê-la ao codificar, mas gostaria de confirmar isso.
Embora pessoalmente eu esteja interessado na resposta para Python 2.4-2.5, fique à vontade para postar uma resposta para outras linguagens! E se desejar, sinta-se à vontade para postar outras maneiras mais elaboradas (como usar operadores de mudança bit a bit) também.
performance
programming-languages
Edmundito
fonte
fonte
Respostas:
Pitão:
multiplicação é 33% mais rápida
Lua:
=> nenhuma diferença real
LuaJIT:
=> é apenas 5% mais rápido
Conclusões: em Python é mais rápido multiplicar do que dividir, mas à medida que você se aproxima da CPU usando VMs ou JITs mais avançados, a vantagem desaparece. É bem possível que uma futura VM Python a torne irrelevante
fonte
Sempre use o que for mais claro. Qualquer outra coisa que você faça é tentar ser mais esperto que o compilador. Se o compilador for inteligente, fará o melhor para otimizar o resultado, mas nada pode fazer o próximo cara não odiar você por sua solução de deslocamento de bits de baixa qualidade (eu adoro manipulação de bits, a propósito, é divertido. Mas divertido! = Legível )
Otimização prematura é a raiz de todo o mal. Lembre-se sempre das três regras de otimização!
Se você for um especialista e puder justificar a necessidade, use o seguinte procedimento:
Além disso, fazer coisas como remover loops internos quando não são necessários ou escolher uma lista vinculada em um array para uma classificação por inserção não são otimizações, apenas programação.
fonte
Acho que isso está ficando tão minucioso que seria melhor você fazer o que quer que torne o código mais legível. A menos que você execute as operações milhares, senão milhões, de vezes, duvido que alguém notará a diferença.
Se você realmente precisa fazer uma escolha, o benchmarking é o único caminho a percorrer. Descubra quais funções estão causando problemas e, em seguida, descubra onde os problemas ocorrem na função e corrija essas seções. No entanto, ainda duvido que uma única operação matemática (mesmo uma repetida muitas e muitas vezes) seria a causa de qualquer gargalo.
fonte
A multiplicação é mais rápida, a divisão é mais precisa. Você perderá alguma precisão se seu número não for uma potência de 2:
Mesmo se você deixar o compilador descobrir a constante invertida com precisão perfeita, a resposta ainda pode ser diferente.
O problema de velocidade provavelmente só importa nas linguagens C / C ++ ou JIT e, mesmo assim, apenas se a operação estiver em um loop em um gargalo.
fonte
y = x * (1.0/3.0);
e o compilador geralmente calculará 1/3 em tempo de compilação. Sim, 1/3 não é perfeitamente representável no IEEE-754, mas quando você está executando aritmética de ponto flutuante, você perde a precisão de qualquer maneira , quer esteja fazendo multiplicação ou divisão, porque os bits de ordem inferior são arredondados. Se você sabe que seu cálculo é tão sensível a erros de arredondamento, também deve saber como lidar da melhor forma com o problema.(1.0/3.0)
com a divisão por3.0
. Cheguei a 1.0000036666774155, e nesse espaço 7,3% dos resultados foram diferentes. Presumo que eles foram diferentes apenas em 1 bit, mas como a aritmética IEEE é garantida para arredondar para o resultado correto mais próximo, mantenho minha afirmação de que a divisão é mais precisa. Se a diferença é significativa depende de você.Se você deseja otimizar seu código, mas ainda é claro, tente isto:
O compilador deve ser capaz de fazer a divisão em tempo de compilação, para que você obtenha uma multiplicação em tempo de execução. Eu esperaria que a precisão fosse a mesma do
y = x / 2.0
case.Onde isso pode importar, MUITO é em processadores embutidos onde a emulação de ponto flutuante é necessária para calcular a aritmética de ponto flutuante.
fonte
y = x / 2.0;
mas no mundo real, pode ser necessário persuadir o compilador a realizar uma multiplicação menos dispendiosa. Talvez seja menos claro porquey = x * (1.0 / 2.0);
é melhor, e seria mais claro afirmar emy = x * 0.5;
vez disso. Mas mude o2.0
para um7.0
e prefiro ver doy = x * (1.0 / 7.0);
quey = x * 0.142857142857;
.Só vou adicionar algo para a opção "outros idiomas".
C: Uma vez que este é apenas um exercício acadêmico que realmente não faz diferença, pensei em contribuir com algo diferente.
Compilei para a montagem sem otimizações e olhei o resultado.
O código:
compilado com
gcc tdiv.c -O1 -o tdiv.s -S
a divisão por 2:
e a multiplicação por 0,5:
No entanto, quando eu mudei aqueles
int
s paradouble
s (que é o que provavelmente o python faria), obtive o seguinte:divisão:
multiplicação:
Não fiz benchmark de nenhum deste código, mas apenas examinando o código, você pode ver que usando inteiros, a divisão por 2 é mais curta do que a multiplicação por 2. Usando duplas, a multiplicação é mais curta porque o compilador usa os opcodes de ponto flutuante do processador, que provavelmente correr mais rápido (mas na verdade eu não sei) do que não usá-los para a mesma operação. Portanto, em última análise, essa resposta mostrou que o desempenho da multiplicação por 0,5 vs. divisão por 2 depende da implementação da linguagem e da plataforma em que ela é executada. Em última análise, a diferença é insignificante e é algo com que você nunca deve se preocupar, exceto em termos de legibilidade.
Como observação lateral, você pode ver que no meu programa
main()
retornaa + b
. Quando eu remover a palavra-chave volatile, você nunca adivinhará a aparência do assembly (excluindo a configuração do programa):ele fez a divisão, multiplicação e adição em uma única instrução! Obviamente, você não precisa se preocupar com isso se o otimizador for respeitável.
Desculpe pela resposta excessivamente longa.
fonte
movl $5, %eax
o nome da otimização não é importante ou mesmo relevante. Você só queria ser condescendente com a resposta de uma criança de quatro anos.Em primeiro lugar, a menos que você esteja trabalhando em C ou ASSEMBLY, você provavelmente está em uma linguagem de nível superior, onde a memória travada e sobrecargas de chamadas gerais irão absolutamente diminuir a diferença entre multiplicar e dividir ao ponto da irrelevância. Portanto, basta escolher o que é melhor nesse caso.
Se você estiver falando de um nível muito alto, não será mensuravelmente mais lento para qualquer coisa para a qual você possa usá-lo. Você verá em outras respostas, as pessoas precisam multiplicar / dividir um milhão apenas para medir alguma diferença de submilissegundo entre os dois.
Se você ainda estiver curioso, do ponto de vista de otimização de baixo nível:
O Divide tende a ter um pipeline significativamente mais longo do que o Multiply. Isso significa que leva mais tempo para obter o resultado, mas se você puder manter o processador ocupado com tarefas não dependentes, isso não custará mais do que uma multiplicação.
O tamanho da diferença de pipeline depende totalmente do hardware. O último hardware que usei foi algo como 9 ciclos para uma multiplicação de FPU e 50 ciclos para uma divisão de FPU. Parece muito, mas você perderia 1000 ciclos por uma perda de memória, então isso pode colocar as coisas em perspectiva.
Uma analogia é colocar uma torta no microondas enquanto você assiste a um programa de TV. O tempo total que você levou para longe do programa de TV é quanto tempo foi para colocá-lo no micro-ondas e tirá-lo do micro-ondas. O resto do tempo você ainda assistia ao programa de TV. Portanto, se a torta levasse 10 minutos para cozinhar em vez de 1 minuto, ela não consumia mais o tempo de exibição da TV.
Na prática, se você chegar ao nível de se preocupar com a diferença entre Multiply e Divide, precisará entender pipelines, cache, paralisações de ramificação, predição fora de ordem e dependências de pipeline. Se isso não soa como você pretendia chegar com essa pergunta, a resposta correta é ignorar a diferença entre as duas.
Muitos (muitos) anos atrás, era absolutamente crítico evitar divisões e sempre usar multiplicações, mas naquela época os acertos de memória eram menos relevantes e as divisões eram muito piores. Hoje em dia eu dou um valor mais alto à legibilidade, mas se não houver diferença na legibilidade, acho um bom hábito optar por multiplicar.
fonte
Escreva o que declarar mais claramente sua intenção.
Depois que seu programa funcionar, descubra o que é lento e torne-o mais rápido.
Não faça o contrário.
fonte
Faça o que você precisar. Pense no seu leitor primeiro, não se preocupe com o desempenho até ter certeza de que há um problema de desempenho.
Deixe o compilador fazer o desempenho para você.
fonte
Se você estiver trabalhando com inteiros ou tipos de ponto não flutuante, não se esqueça dos operadores de bitshifting: << >>
fonte
Na verdade, há uma boa razão para que, como regra geral, a multiplicação seja mais rápida do que a divisão. A divisão de ponto flutuante no hardware é feita com algoritmos de mudança e subtração condicional ("divisão longa" com números binários) ou - mais provavelmente hoje em dia - com iterações como a de Goldschmidt algoritmo . Shift e subtract precisam de pelo menos um ciclo por bit de precisão (as iterações são quase impossíveis de paralelizar, assim como o shift-and-add da multiplicação), e algoritmos iterativos fazem pelo menos uma multiplicação por iteração. Em ambos os casos, é altamente provável que a divisão leve mais ciclos. É claro que isso não leva em consideração peculiaridades em compiladores, movimentação de dados ou precisão. De modo geral, entretanto, se você estiver codificando um loop interno em uma parte sensível ao tempo de um programa, escrever
0.5 * x
ou,1.0/2.0 * x
ao invés disso,x / 2.0
é uma coisa razoável a se fazer. O pedantismo de "codificar o que é mais claro" é absolutamente verdadeiro, mas todos os três são tão próximos em legibilidade que o pedantismo, neste caso, é apenas pedante.fonte
Sempre aprendi que a multiplicação é mais eficiente.
fonte
A multiplicação geralmente é mais rápida - certamente nunca mais lenta. No entanto, se não for crítico para a velocidade, escreva o que estiver mais claro.
fonte
A divisão de ponto flutuante é (geralmente) especialmente lenta, portanto, embora a multiplicação de ponto flutuante também seja relativamente lenta, é provavelmente mais rápida do que a divisão de ponto flutuante.
Mas estou mais inclinado a responder "isso realmente não importa", a menos que a criação de perfil tenha mostrado que a divisão é um pequeno gargalo em relação à multiplicação. Estou supondo, porém, que a escolha de multiplicação versus divisão não terá um grande impacto no desempenho de seu aplicativo.
fonte
Isso se torna mais problemático quando você está programando em assembly ou talvez em C. Acho que, com a maioria das linguagens modernas, essa otimização está sendo feita para mim.
fonte
Desconfie de "adivinhar que a multiplicação é normalmente melhor, então tento segui-la quando codifico",
No contexto desta questão específica, melhor aqui significa "mais rápido". O que não é muito útil.
Pensar em velocidade pode ser um erro sério. Existem profundas implicações de erro na forma algébrica específica do cálculo.
Consulte aritmética de ponto flutuante com análise de erro . Consulte Problemas básicos em aritmética de ponto flutuante e análise de erros .
Embora alguns valores de ponto flutuante sejam exatos, a maioria dos valores de ponto flutuante é uma aproximação; eles são algum valor ideal mais algum erro. Cada operação se aplica ao valor ideal e ao valor de erro.
Os maiores problemas vêm de tentar manipular dois números quase iguais. Os bits mais à direita (os bits de erro) passam a dominar os resultados.
Neste exemplo, você pode ver que, à medida que os valores ficam menores, a diferença entre números quase iguais cria resultados diferentes de zero em que a resposta correta é zero.
fonte
Eu li em algum lugar que a multiplicação é mais eficiente em C / C ++; Nenhuma ideia sobre idiomas interpretados - a diferença é provavelmente insignificante devido a todas as outras despesas gerais.
A menos que se torne um problema, fique com o que é mais fácil de manter / legível - eu odeio quando as pessoas me dizem isso, mas é tão verdade.
fonte
Eu sugeriria a multiplicação em geral, porque você não precisa gastar os ciclos garantindo que seu divisor não seja 0. Isso não se aplica, é claro, se seu divisor for uma constante.
fonte
Android Java, perfilado em Samsung GT-S5830
Resultados?
A divisão é cerca de 20% mais rápida do que a multiplicação (!)
fonte
a = i*0.5
, nãoa *= 0.5
. É assim que a maioria dos programadores usará as operações.Tal como acontece com os posts # 24 (a multiplicação é mais rápida) e # 30 - mas às vezes ambos são igualmente fáceis de entender:
Acho que ambos são fáceis de ler e tenho que repeti-los bilhões de vezes. Portanto, é útil saber que a multiplicação geralmente é mais rápida.
fonte
Há uma diferença, mas depende do compilador. No início, no vs2003 (c ++), não obtive diferença significativa para os tipos duplos (ponto flutuante de 64 bits). No entanto, executando os testes novamente no vs2010, detectei uma grande diferença, até fator 4 mais rápido para multiplicações. Rastreando isso, parece que o vs2003 e o vs2010 geram um código fpu diferente.
Em um Pentium 4, 2,8 GHz, vs2003:
Em um Xeon W3530, vs2003:
Em um Xeon W3530, vs2010:
Parece que no vs2003 uma divisão em um loop (então o divisor foi usado várias vezes) foi convertida em uma multiplicação com o inverso. No vs2010, essa otimização não é mais aplicada (suponho que seja porque há um resultado ligeiramente diferente entre os dois métodos). Observe também que a CPU realiza divisões mais rápido assim que o numerador for 0,0. Não sei o algoritmo preciso conectado ao chip, mas talvez dependa do número.
Editar 18-03-2013: a observação para vs2010
fonte
n/10.0
por uma expressão do formulário(n * c1 + n * c2)
? Eu esperaria que na maioria dos processadores uma divisão levasse mais do que duas multiplicações e uma divisão, e acredito que a divisão por qualquer constante pode produzir um resultado arredondado corretamente em todos os casos usando a formulação indicada.Aqui está uma resposta boba e divertida:
x / 2.0 não é equivalente a x * 0,5
Digamos que você escreveu esse método em 22 de outubro de 2008.
Agora, 10 anos depois, você aprende que pode otimizar esse trecho de código. O método é referenciado em centenas de fórmulas em todo o seu aplicativo. Então, você o altera e experimenta uma notável melhoria de desempenho de 5%.
Foi a decisão certa alterar o código? Em matemática, as duas expressões são realmente equivalentes. Na ciência da computação, isso nem sempre é verdade. Leia Minimizando o efeito dos problemas de precisão para obter mais detalhes. Se seus valores calculados forem - em algum ponto - comparados com outros valores, você mudará o resultado dos casos extremos. Por exemplo:
Resumindo; uma vez que você se contentar com qualquer um dos dois, mantenha-o!
fonte
2
e0.5
podem ser representados exatamente no IEEE 754, sem nenhuma perda de precisão (ao contrário de, por exemplo ,0.4
ou0.1
, eles não podem).Bem, se assumirmos que uma operação adicionar / subtrair custa 1, então multiplique os custos 5 e divida os custos por cerca de 20.
fonte
Depois de uma discussão tão longa e interessante, aqui está minha opinião sobre isso: Não há uma resposta final para essa pergunta. Como algumas pessoas apontaram, isso depende de ambos, do hardware (cf piotrk e gast128 ) e do compilador (cf @Javier 's tests). Se a velocidade não for crítica, se seu aplicativo não precisar processar em tempo real uma grande quantidade de dados, você pode optar pela clareza usando uma divisão, enquanto se a velocidade de processamento ou a carga do processador forem um problema, a multiplicação pode ser a mais segura. Finalmente, a menos que você saiba exatamente em qual plataforma seu aplicativo será implementado, o benchmark não tem sentido. E para maior clareza do código, um único comentário faria o trabalho!
fonte
Tecnicamente, não existe divisão, existe apenas multiplicação por elementos inversos. Por exemplo, você nunca divide por 2, na verdade você multiplica por 0,5.
'Divisão' - vamos nos enganar que existe por um segundo - é sempre mais difícil que a multiplicação porque para 'dividir'
x
pory
um primeiro é necessário calcular o valory^{-1}
tal quey*y^{-1} = 1
e depois fazer a multiplicaçãox*y^{-1}
. Se você já sabey^{-1}
, não calculá-loy
deve ser uma otimização.fonte