결정적 모델 실행으로 예측할 수없는 작고 예측 가능한 결과


10

나는 C로 작성된 크기 조정 가능한 모델 (~ 5000 줄)을 가지고 있습니다. 이것은 임의의 숫자 생성이없는 직렬 프로그램입니다. FFT를 사용하는 함수에 FFTW 라이브러리를 사용합니다. FFTW 구현의 세부 사항을 알지 못하지만 그 함수가 결정적이라고 가정합니다 (오류가있는 경우 수정하십시오).

내가 이해할 수없는 문제는 동일한 컴퓨터 (동일한 컴파일러, 동일한 라이브러리)에서 동일한 실행 결과에 약간의 차이가 있다는 것입니다.

I는 배정도 변수를 사용하고, 변수의 결과 출력 value예를 들어, I는 발행 : fprintf(outFID, "%.15e\n", value);또는
fwrite(&value, 1, sizeof(double), outFID);

그리고 나는 지속적으로 다음과 같은 차이점을 얻을 것입니다 :
2.07843469652206 4 e-16 대 2.07843469652206 3 e-16

나는 이것이 왜 그런지 알아 내려고 많은 시간을 보냈습니다. 나는 처음에 내 메모리 칩 중 하나가 잘못되었다고 생각하고 주문하고 교체했지만 아무 소용이 없었습니다. 그 후 동료의 Linux 컴퓨터에서 코드를 실행하려고 시도했지만 동일한 특성의 차이점을 얻었습니다.

이 문제의 원인은 무엇입니까? 지금은 작은 문제이지만, 그것이 심각한 문제인 "빙산의 일각"인지 궁금합니다.

수치 모델을 사용하는 사람 이이 문제를 겪을 수 있으므로 StackOverflow 대신 여기에 게시 할 것이라고 생각했습니다. 누구든지 이것에 대해 밝힐 수 있다면 나는 많은 의무를 져야 할 것입니다.

후속 의견 :
Christian Clason과 Vikram : 우선, 내 질문에 관심을 가져 주셔서 감사합니다. 1. 반올림 오류는 정확도를 제한하며, 2. 다른 코드 (예 : 무해한 인쇄 문 소개)는 기계 엡실론까지 결과에 영향을 줄 수 있습니다. 나는 효과 fwritefprintf기능을 비교하지 않는다는 것을 분명히해야 합니다. 하나 또는 다른 것을 사용하고 있습니다. 특히 동일한 실행 파일이 두 실행에 모두 사용됩니다. fprintfOR 사용 여부에 관계없이 문제가 발생한다고 간단히 말하고 있습니다 fwrite.

따라서 코드 경로 (및 실행 파일)는 동일하며 하드웨어는 동일합니다. 이러한 모든 외부 요인들이 일정하게 유지되면서 무작위성은 어디에서 오는가? 결함이있는 메모리가 비트를 올바르게 유지하지 않아 비트 플립이 발생했다고 의심했기 때문에 메모리 칩을 교체 한 이유는 여기에서 문제가되지 않는 것으로 확인되었습니다. 내 프로그램은 한 번의 실행으로 수천 배의 배정도 숫자를 출력하며 임의의 비트 플립을 갖는 임의의 소수가 항상 있습니다.

기독교 Clason 최초의 코멘트에 후속 : 왜 기계 정밀도 내에서 0과 동일? 더블에 대한 가장 작은 양수는 2.22e-308이므로 0과 같지 않아야합니까? 내 프로그램은 10 ^ -16 범위 (1e-15에서 8e-17 범위)에서 수천 개의 값을 출력하며 연구 프로젝트에서 의미있는 변화를 보았으므로 우리는 비의 미적이지 않은 것을 보지 않았기를 바랍니다. 번호.21016

Followup # 2 :
이것은 주석에서 파생 된 토론을 돕기 위해 모델에 의해 출력 된 시계열의 도표입니다. 여기에 이미지 설명을 입력하십시오


21016

기계가 기계 정밀도보다 정확하지 않은 이유를 묻습니다. en.wikipedia.org/wiki/Machine_epsilon
Vikram

1
부동 소수점 산술에 대한 코드 경로의 미묘한 영향에 대한 관련 예제는 inf.ethz.ch/personal/gander/Heisenberg/paper.html 을 참조하십시오 . 그리고 물론, ece.uwaterloo.ca/~dwharder/NumericalAnalysis/02Numerics/Double/…
Christian Clason

1
1016

2
1

답변:


9

이러한 종류의 차이를 유발할 수있는 본질적으로 비 결정적인 현대 컴퓨팅 시스템의 측면이 있습니다. 솔루션의 필요한 정확도와 비교하여 차이가 매우 작은 한, 이것에 대해 걱정할 이유가 없습니다.

내 경험에 따라 잘못 될 수있는 예. 두 벡터 x와 y의 내적을 계산하는 문제를 고려하십시오.

=나는=1엑스나는와이나는

엑스나는와이나는

예를 들어 두 벡터의 곱을 먼저 다음과 같이 계산할 수 있습니다.

=((엑스1와이1)+(엑스2와이2))+(엑스와이)

그런 다음

=(엑스1와이1)+((엑스2와이2)+(엑스와이))

어떻게 이런 일이 일어날 수 있습니까? 두 가지 가능성이 있습니다.

  1. 병렬 코어에서의 다중 스레드 계산. 최신 컴퓨터에는 일반적으로 병렬로 작동 할 수있는 2, 4, 8 개 이상의 프로세서 코어가 있습니다. 코드가 병렬 스레드를 사용하여 여러 프로세서에서 내적을 계산하는 경우 시스템에 대한 임의의 섭동 (예 : 사용자가 마우스를 움직이면 프로세서 코어 중 하나가 내적으로 돌아 가기 전에 마우스 움직임을 처리해야 함) 추가 순서가 변경됩니다.

  2. 데이터 및 벡터 명령어의 정렬. 최신 인텔 프로세서에는 한 번에 부동 소수점 숫자에 대해 작동 할 수있는 특수 명령 세트가 있습니다. 이 벡터 명령어는 데이터가 16 바이트 경계에 정렬 된 경우 가장 잘 작동합니다. 일반적으로 닷 프로덕트 루프는 데이터를 16 바이트의 섹션으로 나눕니다 (한 번에 4 개의 플로트). 코드를 두 번째로 다시 실행하면 데이터가 16 바이트 메모리 블록과 다르게 정렬되어 추가됩니다. 다른 순서로 수행되어 다른 답변을 얻습니다.

코드를 단일 스레드로 실행하고 모든 병렬 처리를 비활성화하여 포인트 1을 지정할 수 있습니다. 메모리 블록을 정렬하기 위해 메모리 할당을 요구하여 포인트 2를 지정할 수 있습니다 (일반적으로 -align과 같은 스위치로 코드를 컴파일하여이 작업을 수행합니다). 코드가 여전히 다른 결과를 제공하는 경우 다른 가능성이 있습니다. 에서.

인텔 의이 문서 에서는 인텔 수학 커널 라이브러리를 사용하여 결과를 재현 할 수없는 문제에 대해 설명합니다. 인텔 컴파일러와 함께 사용할 컴파일러 스위치에 대해 설명하는 인텔의 다른 문서 입니다.


코드가 단일 스레드로 실행되고 있다고 생각합니다. 코드를 잘 알고있을지라도 멀티 스레드 방식으로 실행되는 서브 루틴 (예 : BLAS 루틴)을 호출한다고해도 놀라지 않을 것입니다. 사용중인 라이브러리를 정확하게 확인해야합니다. 시스템 모니터링 도구를 사용하여 CPU 사용량을 확인할 수도 있습니다.
Brian Borchers

1
또는 명시된 바와 같이 FFTW 도서관 ...
Christian Clason

@BrianBorchers, 감사합니다. 부동 소수점 추가의 비 연관적인 특성에서 나오는 임의성 예는 깨달음입니다. Christian Clason은 숫자의 크기를 고려할 때 내 모델 출력이 의미가 있는지에 대한 두 번째 문제를 제기했습니다.
boxofchalk1

2

언급 된 FFTW 라이브러리는 비 결정적 모드에서 실행될 수 있습니다.

FFTW_MEASURE 또는 FFTW_PATIENT 모드를 사용하는 경우 프로그램은 런타임시 어떤 매개 변수 값이 가장 빠르게 작동하는지 확인한 다음 전체 프로그램에서 해당 매개 변수를 사용합니다. 런타임은 분명히 약간 변동하기 때문에 매개 변수가 다르고 푸리에 변환의 결과는 결정적이지 않습니다. 결정적인 FFTW를 원하면 FFTW_ESTIMATE 모드를 사용하십시오.


1

멀티 코어 / 멀티 스레드 처리 시나리오로 인해 표현식 용어 평가 순서 변경이 발생할 수 있다는 것은 사실이지만, 비록 오랜 시간이더라도 하드웨어 설계 결함이있을 수 있다는 것을 잊지 마십시오. 펜티엄 FDIV 문제를 기억하십니까? ( https://en.wikipedia.org/wiki/Pentium_FDIV_bug 참조 . 얼마 전에 저는 PC 기반 아날로그 회로 시뮬레이션 소프트웨어를 작업했습니다. 우리의 방법론 중 일부는 회귀 테스트 스위트를 개발하는 것과 관련이 있으며 소프트웨어의 야간 빌드에 대해 실행됩니다. 우리가 개발 한 많은 모델에서 반복적 인 방법 (예 : Newton-Raphson ( https://en.wikipedia.org/wiki/Newton%27s_method)) 및 Runge-Kutta)는 시뮬레이션 알고리즘에 광범위하게 사용되었습니다. 아날로그 장치의 경우 전압, 전류 등과 같은 내부 아티팩트가 매우 작은 숫자 값을 갖는 경우가 종종 있습니다. 시뮬레이션 프로세스의 일부로 이러한 값은 (시뮬레이션 된) 시간에 따라 점차적으로 변합니다. 이러한 변화의 규모는 매우 작을 수 있으며, 우리가 자주 관찰 한 것은 그러한 델타 값에 대한 후속 FPU 동작이 FPU 정밀도의 "노이즈"임계 값과 경계를 이룬다는 것입니다 (64 비트 플로팅에는 53 비트 가수, IIRC가 있음). 사실, 우리는 매일 "PrintF"로깅 코드를 모델에 도입하여 디버깅 (아아, 좋은 시절!), 실제로 산발적 인 결과를 매일 보장해야했습니다! 그래서, 무엇 이 모든 것이 의미 하는가? 이러한 상황에서 차이점을 예상해야하며, 가장 좋은 방법은 무시할시기 / 방법을 결정하는 방법 (크기, 빈도, 추세 등)을 정의하고 구현하는 것입니다.


통찰력을 가져 주셔서 감사합니다. 어떤 "기본 내부 현상"이 그러한 "내부 인공물"을 유발하는지에 대한 아이디어가 있습니까? 전자기 간섭이 하나 일 수 있다고 생각했지만, 중요한 비트도 영향을받지 않습니까?
boxofchalk1 1

1

비동기 작업에서 부동 소수점 반올림이 문제가 될 수 있지만 더 평범한 것으로 생각됩니다. 결정 론적 코드에 임의성을 추가하는 초기화되지 않은 변수 사용. 디버그 모드에서 실행할 때 모든 변수가 선언시 0으로 초기화되기 때문에 개발자가 종종 간과하는 일반적인 문제입니다. 디버그 모드에서 실행되지 않는 경우 변수에 할당 된 메모리는 할당 전에 메모리에 있던 값을 갖습니다. 최적화로 할당시 메모리가 0으로 설정되지 않습니다. 코드에서 이런 일이 발생하면 라이브러리 코드에서는 쉽게 해결할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.