Kecil, hasil tak terduga dalam menjalankan model deterministik

10

Saya memiliki model yang cukup besar (~ 5000 baris) ditulis dalam C. Ini adalah program serial, tanpa generasi nomor acak di mana pun. Itu menggunakan perpustakaan FFTW untuk fungsi menggunakan FFT - Saya tidak tahu detail implementasi FFTW, tapi saya menganggap fungsi di dalamnya juga deterministik (koreksi saya jika saya salah).

Masalah yang saya tidak mengerti adalah bahwa saya mendapatkan perbedaan kecil dalam hasil untuk menjalankan identik pada mesin yang sama (kompiler yang sama, perpustakaan yang sama).

Saya menggunakan variabel presisi ganda, dan untuk menampilkan hasilnya dalam variabel valuemisalnya, saya menerbitkan: fprintf(outFID, "%.15e\n", value);atau
fwrite(&value, 1, sizeof(double), outFID);

Dan saya akan selalu mendapatkan perbedaan seperti:
2.07843469652206 4 e-16 vs 2.07843469652206 3 e-16

Saya telah menghabiskan banyak waktu untuk mencari tahu mengapa ini terjadi. Awalnya saya pikir salah satu chip memori saya sudah rusak, dan saya sudah memesan dan menggantinya, tetapi tidak berhasil. Saya kemudian juga mencoba menjalankan kode saya pada mesin Linux seorang kolega, dan saya mendapatkan perbedaan dari sifat yang sama.

Apa yang menyebabkan ini? Ini adalah masalah kecil sekarang, tapi saya ingin tahu apakah itu adalah "puncak gunung es" (masalah serius).

Saya pikir saya akan memposting di sini daripada StackOverflow jika seseorang bekerja dengan model numerik mungkin menemukan masalah ini. Jika ada yang bisa menjelaskan ini, saya akan sangat berterima kasih.

Tindak lanjut komentar:
Christian Clason dan Vikram: pertama, terima kasih atas perhatian Anda pada pertanyaan saya. Artikel yang Anda tautkan menyarankan bahwa: 1. kesalahan pembulatan membatasi keakuratan, dan 2. kode yang berbeda (seperti memperkenalkan pernyataan cetak yang tampaknya tidak berbahaya) dapat memengaruhi hasil hingga epsilon mesin. Saya harus mengklarifikasi bahwa saya tidak membandingkan efek fwritedan fprintffungsi. Saya menggunakan satu ATAU yang lainnya. Secara khusus, executable yang sama digunakan untuk kedua run. Saya hanya menyatakan masalah terjadi apakah saya menggunakan fprintfOR fwrite.

Jadi jalur kode (dan dapat dieksekusi) adalah sama, dan perangkat kerasnya sama. Dengan semua faktor eksternal ini tetap konstan, dari mana asal keacakan, secara fundamental? Saya menduga bit flip terjadi karena memori yang salah tidak mempertahankan sedikit dengan benar, itulah sebabnya saya mengganti chip memori, tetapi itu tampaknya tidak menjadi masalah di sini, saya memverifikasi dan Anda menunjukkan. Program saya menghasilkan ribuan angka presisi ganda ini dalam sekali jalan, dan selalu ada beberapa acak yang memiliki bit bit acak.

Tindak lanjuti komentar pertama Christian Clason: Mengapa sama dengan 0 dalam presisi mesin? Angka positif terkecil untuk ganda adalah 2.22e-308, jadi bukankah seharusnya sama dengan 0? Program saya menghasilkan ribuan nilai dalam kisaran 10 ^ -16 (mulai dari 1e-15 hingga 8e-17) dan kami telah melihat variasi yang berarti dalam proyek penelitian kami, jadi saya harap kami tidak melihat omong kosong angka.21016

Tindak lanjut # 2 :
Ini adalah plot output seri waktu oleh model, untuk membantu dalam diskusi cabang di komentar. masukkan deskripsi gambar di sini

boxofchalk1
sumber
Selamat datang di SciComp.SE! Tahukah Anda bahwa angka floating point memiliki keakuratan terbatas - khususnya, bahwa dalam presisi ganda, sama dengan nol hingga ke presisi mesin? Jadi perbedaan yang Anda laporkan tidak benar-benar bermakna, dan kemungkinan disebabkan oleh sedikit perbedaan dalam implementasi kedua fungsi yang Anda beri nama yang mengarah ke kode mesin yang sedikit berbeda. 21016
Christian Clason
Anda bertanya mengapa mesin Anda tidak lebih akurat daripada presisi mesin. en.wikipedia.org/wiki/Machine_epsilon
Vikram
1
Lihat inf.ethz.ch/personal/gander/Heisenberg/paper.html untuk contoh terkait dari pengaruh halus jalur kode pada aritmatika floating point. Dan, tentu saja, ece.uwaterloo.ca/~dwharder/NumericalAnalysis/02Numerics/Double/…
Christian Clason
1
Mungkin penskalaan masalah Anda sedemikian rupa sehingga jawaban yang benar berada di urutan . Dalam hal apa pun, Anda harus memahami dengan akurasi relatif dari solusi Anda. 1016
Brian Borchers
2
@ boxofchalk1 Mereka jelas tidak terlihat seperti suara; seperti yang dikatakan Brian, jika semua data Anda dari urutan besarnya, Anda mungkin baik-baik saja (sekali lagi, ini tentang akurasi relatif ). Untuk memastikan, Anda dapat mengubah skala masalah Anda menjadi urutan , atau menjalankan kembali kode Anda dengan presisi yang lebih tinggi (lihat fftw.org/doc/Precision.html ). 1
Christian Clason

Jawaban:

9

Ada aspek dari sistem komputasi modern yang secara inheren non-deterministik yang dapat menyebabkan perbedaan-perbedaan semacam ini. Selama perbedaannya sangat kecil dibandingkan dengan keakuratan yang diperlukan dari solusi Anda, mungkin tidak ada alasan untuk khawatir tentang ini.

Contoh dari apa yang bisa salah berdasarkan pengalaman saya sendiri. Pertimbangkan masalah penghitungan produk titik dari dua vektor x dan y.

d=i=1nxiyi

Menghitung produk titik ini memerlukan perhitungan produk dan kemudian menambahkan hasilnya. Perkalian floating point harus menghasilkan hasil yang persis sama setiap waktu. Jika penambahan floating point dihitung dalam urutan yang sama setiap kali maka jumlahnya harus sama. Namun, karena penambahan titik apung tidak asosiatif, Anda mungkin mendapatkan hasil yang berbeda jika produk dari dua vektor dihitung sedemikian rupa sehingga penambahan dilakukan dalam urutan yang berbeda. xiyi

Misalnya, Anda dapat menghitung produk dari dua vektor pertama sebagai

d=((x1y1)+(x2y2))+(x3y3)

dan kemudian sebagai

d=(x1y1)+((x2y2)+(x3y3)) .

Bagaimana ini bisa terjadi? Berikut ini dua kemungkinan.

  1. Komputasi multithreaded pada core paralel. Komputer modern biasanya memiliki 2, 4, 8, atau bahkan lebih banyak core prosesor yang dapat bekerja secara paralel. Jika kode Anda menggunakan utas paralel untuk menghitung produk titik pada banyak prosesor, maka segala gangguan sistem secara acak (mis. Pengguna menggerakkan tetikusnya dan salah satu inti prosesor harus memproses gerakan tetikus itu sebelum kembali ke produk dot) dapat menghasilkan perubahan dalam urutan penambahan.

  2. Penyelarasan instruksi data dan vektor. Prosesor Intel modern memiliki serangkaian instruksi khusus yang dapat beroperasi pada (misalnya) untuk nomor floating point pada suatu waktu. Instruksi vektor ini bekerja paling baik jika data diselaraskan pada batas 16 byte. Biasanya, loop produk titik akan memecah data menjadi beberapa bagian dari 16 byte (4 mengapung sekaligus.) Jika Anda menjalankan kembali kode untuk kedua kalinya data mungkin akan disejajarkan secara berbeda dengan blok memori 16 byte sehingga penambahannya adalah dilakukan dalam urutan yang berbeda, menghasilkan jawaban yang berbeda.

Anda dapat mengatasi titik 1 dengan menjalankan kode Anda sebagai utas tunggal dan menonaktifkan semua pemrosesan paralel. Anda dapat mengatasi titik 2 dengan memerlukan alokasi memori untuk menyelaraskan blok memori (biasanya Anda akan melakukan ini dengan mengkompilasi kode dengan switch seperti -align.) Jika kode Anda masih memberikan hasil yang bervariasi maka ada kemungkinan lain untuk melihat di.

Ini dokumentasi dari Intel membahas isu-isu yang dapat menyebabkan non reproduktifitas hasil dengan Intel Math Kernel Perpustakaan. Dokumen lain dari Intel yang membahas sakelar kompiler untuk digunakan dengan kompiler Intel.

Brian Borchers
sumber
Saya melihat bahwa Anda berpikir kode Anda berjalan dengan utas tunggal. Meskipun Anda mungkin tahu kode Anda dengan baik, saya tidak akan terkejut jika Anda memanggil subrutin (misalnya rutin BLAS) yang menjalankan multithreaded. Anda harus memeriksa untuk melihat perpustakaan apa yang Anda gunakan. Anda juga dapat menggunakan alat pemantauan sistem untuk melihat penggunaan CPU Anda.
Brian Borchers
1
atau, sebagaimana dinyatakan, perpustakaan FFTW ...
Christian Clason
@BrianBorchers, terima kasih. Contoh keacakan datang dari sifat non-asosiatif penambahan floating point mencerahkan. Christian Clason mengemukakan masalah sekunder tentang apakah output model saya bermakna, mengingat besarnya angka - itu bisa menjadi masalah besar jika dia benar (dan saya memahaminya dengan benar), jadi saya mencari tahu sekarang.
boxofchalk1
2

Pustaka FFTW yang disebutkan mungkin berjalan dalam mode non-deterministik.

Jika Anda menggunakan mode FFTW_MEASURE atau FFTW_PATIENT, program akan memeriksa saat runtime, nilai parameter mana yang bekerja paling cepat dan kemudian akan menggunakan parameter tersebut di seluruh program. Karena run time akan sedikit berfluktuasi, parameternya akan berbeda dan hasil dari transformasi Fourier akan menjadi non-deterministik. Jika Anda ingin FFTW deterministik, gunakan mode FFTW_ESTIMATE.

eimrek
sumber
1

Meskipun benar bahwa perubahan urutan evaluasi istilah ekspresi mungkin sangat baik terjadi karena skenario pemrosesan multi-inti / multi-utas, jangan lupa bahwa mungkin ada (walaupun ini merupakan pukulan panjang) semacam cacat desain perangkat keras di tempat kerja. Ingat masalah Pentium FDIV? (Lihat https://en.wikipedia.org/wiki/Pentium_FDIV_bug ). Beberapa waktu lalu, saya mengerjakan perangkat lunak simulasi sirkuit analog berbasis pc. Bagian dari metodologi kami melibatkan pengembangan suite uji-regresi, yang akan kami jalankan terhadap perangkat lunak malam hari. Dengan banyak model yang kami kembangkan, metode berulang (mis. Newton-Raphson ( https://en.wikipedia.org/wiki/Newton%27s_method)) dan Runge-Kutta) digunakan secara luas dalam algoritma simulasi. Dengan perangkat analog, sering kali artefak internal, seperti tegangan, arus, dll., Terjadi memiliki nilai numerik yang sangat kecil. Nilai-nilai ini, sebagai bagian dari proses simulasi, secara bertahap bervariasi dari waktu (simulasi). Besarnya perubahan ini mungkin sangat kecil, dan yang sering kami amati adalah bahwa operasi FPU selanjutnya pada nilai delta seperti itu berbatasan dengan ambang "noise" dari presisi FPU (mengambang 64-bit memiliki mantissa 53-bit, IIRC). Itu, ditambah dengan fakta bahwa kami sering harus memperkenalkan kode pencatatan "PrintF" ke dalam model untuk memungkinkan debugging (ah, hari-hari yang baik!), Secara praktis menjamin hasil sporadis, setiap hari! Terus' Apakah semua ini berarti? Anda harus mengharapkan untuk melihat perbedaan dalam keadaan seperti itu, dan hal terbaik untuk dilakukan adalah menentukan dan menerapkan cara untuk memutuskan (besarnya, frekuensi, tren dll) kapan / bagaimana mengabaikannya.

Jim
sumber
Terima kasih, Jim atas wawasannya. Adakah gagasan tentang fenomena mendasar apa yang akan menyebabkan "artefak internal" seperti itu? Saya pikir interferensi elektromagnetik mungkin satu, tetapi bit yang signifikan akan terpengaruh juga, bukan?
boxofchalk1
1

Sementara pembulatan floating point dari operasi async mungkin menjadi masalah, saya menduga itu adalah sesuatu yang lebih dangkal. Penggunaan variabel tidak diinisialisasi yang menambahkan keacakan ke kode deterministik Anda yang lain. Ini adalah masalah umum yang sering diabaikan oleh pengembang karena ketika Anda menjalankan dalam mode debug semua variabel diinisialisasi ke 0 pada deklarasi. Saat tidak berjalan dalam mode debug, memori yang ditetapkan ke variabel memiliki nilai berapapun yang dimiliki memori sebelum penetapan. Memori tidak memusatkan perhatian pada tugas sebagai optimasi. Jika ini terjadi dalam kode Anda, itu akan mudah untuk diperbaiki, apalagi di kode perpustakaan.

brent.payne
sumber