As implementações do BLAS são garantidas para fornecer exatamente o mesmo resultado?

17

Dadas duas implementações BLAS diferentes, podemos esperar que elas façam exatamente os mesmos cálculos de ponto flutuante e retornem os mesmos resultados? Ou pode acontecer, por exemplo, que alguém calcule um produto escalar como e um como ( x 1 y 1 + x 2 y 2 ) + ( x 3 y 3 + x 4

((x1 1y1 1+x2y2)+x3y3)+x4y4
possivelmente dando resultados diferentes na aritmética de ponto flutuante IEEE?
(x1 1y1 1+x2y2)+(x3y3+x4y4),
Federico Poloni
fonte
11
Há algumas queixas sobre a qualidade do BLAS neste tópico , procure CBLAS na página. Isso sugeriria que não só eles não dão o mesmo resultado, mas nem todos eles são bastante precisos para qualquer tarefa ...
Szabolcs

Respostas:

15

Não, isso não é garantido. Se você estiver usando um NETLIB BLAS sem nenhuma otimização, é verdade que os resultados são os mesmos. Porém, para qualquer uso prático do BLAS e LAPACK, utiliza-se um BLAS paralelo altamente otimizado. A paralelização faz com que, mesmo que funcione paralelamente nos registros vetoriais de uma CPU, a ordem de avaliação dos termos únicos seja alterada e a ordem da soma também. Agora segue a propriedade associativa ausente no padrão IEEE que os resultados não são os mesmos. Então, exatamente o que você mencionou pode acontecer.

No NETLIB BLAS, o produto escalar é apenas um loop for desenrolado por um fator 5:

DO I = MP1,N,5
          DTEMP = DTEMP + DX(I)*DY(I) + DX(I+1)*DY(I+1) +
     $            DX(I+2)*DY(I+2) + DX(I+3)*DY(I+3) + DX(I+4)*DY(I+4)
END DO

e cabe ao compilador se cada multiplicação for adicionada ao DTEMP imediatamente ou se todos os 5 componentes forem somados primeiro e depois adicionados ao DTEMP. No OpenBLAS, dependendo da arquitetura, um kernel mais complicado:

 __asm__  __volatile__
    (
    "vxorpd     %%ymm4, %%ymm4, %%ymm4               \n\t"
    "vxorpd     %%ymm5, %%ymm5, %%ymm5               \n\t"
    "vxorpd     %%ymm6, %%ymm6, %%ymm6               \n\t"
    "vxorpd     %%ymm7, %%ymm7, %%ymm7               \n\t"

    ".align 16                           \n\t"
    "1:                          \n\t"
        "vmovups                  (%2,%0,8), %%ymm12         \n\t"  // 2 * x
        "vmovups                32(%2,%0,8), %%ymm13         \n\t"  // 2 * x
        "vmovups                64(%2,%0,8), %%ymm14         \n\t"  // 2 * x
        "vmovups                96(%2,%0,8), %%ymm15         \n\t"  // 2 * x

    "vmulpd      (%3,%0,8), %%ymm12, %%ymm12 \n\t"  // 2 * y
    "vmulpd    32(%3,%0,8), %%ymm13, %%ymm13 \n\t"  // 2 * y
    "vmulpd    64(%3,%0,8), %%ymm14, %%ymm14 \n\t"  // 2 * y
    "vmulpd    96(%3,%0,8), %%ymm15, %%ymm15 \n\t"  // 2 * y

    "vaddpd      %%ymm4 , %%ymm12, %%ymm4 \n\t"  // 2 * y
    "vaddpd      %%ymm5 , %%ymm13, %%ymm5 \n\t"  // 2 * y
    "vaddpd      %%ymm6 , %%ymm14, %%ymm6 \n\t"  // 2 * y
    "vaddpd      %%ymm7 , %%ymm15, %%ymm7 \n\t"  // 2 * y

    "addq       $16 , %0	  	     \n\t"
	"subq	        $16 , %1            \n\t"      
    "jnz        1b                   \n\t"
...

que divide o produto escalar em pequenos produtos escalares de comprimento 4 e os resume.

Usando outras implementações típicas do BLAS, como ATLAS, MKL, ESSL, ... esse problema permanece o mesmo, porque cada implementação do BLAS usa otimizações diferentes para obter código rápido. Mas, tanto quanto eu sei, é preciso um exemplo artificial para causar resultados realmente defeituosos.

Se for necessário que a biblioteca BLAS retorne para os mesmos resultados (bit a bit o mesmo), é necessário usar uma biblioteca BLAS reproduzível, como:

MK tcp Grisu
fonte
8

A resposta curta

Se as duas implementações do BLAS forem escritas para executar as operações exatamente na mesma ordem e as bibliotecas foram compiladas usando os mesmos sinalizadores do compilador e com o mesmo compilador, eles fornecerão o mesmo resultado. A aritmética de ponto flutuante não é aleatória, portanto, duas implementações idênticas fornecerão resultados idênticos.

No entanto, há uma variedade de coisas que podem quebrar esse comportamento em prol do desempenho ...

A resposta mais longa

O IEEE também especifica a ordem em que essas operações são realizadas, além de como cada operação deve se comportar. No entanto, se você compilar sua implementação do BLAS com opções como "-ffast-math", o compilador poderá executar transformações que seriam verdadeiras na aritmética exata, mas não "corretas" no ponto flutuante IEEE. O exemplo canônico é a não associatividade da adição de ponto flutuante, como você apontou. Com as configurações de otimização mais agressivas, a associatividade será assumida e o processador fará o máximo possível em paralelo ao reordenar as operações.

uma+bc

Tyler Olsen
fonte
3
"A aritmética de ponto flutuante não é aleatória" . É triste que isso tem de ser explicitamente indicado, mas parece que muitas pessoas pensam que é ...
tubo de
Bem, imprevisível e aleatório parecem bastante semelhantes, e muitas classes de programação de introdução dizem "nunca compare floats por igualdade", o que dá a impressão de que o valor exato não pode ser invocado da mesma maneira que números inteiros.
Tyler Olsen
@TylerOlsen Isso não é relevante para a questão, e não é por isso que essas classes dizem essas coisas, mas no IIRC costumava haver uma classe de erros de compilador nos quais a igualdade não era confiável. Por exemplo, às vezes if (x == 0) assert(x == 0) pode falhar, o que de um certo ponto de vista é tão bom quanto aleatório.
Kirill
@ Kirill Desculpe, eu estava simplesmente tentando dizer que muitas pessoas nunca aprendem realmente como o ponto flutuante funciona. Quanto ao ponto histórico, isso é meio assustador, mas deve ter sido resolvido antes de eu entrar no jogo.
Tyler Olsen
@TylerOlsen Opa, meu exemplo está errado. Deveria ser if (x != 0) assert(x != 0), devido à aritmética de precisão estendida.
Kirill
4

Em geral, não. Deixando de lado a associatividade, a escolha de sinalizadores do compilador (por exemplo, instruções SIMD ativadas, uso de adição de multiplicação por fusão etc.) ou o hardware (por exemplo, se a precisão estendida está sendo usada) pode produzir resultados diferentes.

Existem alguns esforços para obter implementações reproduzíveis do BLAS. Consulte ReproBLAS e ExBLAS para obter mais informações.

Juan M. Bello-Rivas
fonte
11
Consulte também o recurso Reprodutibilidade numérica condicional (CNR) nas versões recentes do MKL BLAS da Intel. Perceba que obter esse nível de reprodutibilidade normalmente desacelera seus cálculos e pode desacelerá-los bastante!
22817 Brian Borchers