BLAS 구현은 동일한 결과를 보장합니까?


17

두 가지 다른 BLAS 구현이 주어지면 정확히 동일한 부동 소수점 계산을 수행하고 동일한 결과를 반환 할 것으로 기대할 수 있습니까? 또는 예를 들어 스칼라 곱을 하고 다른 하나는 계산하여 IEEE 부동 소수점에서 다른 결과를 얻을 수 산수?

((엑스1와이1+엑스2와이2)+엑스와이)+엑스4와이4
(엑스1와이1+엑스2와이2)+(엑스와이+엑스4와이4),

1
이 글타래 에 BLAS 품질에 대한 불만 있습니다. 페이지에서 CBLAS을 검색하십시오. 그것은 그들이 같은 결과를 줄뿐만 아니라 모든 작업에 대해 충분히 정확하지는 않다는 것을 시사합니다.
Szabolcs

답변:


15

아니요, 보장되지 않습니다. 최적화없이 NETLIB BLAS를 사용하는 경우 결과가 동일하다는 것은 대부분 사실입니다. 그러나 BLAS 및 LAPACK의 실제 사용에는 고도로 최적화 된 병렬 BLAS를 사용합니다. 병렬화는 CPU의 벡터 레지스터 내에서 병렬로만 작동하더라도 단일 항의 평가 순서와 합산 순서도 변경되도록합니다. 이제 IEEE 표준에서 누락 된 연관 속성을 따라 결과가 동일하지 않습니다. 그래서 당신이 언급 한 것은 일어날 수 있습니다.

NETLIB BLAS에서 스칼라 곱은 인수 5에 의해 풀린 for 루프 일뿐입니다.

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

각 곱셈이 DTEMP에 즉시 추가되거나 5 개의 구성 요소가 모두 먼저 요약되고 DTEMP에 추가되는 경우 컴파일러의 책임입니다. OpenBLAS에서는 아키텍처에 따라 더 복잡한 커널이 있습니다.

 __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"
...

스칼라 곱을 길이가 4 인 작은 스칼라 곱으로 나누고 합산합니다.

ATLAS, MKL, ESSL과 같은 다른 일반적인 BLAS 구현을 사용하면 각 BLAS 구현이 서로 다른 최적화를 사용하여 빠른 코드를 가져 오기 때문에이 문제는 동일하게 유지됩니다. 그러나 내가 아는 한 실제로 잘못된 결과를 초래하는 인공적인 예가 필요합니다.

BLAS 라이브러리가 동일한 결과 (비트 단위로 동일)를 리턴해야하는 경우 다음과 같은 재현 가능한 BLAS 라이브러리를 사용해야합니다.


8

짧은 대답

두 BLAS 구현이 동일한 순서로 작업을 수행하도록 작성되고 라이브러리가 동일한 컴파일러 플래그와 동일한 컴파일러를 사용하여 컴파일 된 경우 동일한 결과를 제공합니다. 부동 소수점 산술은 임의적 이지 않으므로 두 개의 동일한 구현은 동일한 결과를 제공합니다.

그러나 성능을 위해이 동작을 중단시킬 수있는 다양한 것들이 있습니다 ...

더 긴 대답

IEEE는 또한 각 작업의 동작 방식 외에도 이러한 작업이 수행되는 순서 를 지정 합니다. 그러나 "-ffast-math"와 같은 옵션을 사용하여 BLAS 구현을 컴파일하는 경우 컴파일러는 IEEE 부동 소수점에서는 정확한 산술에서는 정확하지만 "올바르지 않은"변환을 수행 할 수 있습니다. 정식 예제는 지적했듯이 부동 소수점 추가의 비 연관성입니다. 보다 적극적인 최적화 설정을 사용하면 연관성이 가정되며 프로세서는 작업 순서를 다시 정렬하여 최대한 많은 병렬 처리를 수행합니다.

+


3
"부동 소수점 산술은 임의적이지 않습니다" . 이것이 명시 적으로 언급되어야하는 것이 슬픈 일이지만 너무 많은 사람들이 그렇게 생각하는 것 같습니다.
pipe

글쎄, 예측할 수없고 임의적 인 모습은 꽤 비슷하며, 많은 인트로 프로그래밍 클래스는 "평등을 비교하지 않습니다"라고 말하는데, 이는 정수와 같은 방식으로 정확한 값에 의존 할 수 없다는 인상을줍니다.
Tyler Olsen

@TylerOlsen 이것은 질문과 관련이 없으며, 이것이 그러한 클래스가 그런 말을하는 이유는 아니지만 IIRC는 평등에 의존 할 수없는 컴파일러 버그 클래스가있었습니다. 예를 들어, 때때로 실패 할 if (x == 0) assert(x == 0) 있으며, 특정 관점에서는 무작위만큼 좋습니다.
Kirill

@Kirill 죄송합니다. 많은 사람들이 부동 소수점의 작동 방식을 실제로 배우지 않는다는 점을 간단히 지적하려고했습니다. 역사적 요점에 관해서는, 그것은 끔찍한 일이지만, 게임에 들어가기 전에 해결되었을 것입니다.
Tyler Olsen

@TylerOlsen 죄송합니다. 내 예가 잘못되었습니다. if (x != 0) assert(x != 0)확장 정밀도 산술 때문에 이어야합니다 .
Kirill

4

일반적으로 아닙니다. 연관성을 제쳐두고 컴파일러 플래그 선택 (예 : SIMD 명령어 활성화, fused multiply add 사용 등) 또는 하드웨어 (예 : 확장 정밀도 사용 여부 )는 다른 결과를 생성 할 수 있습니다.

재현 가능한 BLAS 구현을 얻기위한 몇 가지 노력이 있습니다. 자세한 내용은 ReproBLASExBLAS 를 참조하십시오.


1
최신 버전의 Intel MKL BLAS에있는 CNR (Conditional Numerical Reproducibility) 기능도 참조하십시오. 이 수준의 재현성을 얻으면 일반적으로 계산 속도가 느려지고 계산 속도가 크게 느려질 수 있습니다!
Brian Borchers
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.