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 value
misalnya, 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 fwrite
dan fprintf
fungsi. Saya menggunakan satu ATAU yang lainnya. Secara khusus, executable yang sama digunakan untuk kedua run. Saya hanya menyatakan masalah terjadi apakah saya menggunakan fprintf
OR 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.
Tindak lanjut # 2 :
Ini adalah plot output seri waktu oleh model, untuk membantu dalam diskusi cabang di komentar.
sumber
Jawaban:
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.
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
dan kemudian sebagai
Bagaimana ini bisa terjadi? Berikut ini dua kemungkinan.
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.
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.
sumber
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.
sumber
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.
sumber
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.
sumber