Float vs Double Performance

91

Eu fiz alguns testes de tempo e também li alguns artigos como este (último comentário), e parece que no Release build, os valores float e double levam a mesma quantidade de tempo de processamento.

Como isso é possível? Quando a flutuação é menos precisa e menor em comparação com os valores duplos, como o CLR pode obter dobras no mesmo tempo de processamento?

Joan Venge
fonte
10
Eu não acho que seja uma duplicata exata, pois este está perguntando o motivo por trás disso, enquanto o outro usuário está perguntando se é realmente mais rápido, mas não necessariamente por quê,
Joan Venge
Supostamente uma duplicata exata de Are doubles mais rápido que floats em C #? (reivindicado em 2009 por outro usuário).
Peter Mortensen

Respostas:

153

Em processadores x86, pelo menos, floate doublecada um será convertido em um real de 10 bytes pela FPU para processamento. A FPU não possui unidades de processamento separadas para os diferentes tipos de ponto flutuante que suporta.

O conselho antigo que floaté mais rápido do que o doubleaplicado há 100 anos, quando a maioria das CPUs não tinha FPUs embutidos (e poucas pessoas tinham chips FPU separados), então a maior parte da manipulação de ponto flutuante era feita em software. Nessas máquinas (que eram movidas a vapor gerado pelos poços de lava), era mais rápido usar floats. Agora, o único benefício real para floats é que eles ocupam menos espaço (o que só importa se você tiver milhões deles).

P papai
fonte
9
Talvez não 100 anos atrás ... Algumas FPUs suportam manipulação nativa em níveis flutuantes, duplos e de 80 bits e serão executadas mais rapidamente em comprimentos mais curtos. Alguns vão realmente executar algumas coisas mais lentamente em comprimentos mais curtos também ... :-)
Brian Knoblauch
4
Possível exceção: acho que o tempo para as divisões depende do número de bits (1 ciclo de clock / 2 bits). Os tempos que fiz de float vs divisão dupla parecem corresponder a isso.
Neil Coffey
21
Advertência para código SIMD - uma vez que você pode embalar 2x flutuantes do que duplas em um registro SIMD (por exemplo, SSE), operar potencialmente em flutuadores pode ser mais rápido. Mas, como é C #, isso provavelmente não acontecerá.
Calyth de
13
@P Daddy: Eu diria que a vantagem de espaço é importante em todos os níveis da hierarquia do cache. Quando o cache de dados de primeiro nível tem 16 KB e você está processando uma matriz de 4.000 números, o float pode ser facilmente mais rápido.
Peter G.
4
@artificialidiot Nunca diga nunca;). SIMD é compatível com .NET desde 4.6
ghord
13

Tive um pequeno projeto em que usei CUDA e lembro que o float era mais rápido que o dobro também. Por uma vez, o tráfego entre o Host e o Dispositivo é menor (o Host é a CPU e a RAM "normal" e o Dispositivo é a GPU e a RAM correspondente ali). Mas mesmo que os dados residam no dispositivo o tempo todo, é mais lento. Acho que li em algum lugar que isso mudou recentemente ou deve mudar com a próxima geração, mas não tenho certeza.

Portanto, parece que a GPU simplesmente não consegue lidar com precisão dupla nativamente nesses casos, o que também explicaria porque o GLFloat é geralmente usado em vez do GLDouble.

(Como eu disse, só consigo me lembrar, apenas descobri isso enquanto procurava por float vs. double em uma CPU.)

Mene
fonte
5
GPUs são animais totalmente diferentes de FPUs. Como outros mencionaram, o formato nativo da FPU é a precisão dupla de 80 bits. E isso já faz muito tempo. As GPUs, entretanto, abordam esse campo com uma precisão única. É bem conhecido que o desempenho do DP FP (ponto flutuante de precisão dupla) é frequentemente exatamente a metade do desempenho do SP FP. Parece que muitas vezes eles têm unidades de ponto flutuante SP e precisam reutilizar a unidade para cobrir a precisão dupla. O que produz exatamente dois ciclos em comparação com um. Essa é uma enorme diferença de desempenho , que me surpreendeu quando me deparei com isso.
Csaba Toth
1
Alguns cálculos científicos exigem DP FP, e os principais fabricantes de GPU não anunciaram a penalidade de desempenho em torno disso. Agora eles (AMD, nVidia) parecem melhorar um pouco no tópico DP vs SP. Os vários núcleos do Intel Xeon Phi contêm FPUs do Pentium e observe que a Intel enfatizou seus recursos de precisão dupla . É aí que ele pode realmente competir com monstros GPGPU.
Csaba Toth
12

Ainda existem alguns casos onde floats são preferidos - com a codificação OpenGL, por exemplo, é muito mais comum usar o tipo de dados GLFloat (geralmente mapeado diretamente para float de 16 bits), pois é mais eficiente na maioria das GPUs do que GLDouble.

Cruachan
fonte
3
Talvez devido ao maior rendimento de dados? Se você tiver uma matriz de números (z-buffer etc.), o tamanho dos dados se torna mais importante, e evitar conversões entre float e double acelera o manuseio. Meu palpite.
Lucero,
2
Sem dúvida, rendimento. Além disso, dado o contexto especializado, é improvável que algo visível seja ganho com o uso de dobras sobre os flutuadores, então por que desperdiçar memória - especialmente porque ela é menor em GPUs do que em CPUs
Cruachan,
1
Taxa de transferência e também o fato de que SP FP (ponto flutuante de precisão única) é mais o formato nativo das FPUs internas da GPU do que DP FP (precisão dupla). Veja meu comentário à resposta de @Mene. GPU's e CPU FPUs são animais muito diferentes, a FPU da CPU está pensando em DP FP.
Csaba Toth
12

Depende do sistema de 32 ou 64 bits . Se você compilar para 64 bits, o dobro será mais rápido. Compilado para 32 bits em 64 bits (máquina e sistema operacional) fez flutuar cerca de 30% mais rápido:

    public static void doubleTest(int loop)
    {
        Console.Write("double: ");
        for (int i = 0; i < loop; i++)
        {
            double a = 1000, b = 45, c = 12000, d = 2, e = 7, f = 1024;
            a = Math.Sin(a);
            b = Math.Asin(b);
            c = Math.Sqrt(c);
            d = d + d - d + d;
            e = e * e + e * e;
            f = f / f / f / f / f;
        }
    }

    public static void floatTest(int loop)
    {
        Console.Write("float: ");
        for (int i = 0; i < loop; i++)
        {
            float a = 1000, b = 45, c = 12000, d = 2, e = 7, f = 1024;
            a = (float) Math.Sin(a);
            b = (float) Math.Asin(b);
            c = (float) Math.Sqrt(c);
            d = d + d - d + d;
            e = e * e + e * e;
            f = f / f / f / f / f;
        }
    }

    static void Main(string[] args)
    {
        DateTime time = DateTime.Now;
        doubleTest(5 * 1000000);
        Console.WriteLine("milliseconds: " + (DateTime.Now - time).TotalMilliseconds);

        time = DateTime.Now;
        floatTest(5 * 1000000);
        Console.WriteLine("milliseconds: " + (DateTime.Now - time).TotalMilliseconds);

        Thread.Sleep(5000);
    }
Azul amargo
fonte
2
Você já considerou que esses 30% podem ser por causa dos casts extras que você usa ??
Rasmus Damgaard Nielsen
@RasmusDamgaardNielsen Os elencos são parte do problema já que Mathfunciona com duplo. Mas você interpretou mal minha postagem: meus testes me mostraram flutuar melhor no desempenho.
Bitterblue de
2
Os resultados postados acima são falsos. Meus testes mostram que em uma máquina de 32 bits mais antiga com .NET 4.0 no modo Release, o desempenho floate doublesão virtualmente idênticos. Menos de 0,3% de diferença quando calculada a média de muitos testes independentes, onde cada teste exerceu operações de multiplicação, divisão e adição em variáveis ​​encadeadas consecutivamente (para evitar que qualquer otimização do compilador atrapalhe). Eu tentei um segundo conjunto de testes com Math.Sin()e Math.Sqrt()e também tem resultados idênticos.
Molho Especial