Lapack: Requisitos de processador para LAPACK

Criado em 4 jun. 2021  ·  16Comentários  ·  Fonte: Reference-LAPACK/lapack

Estamos trabalhando na tradução do LAPACK para o .NET. Nós escrevemos um compilador FORTRAN que traduz com sucesso todo o LAPACK, incluindo todos os testes. Em tipos de dados reais (quase) todos os testes passam. Em dados complexos, ainda estamos vendo alguns problemas de precisão.

Exemplo:
XEIGTSTZ <zec.in - falha devido ao estouro negativo em ZLAHQR.
Etapas para reproduzir: ZGET37 -> knt == 31, ZHSEQR -> ZLAHQR -> no final da segunda etapa QR (ITS == 2) o código a seguir causa underflow (em certos registros, veja abaixo)

TEMP = H( I, I-1 )
    IF( DIMAG( TEMP ).NE.RZERO ) THEN
        RTEMP = ABS( TEMP)    ! <-- underflow on TEMP = (~1e-0173, ~1e-0173)
        IF (RTEMP .EQ. RZERO) RTEMP = CABS1(TEMP)
        H( I, I-1 ) = RTEMP
        TEMP = TEMP / RTEMP
        IF( I2.GT.I )

Nosso compilador tem como alvo o .NET CLR. Seu JIT decide usar registradores SSE para ABS (TEMP), o que leva ao underflow no cálculo intermediário da magnitude. Ifort (como outro exemplo) usa registradores de ponto flutuante nesta situação, portanto, não underflow (por causa de seu comprimento maior: 80 bits). Estou tentando obter uma imagem clara (er) do que esperar do LAPACK em relação à precisão / intervalo de números que ele requer do compilador / processador em tempo de execução.

Todos os testes de precisão dupla são projetados para exigir pelo menos registros de 64 bits? Ou eles são projetados de forma a ter sucesso para o conjunto de compiladores FORTRAN populares disponíveis hoje? (No primeiro caso, o problema acima (e outros semelhantes) pode exigir atenção. Devo registrar um problema para eles?)

Procurei alguma especificação, mas ainda não consegui encontrar. Qualquer link também seria apreciado. Desde já, obrigado!

Question

Todos 16 comentários

O underflow em si não é o verdadeiro problema. Após o underflow, o algoritmo muda para CABS1, que é menos sujeito a underflow. O problema que se cria é que TEMP não será exatamente unitário, levando ao arredondamento em Z.

Uma possível solução é pré-escalonar usando CABS1 e, em seguida, corrigir usando ABS (devido ao primeiro escalonamento, o ABS não deve mais transbordar). (Eu não recebo o estouro negativo na minha máquina, então não posso testá-lo para você)

IF (RTEMP .EQ. RZERO) THEN
    RTEMP = CABS1(TEMP)
    H( I, I-1 ) = RTEMP
    TEMP = TEMP / RTEMP
    RTEMP = ABS( TEMP)
    H( I, I-1 ) = H( I, I-1 )*RTEMP
    TEMP = TEMP / RTEMP
END IF

Acho que os testes são definitivamente projetados para serem bem-sucedidos para o conjunto de compiladores FORTRAN populares, porque é assim que eles são executados. Prever falta / estouro é incrivelmente difícil. Pelo menos no meu caso, essas sub-rotinas são projetadas simplesmente testando-as (usando os compiladores populares) por completo e corrigindo qualquer excesso / insuficiência de fluxo que encontrarmos.

Obrigado! Isso é muito útil.
Tentamos nos recuperar do underflow usando CABS1. Mas nossa tentativa não estava indo longe o suficiente. Sua sugestão parece ser muito melhor. Usando ...

*
*        Ensure that H(I,I-1) is real.
*
         TEMP = H( I, I-1 )
         IF( DIMAG( TEMP ).NE.RZERO ) THEN
            RTEMP = ABS( TEMP)
            IF (RTEMP .EQ. RZERO) THEN 
                RTEMP = CABS1(TEMP)
                H( I, I-1 ) = RTEMP
                TEMP = TEMP / RTEMP
                RTEMP = ABS( TEMP)
                H( I, I-1 ) = H( I, I-1 )*RTEMP
            ELSE 
                H( I, I-1 ) = RTEMP
            END IF
            TEMP = TEMP / RTEMP
            IF( I2.GT.I )
     $         CALL ZSCAL( I2-I, DCONJG( TEMP ), H( I, I+1 ), LDH )
            CALL ZSCAL( I-I1, TEMP, H( I1, I ), 1 )
            IF( WANTZ ) THEN
               CALL ZSCAL( NZ, TEMP, Z( ILOZ, I ), 1 )
            END IF
         END IF
*
  130 CONTINUE

... esta iteração é concluída com êxito (mesmo ao usar registros SSE para ABS ()).

Acho que os testes são definitivamente projetados para serem bem-sucedidos para o conjunto de compiladores FORTRAN populares, porque é assim que eles são executados. Prever falta / estouro é incrivelmente difícil. Pelo menos no meu caso, essas sub-rotinas são projetadas simplesmente testando-as (usando os compiladores populares) por completo e corrigindo qualquer excesso / insuficiência de fluxo que encontrarmos.

O conjunto de testes é de grande ajuda! Minha estimativa aproximada é que muito menos de 1% dos testes são afetados por este ou por problemas de estouro semelhantes (ao usar nosso compilador). Tornar os testes ainda mais robustos contra sub / overflow pode ajudar a trazer o LAPACK para mais plataformas. Nossa tentativa (falhada) acima é apenas um exemplo, que mostra claramente, que dificilmente seríamos capazes de encontrar uma solução do nosso lado, no entanto. Antes de abrir vários assuntos relacionados, gostaria de iniciar uma discussão, se há ou não interesse em tal jornada e qual seria uma boa abordagem.

Obrigado pela melhoria @hokb e @thijssteel! Devo escrever um PR com as modificações ou você está disposto a fazer isso, @hokb?

Dada a minha experiência limitada com o projeto, eu agradeceria seu esforço e a chance de usar seu RP como uma diretriz para a maconha. futuros PRs de nós ... (se estiver tudo bem?)

Olá @hokb ,

Procurei alguma especificação, mas ainda não consegui encontrar. Qualquer link também seria apreciado. Desde já, obrigado!

Não tenho certeza se algo está especificado em qualquer lugar.

Nosso compilador tem como alvo o .NET CLR. Seu JIT decide usar registradores SSE para ABS (TEMP), o que leva ao underflow no cálculo intermediário da magnitude. Ifort (como outro exemplo) usa registradores de ponto flutuante nesta situação, portanto, não underflow (por causa de seu comprimento maior: 80 bits). Estou tentando obter uma imagem clara (er) do que esperar do LAPACK em relação à precisão / intervalo de números que ele requer do compilador / processador em tempo de execução.

Declaração em negrito: se todos os cálculos forem feitos usando aritmética IEEE de 64 bits, o LAPACK deve funcionar.

O LAPACK não espera que o registro de 80 bits venha a ajudar em seu cálculo a qualquer momento. Os algoritmos são projetados com a aritmética de 64 bits em mente. Agora, como mencionado por @thijssteel , o LAPACK é testado com vários compiladores / arquiteturas, e esses compiladores / arquiteturas usam registradores de 80 bits às vezes, e podemos pensar que nossos algoritmos precisam apenas de 64 bits o tempo todo, mas eles não precisam, e eles, na verdade, exigem um de 80 bits.

Não fizemos nada sistemático em nossa jornada para ir atrás dessas questões. Em geral, ficamos felizes quando os algoritmos passam no conjunto de testes e, se houver ajuda do registrador de 80 bits, que seja.

Todos os testes de precisão dupla são projetados para exigir pelo menos registros de 64 bits? Ou eles são projetados de forma a ter sucesso para o conjunto de compiladores FORTRAN populares disponíveis hoje? (No primeiro caso, o problema acima (e outros semelhantes) pode exigir atenção. Devo registrar um problema para eles?)

Minha estimativa aproximada é que muito menos de 1% dos testes são afetados por este ou por problemas de estouro semelhantes (ao usar nosso compilador).

Oh meu. 1%? Esse é um grande número assustador.

Os testes estão testando muito em torno da região de underflow e overflow, portanto, pode-se esperar que os testes sejam muito mais prováveis ​​em termos de acionar esse problema do que o código dos usuários, mas ainda assim.

Tornar os testes ainda mais robustos contra sub / overflow pode ajudar a trazer o LAPACK para mais plataformas.

A portabilidade para mais plataformas é de fato um interesse. Outro interesse é a precisão estendida com pacote como o GMP onde, pelo que entendi, a precisão é fixada em todo o cálculo. (Então, por exemplo, você é o pensamento de 256 bits, e não há um registro de 300 bits para ajudá-lo.)

Nossa tentativa (falhada) acima é apenas um exemplo, que mostra claramente, que dificilmente seríamos capazes de encontrar uma solução do nosso lado, no entanto. Antes de abrir vários assuntos relacionados, gostaria de iniciar uma discussão, se há ou não interesse em tal jornada e qual seria uma boa abordagem.

sim. Nós estamos interessados. Só podemos fazer muito embora. E temos muito em nossos pratos. Então, talvez tomemos esse único problema de cada vez e vejamos até onde vamos.

Em qualquer caso, postar problemas no GitHub é sempre uma boa ideia. Dá consciência do problema e ajuda a reunir ajuda e ideias para resolver os problemas.

Estou feliz por seguirmos esse caminho, mas recomendo ir com calma.

Talvez, para o gfortran, devamos compilar com os sinalizadores -mfpmath=sse -msse2 para fins de teste. Acho que isso forçará todos os cálculos a serem feitos com aritmética de 64 bits. Eu não tenho certeza embora.

Dada a minha experiência limitada com o projeto, eu agradeceria seu esforço e a chance de usar seu RP como uma diretriz para a maconha. futuros PRs de nós ... (se estiver tudo bem?)

Certo! Por favor, veja # 577.

@weslleyspereira Incrível! Ainda estou verificando, se isso se aplica ao CLAHQR da mesma forma. Postarei meu resultado o mais rápido possível (amanhã)

Olá @langou !

Declaração em negrito: se todos os cálculos forem feitos usando aritmética IEEE de 64 bits, o LAPACK deve funcionar.

Agradável! Suponho que por 'trabalho' queremos dizer: quando alimentado com dados 'em uma certa faixa', não irá estourar devido a um determinado tamanho de registro?

O LAPACK não espera que o registro de 80 bits venha a ajudar em seu cálculo a qualquer momento. Os algoritmos são projetados com a aritmética de 64 bits em mente. Agora, como mencionado por @thijssteel , o LAPACK é testado com vários compiladores / arquiteturas, e esses compiladores / arquiteturas usam registradores de 80 bits às vezes, e podemos pensar que nossos algoritmos precisam apenas de 64 bits o tempo todo, mas eles não precisam, e eles, na verdade, exigem um de 80 bits.

Não fizemos nada sistemático em nossa jornada para ir atrás dessas questões. Em geral, ficamos felizes quando os algoritmos passam no conjunto de testes e, se houver ajuda do registrador de 80 bits, que seja.

Parece muito razoável!

Minha estimativa aproximada é que muito menos de 1% dos testes são afetados por este ou por problemas de estouro semelhantes (ao usar nosso compilador).

Oh meu. 1%? Esse é um grande número assustador.

Bem, provavelmente é 'muito menos' do que isso;)

A portabilidade para mais plataformas é de fato um interesse. Outro interesse é a precisão estendida com pacote como o GMP onde, pelo que entendi, a precisão é fixada em todo o cálculo. (Então, por exemplo, você é o pensamento de 256 bits, e não há um registro de 300 bits para ajudá-lo.)

Parece interessante, mas não posso comentar sobre isso, pois não tenho experiência com essas tentativas de precisão fixa.

sim. Nós estamos interessados. Só podemos fazer muito embora. E temos muito em nossos pratos. Então, talvez tomemos esse único problema de cada vez e vejamos até onde vamos.

Ainda não tenho certeza de qual seria uma boa abordagem geral. Cuidado comigo, se meu entendimento for muito ingênuo. Mas o over- / underflow não depende sempre de ambos: dados de entrada e algoritmo? Portanto, em vez de inundar o código com novos testes condicionais e um novo código para recuperação deles, podemos diminuir o 'intervalo permitido' para os dados de entrada. No entanto, não tenho o insight necessário sobre o esforço exigido para nenhuma das abordagens. Portanto, não posso julgar o que seria mais viável.

Em qualquer caso, postar problemas no GitHub é sempre uma boa ideia. Dá consciência do problema e ajuda a reunir ajuda e ideias para resolver os problemas.

Boa. Iremos registrar os problemas à medida que avançamos. Eu entendo que será um desafio encontrar uma solução sem ser capaz de reproduzir um estouro negativo. Então, que informações podemos fornecer para tornar o assunto mais claro? O caminho até o underflow de concreto ajuda? Ou seja: fornecer contagens de iteração, valores atuais de locais junto com nomes de arquivos etc.?

Estou feliz por seguirmos esse caminho, mas recomendo ir com calma.

Mesma coisa aqui! :)

Um resultado do # 577 é que o LAPACK depende do compilador FORTRAN para implementar divisão complexa razoavelmente robusta (sub / overflow) e ABS (). Será que devemos começar a manter um documento, coletando tais e outros requisitos semelhantes? Eles serão igualmente importantes e úteis para quem deseja usar o LAPACK com outros / novos compiladores, para construtores de compiladores e para transferir partes ou todos os algoritmos do LAPACK para outras linguagens?

Certo! Será bom ter essas informações bem documentadas.

Para começar, passei algum tempo rastreando (talvez) todas as divisões nos arquivos LAPACK/SRC/z*.f (COMPLEX * 16 algoritmos) do formulário

 REAL / COMPLEX   or   COMPLEX / COMPLEX

Encontrei um total de 53 arquivos. Veja o arquivo anexado: complexDivisionFound.code-search

  • Para isso, usei a expressão REGEX no Visual Studio Code:

    \ n. * / ^ 0-9 (?! DBLE) (?! REAL) (?! MIN) (?! MAX) [^ 0-9]

Talvez, para o gfortran, devamos compilar com os sinalizadores -mfpmath=sse -msse2 para fins de teste. Acho que isso forçará todos os cálculos a serem feitos com aritmética de 64 bits. Eu não tenho certeza embora.

Sim, deveria ao usar o GCC, mas este sinalizador também deve ser definido por padrão em x86-64. O trecho da documentação abaixo é para o GCC 11, mas versões muito mais antigas do GCC devem exibir o mesmo comportamento. Usando a GNU Compiler Collection (GCC): 3.19.59 x86 Options

sse

Use as instruções escalares de ponto flutuante presentes no conjunto de instruções SSE. Este conjunto de instruções é compatível com os chips Pentium III e mais recentes e, na linha AMD, com os chips Athlon-4, Athlon XP e Athlon MP. A versão anterior do conjunto de instruções SSE suporta apenas aritmética de precisão simples, portanto, a aritmética de precisão dupla e estendida ainda é feita usando 387. Uma versão posterior, presente apenas nos chips Pentium 4 e AMD x86-64, suporta aritmética de precisão dupla também.

Para o compilador x86-32, você deve usar os switches -march=cpu-type , -msse ou -msse2 para habilitar extensões SSE e tornar esta opção efetiva. Para o compilador x86-64, essas extensões são habilitadas por padrão.

O código resultante deve ser consideravelmente mais rápido na maioria dos casos e evitar os problemas de instabilidade numérica do código 387, mas pode quebrar algum código existente que espera que os temporários sejam de 80 bits.

Esta é a escolha padrão para o compilador x86-64, destinos Darwin x86-32 e a escolha padrão para destinos x86-32 com a instrução SSE2 definida quando -ffast-math está habilitado.

Exemplo:
XEIGTSTZ <zec.in - falha devido ao estouro negativo em ZLAHQR.
Etapas para reproduzir: ZGET37 -> knt == 31, ZHSEQR -> ZLAHQR -> no final da segunda etapa QR (ITS == 2) o código a seguir causa underflow (em certos registros, veja abaixo)

TEMP = H( I, I-1 )
    IF( DIMAG( TEMP ).NE.RZERO ) THEN
        RTEMP = ABS( TEMP)    ! <-- underflow on TEMP = (~1e-0173, ~1e-0173)
        IF (RTEMP .EQ. RZERO) RTEMP = CABS1(TEMP)
        H( I, I-1 ) = RTEMP
        TEMP = TEMP / RTEMP
        IF( I2.GT.I )

Nosso compilador tem como alvo o .NET CLR. Seu JIT decide usar registradores SSE para ABS (TEMP), o que leva ao underflow no cálculo intermediário da magnitude. Ifort (como outro exemplo) usa registradores de ponto flutuante nesta situação, portanto, não underflow (por causa de seu comprimento maior: 80 bits). Estou tentando obter uma imagem clara (er) do que esperar do LAPACK em relação à precisão / intervalo de números que ele requer do compilador / processador em tempo de execução.

Para recapitular:

  • Você transpilou meio milhão de linhas de Fortran77 em C #.
  • O teste do código transpilado falha ao usar o compilador just-in-time .NET.
  • O teste do código vanilla LAPACK é bem-sucedido ao usar o compilador Intel Fortran (ifort).
  • A diferença observada entre os dois casos é o uso de intermediários de 80 bits por ifort que evita o estouro negativo.

Correto?

Por padrão, o GCC gera apenas código para registradores flutuantes de 64 bits em x86-64 e, em minhas máquinas, geralmente todos os testes LAPACK passam, exceto um ou dois.

O conjunto de testes do Netlib LAPACK passa ao compilar com o GCC?

editar: resolvido https://github.com/Reference-LAPACK/lapack/pull/577#issuecomment -859496175

Talvez, para o gfortran, devamos compilar com os sinalizadores -mfpmath = sse -msse2 para fins de teste. Acho que isso forçará todos os cálculos a serem feitos com aritmética de 64 bits. Eu não tenho certeza embora.

Tentei -mfpmath=sse -msse2 com GCC 11 em MacOS e Linux: https://github.com/weslleyspereira/lapack/actions/runs/966071530. Não houve erros adicionais quando comparado ao fluxo de trabalho sem esses sinalizadores: https://github.com/Reference-LAPACK/lapack/actions/runs/945060330. Veja # 591

@hokb , você poderia reproduzir os problemas de estouro mencionados em https://github.com/Reference-LAPACK/lapack/issues/575#issuecomment -855880000 com GCC usando sinalizadores SSE? Você pode me ajudar com isso?

@weslleyspereira Nem tentei o GCC. Tudo o que tenho acesso a / uma configuração em execução é ifort no Windows. Levaria alguns dias para eu colocar o GCC funcionando via cygwin para testar (especialmente do meu quarto de hotel de férias atual ...: |) Deixe-me saber se você precisa que eu aceite este desafio!
Pelo menos e de acordo com https://godbolt.org/z/YYv5oPxe9 usar os sinalizadores não afeta o código gerado pelo gfortran. Mas é claro que apenas um teste de funcionamento dirá com certeza ...

Não uso windows, mas tenho aqui. Vou começar testando o LAPACK com ifort no meu Ubuntu e ver o que acontece. Aproveite o feriado!

Esta página foi útil?
0 / 5 - 0 avaliações