O que é mais rápido na divisão, Inteiros ou Reais?
Eu sempre achei que trabalhar com numeros Inteiros fosse mais rápido do que com números reais, sei lá, são cheios de casas depois da virgula,e achei que fossem mais complicados de processar, enfim, fiz uns testes comparando o código 1 com o código2:
Código1
Código2
As duas funções dão o mesmo resultado, mais uma usar números de ponto flutuante para fazer os calculos e a outra números inteiros, mas a função que usa ponto flutuante(Double) foi 2 vezes mais rápida que a outra que usa integer.
Agora eu pergunto, não deveria ser o contrário?
Depois alterei a primeira função:
ao invéz de
dessa forma
e com o Round ficou coma mesma performance da 1º função, mais rápido do que tava antes com o div.
sempre achei que usar esse Round seria disperdício se poderia usar o div, ou o Trunc, que achava que era mais rápido, mas não é.
fazer Round(V1/V2) foi mais rápido que usar o [b:ecb54e0e51]div[/b:ecb54e0e51], alguém saberia dizer porque?
para outras operações, somar, subtrair, multiplicar, os Inteiros guanharam, mas na divisão os Reais triunfaram, achei muito estranho isso, será que fiz alguma coisa de errado?
Agradeço qualquer palpite :)
PS: Fiz o teste num Durom 1.3.
Código1
function SomaInteiros: Integer; var I: Integer; Valor1, valor2, Valor3: Integer; begin Valor1 := 2; Valor2 := 20000; for I := 100000000 - 1 downto 0 do //repete cem milhões de vezes Valor3 := Valor2 div Valor1; Result := Valor3; end;
Código2
function SomaReais: Double; var I: Integer; Valor1, valor2, Valor3: Double; begin Valor1 := 2.0; Valor2 := 20000.0; for I := 100000000 - 1 downto 0 do //repete cem milhões de vezes Valor3 := Valor2/Valor1; Result := Valor3; end;
As duas funções dão o mesmo resultado, mais uma usar números de ponto flutuante para fazer os calculos e a outra números inteiros, mas a função que usa ponto flutuante(Double) foi 2 vezes mais rápida que a outra que usa integer.
Agora eu pergunto, não deveria ser o contrário?
Depois alterei a primeira função:
ao invéz de
Valor3 := Valor2 div Valor1;
dessa forma
Valor3 := Round(Valor2/Valor1);
e com o Round ficou coma mesma performance da 1º função, mais rápido do que tava antes com o div.
sempre achei que usar esse Round seria disperdício se poderia usar o div, ou o Trunc, que achava que era mais rápido, mas não é.
fazer Round(V1/V2) foi mais rápido que usar o [b:ecb54e0e51]div[/b:ecb54e0e51], alguém saberia dizer porque?
para outras operações, somar, subtrair, multiplicar, os Inteiros guanharam, mas na divisão os Reais triunfaram, achei muito estranho isso, será que fiz alguma coisa de errado?
Agradeço qualquer palpite :)
PS: Fiz o teste num Durom 1.3.
Marcelo Saviski
Curtidas 0
Respostas
Massuda
23/02/2005
Mais uma peça para seu quebra cabeças... Experimentei fazer uma modificação na sua função SomaInteiros...desse modo, os inteiros não tem sinal e, como era esperado inicialmente, SomaInteiros é mais rápido que SomaReais.
function SomaInteiros: Longword; var I: Integer; Valor1, valor2, Valor3: Longword; begin Valor1 := 2; Valor2 := 20000; for I := 100000000 - 1 downto 0 do //repete cem milhões de vezes Valor3 := Valor2 div Valor1; Result := Valor3; end;
GOSTEI 0
Marcelo Saviski
23/02/2005
Hum, vou testar isso quando chegar em casa, mas de qualquer forma a SomaReais ainda me parace sair ´guanhando´, já que independe de sinal
GOSTEI 0
Motta
23/02/2005
O Help do D5 fala isto :
An integer type represents a subset of the whole numbers. The generic integer types are Integer and Cardinal; use these whenever possible, since they result in the best performance for the underlying CPU and operating system. The table below gives their ranges and storage formats for the current 32-bit Object Pascal compiler.
Mas uma olhada nos tipos ...
TypeRangeSignificant digitsSize in bytes
Real482.9 x 10^–39 .. 1.7 x 10^3811–126
Single1.5 x 10^–45 .. 3.4 x 10^387–84
Double5.0 x 10^–324 .. 1.7 x 10^30815–168
TypeRangeFormat
Integer–2147483648..2147483647signed 32-bit
Qual o tamanho da palavra em Delphi , teria algo a ver ?
Por esta não esperava Marcelo.
An integer type represents a subset of the whole numbers. The generic integer types are Integer and Cardinal; use these whenever possible, since they result in the best performance for the underlying CPU and operating system. The table below gives their ranges and storage formats for the current 32-bit Object Pascal compiler.
Mas uma olhada nos tipos ...
TypeRangeSignificant digitsSize in bytes
Real482.9 x 10^–39 .. 1.7 x 10^3811–126
Single1.5 x 10^–45 .. 3.4 x 10^387–84
Double5.0 x 10^–324 .. 1.7 x 10^30815–168
TypeRangeFormat
Integer–2147483648..2147483647signed 32-bit
Qual o tamanho da palavra em Delphi , teria algo a ver ?
Por esta não esperava Marcelo.
GOSTEI 0
Beppe
23/02/2005
[quote:05c64cfef1=´Marcelo Saviski´]O que é mais rápido na divisão, Inteiros ou Reais?[/quote:05c64cfef1]
Depende.
Para divisão inteira, a operação é a mesma tanto faz quais forem os valores dos operandos. Para a divisão de flutuantes, como você presumiu, o algoritmo é *bem* maior(em números de portas lógicas), e com tratamento especial para casos simples. Mas esta complexidade fica fora do loop principal do algoritmo.
A disparidade pode ser grande em favor dos flutuantes se você usar como divisor um poder de dois e seus negativos, que se transforma em uma réles subtração. Pode observar isto na janela da FPU.
O que afetou também resultados foram resultados externos como mudança de threads, e o fato que a FPU é ociosa a maior parte do tempo, ao contrário da ULA(unidade que executa operações em inteiros, uns 99¬ das instruções).
Sobre o tamanho da palavra...não testei isto, mas pode ter variações. Byte´s devem ter a mesma performance de Integer(que no meu teste foi superioir a Cardinal), mas Word´s deve ser mais lento porque a instrução exige um prefixo de 1 byte. Para flutuantes, a performance é semelhante.
Aqui segue o método que eu desenvolvi para chegar próximo a performance absoluta.
Basicamente, o número de iterações não pode ser grande, pq cairia fora do ´time slice´. Eu usei um segundo loop, externo, mas este não deve fazer diferença.
Substituindo Valor3 por Valor2, tem-se resultados mais dinâmicos e interessantes, e que representa melhor padrões de uso.
Depende.
Para divisão inteira, a operação é a mesma tanto faz quais forem os valores dos operandos. Para a divisão de flutuantes, como você presumiu, o algoritmo é *bem* maior(em números de portas lógicas), e com tratamento especial para casos simples. Mas esta complexidade fica fora do loop principal do algoritmo.
A disparidade pode ser grande em favor dos flutuantes se você usar como divisor um poder de dois e seus negativos, que se transforma em uma réles subtração. Pode observar isto na janela da FPU.
O que afetou também resultados foram resultados externos como mudança de threads, e o fato que a FPU é ociosa a maior parte do tempo, ao contrário da ULA(unidade que executa operações em inteiros, uns 99¬ das instruções).
Sobre o tamanho da palavra...não testei isto, mas pode ter variações. Byte´s devem ter a mesma performance de Integer(que no meu teste foi superioir a Cardinal), mas Word´s deve ser mais lento porque a instrução exige um prefixo de 1 byte. Para flutuantes, a performance é semelhante.
Aqui segue o método que eu desenvolvi para chegar próximo a performance absoluta.
Basicamente, o número de iterações não pode ser grande, pq cairia fora do ´time slice´. Eu usei um segundo loop, externo, mas este não deve fazer diferença.
const Divisor = 81; Dividendo = 10000000; Iteracoes1 = 10000; Iteracoes2 = 1000; function SomaInteiros: Integer; var I: Integer; Valor1, Valor2, Valor3: Integer; begin Valor1 := Divisor; Valor2 := Dividendo; for I := 0 to Iteracoes2 - 1 do Valor3 := Valor2 div Valor1; Result := Valor3; end; function SomaReais: Double; var I: Integer; Valor1, Valor2, Valor3: Double; begin Valor1 := Divisor; Valor2 := Dividendo; for I := 0 to Iteracoes2 - 1 do Valor3 := Valor2 / Valor1; Result := Valor3; end; procedure TForm1.Button1Click(Sender: TObject); var S, F, Total: Int64; I: Integer; begin // processa eventos na fila, como WM_PAINT, WM_LBUTTONDOWN, WM_WM_KEYUP for I := 0 to 9 do Application.ProcessMessages; // ganha fatias de tempo maiores SetPriorityClass(0, REALTIME_PRIORITY_CLASS); SetThreadPriority(0, THREAD_PRIORITY_TIME_CRITICAL); SetThreadPriorityBoost(0, False); SetProcessPriorityBoost(0, False); Total := 0; for I := 0 to Iteracoes1 - 1 do begin Application.ProcessMessages; // começa a contagem no início da nova fatia de tempo Sleep(0); QueryPerformanceCounter(S); SomaInteiros; // SomaReais; QueryPerformanceCounter(F); Total := Total - S + F; end; Caption := IntToStr(Total); end;
Substituindo Valor3 por Valor2, tem-se resultados mais dinâmicos e interessantes, e que representa melhor padrões de uso.
GOSTEI 0
Massuda
23/02/2005
De uma velha documentação do MASM sobre o chip Pentium...Por esses números, faz sentido dizer que a divisão em PF foi otimizada no chip e que pode ser mais rápida do que dividir inteiros 32-bits com sinal.
Instrução Descrição Ciclos de Máquina DIV divisão sem sinal byte=17 word=25 dword=41 IDIV divisão com sinal byte=22 word=30 dword=46 FDIV divisão em PF 39
GOSTEI 0
Beppe
23/02/2005
Infelizmente estas tabelas não existem mais...acho que pq existe uma boa diferenças entre implementações.
GOSTEI 0
Marcelo Saviski
23/02/2005
Testei com Cardinal ou LongWord no lugar de Integer mas acabou ficando igual.
Ainda não pude testar o código do Beppe, mas o que significa?:), num código qualquer, por exemplo, pra processar uma imagem e aplicar um filtro, seria melhor como?
Cor e Filtro entre 0 e 255
vlw pelas respostas, e outra coisa, o processador influenciaria nessa diferença? Por exemplo num Intel ou num AMD o tempo decorrido entre Inteiros e Reais seria maior em um do que no outro? ou isso é meio padrão?
Ainda não pude testar o código do Beppe, mas o que significa?:), num código qualquer, por exemplo, pra processar uma imagem e aplicar um filtro, seria melhor como?
Cor e Filtro entre 0 e 255
Cor := (Cor*Filtro) div 255; -ou- Cor := Round((Cor/255)*Filtro);
vlw pelas respostas, e outra coisa, o processador influenciaria nessa diferença? Por exemplo num Intel ou num AMD o tempo decorrido entre Inteiros e Reais seria maior em um do que no outro? ou isso é meio padrão?
GOSTEI 0
Massuda
23/02/2005
[quote:28fdb25faf=´Marcelo Saviski´]Testei com Cardinal ou LongWord no lugar de Integer mas acabou ficando igual.[/quote:28fdb25faf]Interessante... na máquina que uso no trabalho (Intel P4 2.66GHz), seu teste usando LongWord foi mais rápido do que usando Double.
[quote:28fdb25faf=´Marcelo Saviski´]o processador influenciaria nessa diferença? Por exemplo num Intel ou num AMD o tempo decorrido entre Inteiros e Reais seria maior em um do que no outro? ou isso é meio padrão?[/quote:28fdb25faf]Você está se referindo aos tempos em si (resultado quantitativo) ou ao fato de dividir Integer´s demorar mais que dividir Double´s?
[quote:28fdb25faf=´Marcelo Saviski´]o processador influenciaria nessa diferença? Por exemplo num Intel ou num AMD o tempo decorrido entre Inteiros e Reais seria maior em um do que no outro? ou isso é meio padrão?[/quote:28fdb25faf]Você está se referindo aos tempos em si (resultado quantitativo) ou ao fato de dividir Integer´s demorar mais que dividir Double´s?
GOSTEI 0
Massuda
23/02/2005
Infelizmente estas tabelas não existem mais...acho que pq existe uma boa diferenças entre implementações.
Acho que isso tem relação com o fato das CPUs modernas usarem pipelines e execução especulativa (acho que era esse o nome que estava no manual da Intel) e outros bichos para agilizar a execução do código; com isso tudo, fica difícil prever quantos ciclos de máquina uma instrução irá efetivamente gastar.GOSTEI 0
Marcelo Saviski
23/02/2005
Dizia a proporção de quantas vezes um era mais rápido que o outro.
Mas muda sim, pelo teste que vc fez com LongWord ai ser diferente daqui
Mas muda sim, pelo teste que vc fez com LongWord ai ser diferente daqui
GOSTEI 0
Beppe
23/02/2005
[quote:e0edd5dc70=´Marcelo Saviski´]...num código qualquer, por exemplo, pra processar uma imagem e aplicar um filtro, seria melhor como?
Cor e Filtro entre 0 e 255
[/quote:e0edd5dc70]
Não estou certo sobre divisões, mas multiplicações por constantes, são transformadas em sequencias mais simples(ADD, SUB, SHL, LEA) e mais eficientes...
Se o caso não se repetir para divisões, e vc não precisar de exatidão, posso sugerrir uma modificação. (Cor * (Filtro + 1)) div 256. Divisão por poder de 2 se torna em um SHR.
[quote:e0edd5dc70=´Marcelo Saviski´]vlw pelas respostas, e outra coisa, o processador influenciaria nessa diferença? Por exemplo num Intel ou num AMD o tempo decorrido entre Inteiros e Reais seria maior em um do que no outro? ou isso é meio padrão?[/quote:e0edd5dc70]
O que é padrão são os resultados, a qualidade da implementação varia entre modelaos diferentes(586, 686, Celeron, Athlon, Opteron...).
É verdade, tem isso também. Eu tentei medir, mas são truques demais pra considerar...
Cor e Filtro entre 0 e 255
Cor := (Cor*Filtro) div 255; -ou- Cor := Round((Cor/255)*Filtro);
[/quote:e0edd5dc70]
Não estou certo sobre divisões, mas multiplicações por constantes, são transformadas em sequencias mais simples(ADD, SUB, SHL, LEA) e mais eficientes...
Se o caso não se repetir para divisões, e vc não precisar de exatidão, posso sugerrir uma modificação. (Cor * (Filtro + 1)) div 256. Divisão por poder de 2 se torna em um SHR.
[quote:e0edd5dc70=´Marcelo Saviski´]vlw pelas respostas, e outra coisa, o processador influenciaria nessa diferença? Por exemplo num Intel ou num AMD o tempo decorrido entre Inteiros e Reais seria maior em um do que no outro? ou isso é meio padrão?[/quote:e0edd5dc70]
O que é padrão são os resultados, a qualidade da implementação varia entre modelaos diferentes(586, 686, Celeron, Athlon, Opteron...).
Acho que isso tem relação com o fato das CPUs modernas usarem pipelines e execução especulativa (acho que era esse o nome que estava no manual da Intel) e outros bichos para agilizar a execução do código; com isso tudo, fica difícil prever quantos ciclos de máquina uma instrução irá efetivamente gastar.
É verdade, tem isso também. Eu tentei medir, mas são truques demais pra considerar...
GOSTEI 0
Marcelo Saviski
23/02/2005
ow, agora que vi que coloque o nome de Soma na função que divide dois números...
E comigo, o código que o Beppe passou, fica na mesma, com os Reais fica quase que ´exatamente´ a metade dos ciclos dos Inteiros, mesmop trocando o valor3 por Valor2
Eu sempre procurava evitar colocar ponto flutuante no meio dos códigos pra otimizar, mas acho que vou repensar antes de fazer assim daqui pra frente.
E comigo, o código que o Beppe passou, fica na mesma, com os Reais fica quase que ´exatamente´ a metade dos ciclos dos Inteiros, mesmop trocando o valor3 por Valor2
Eu sempre procurava evitar colocar ponto flutuante no meio dos códigos pra otimizar, mas acho que vou repensar antes de fazer assim daqui pra frente.
GOSTEI 0