Ketika perhitungan terbatas bandwidth memori dilakukan dalam lingkungan memori bersama (mis. Berulir melalui OpenMP, Pthreads, atau TBB), ada dilema tentang bagaimana memastikan bahwa memori didistribusikan dengan benar di seluruh memori fisik , sehingga masing-masing thread kebanyakan mengakses memori pada sebuah bus memori "lokal". Meskipun antarmuka tidak portabel, sebagian besar sistem operasi memiliki cara untuk mengatur afinitas utas (mis. pthread_setaffinity_np()
Pada banyak sistem POSIX, sched_setaffinity()
di Linux, SetThreadAffinityMask()
pada Windows). Ada juga perpustakaan seperti hwloc untuk menentukan hirarki memori, tetapi sayangnya, sebagian besar sistem operasi belum menyediakan cara untuk mengatur kebijakan memori NUMA. Linux adalah pengecualian penting, dengan libnumamemungkinkan aplikasi untuk memanipulasi kebijakan memori dan migrasi halaman pada granularity halaman (dalam arus utama sejak 2004, dengan demikian tersedia secara luas). Sistem operasi lain mengharapkan pengguna untuk mematuhi kebijakan "sentuhan pertama" implisit.
Bekerja dengan kebijakan "sentuhan pertama" berarti bahwa penelepon harus membuat dan mendistribusikan utas dengan afinitas apa pun yang mereka rencanakan untuk digunakan nanti ketika pertama kali menulis ke memori yang baru dialokasikan. (Sangat sedikit sistem yang dikonfigurasikan sehingga malloc()
benar - benar menemukan halaman, itu hanya menjanjikan untuk menemukan mereka ketika mereka benar-benar rusak, mungkin oleh utas berbeda.) Ini menyiratkan bahwa alokasi menggunakan calloc()
atau segera menginisialisasi memori setelah alokasi menggunakan memset()
berbahaya karena akan cenderung untuk kesalahan semua memori ke bus memori inti menjalankan utas pengalokasian, yang mengarah ke bandwidth memori terburuk saat memori diakses dari banyak utas. Hal yang sama berlaku untuk new
operator C ++ yang bersikeras menginisialisasi banyak alokasi baru (misstd::complex
). Beberapa pengamatan tentang lingkungan ini:
- Alokasi dapat dibuat "thread threaded", tetapi sekarang alokasi menjadi dicampur ke dalam model threading yang tidak diinginkan untuk perpustakaan yang mungkin harus berinteraksi dengan klien menggunakan model threading yang berbeda (mungkin masing-masing dengan kumpulan thread mereka sendiri).
- RAII dianggap sebagai bagian penting dari C ++ idiomatik, tetapi tampaknya berbahaya secara aktif untuk kinerja memori di lingkungan NUMA. Penempatan
new
dapat digunakan dengan memori yang dialokasikan melaluimalloc()
atau dari rutinitaslibnuma
, tetapi ini mengubah proses alokasi (yang saya percaya perlu). - EDIT: Pernyataan saya sebelumnya tentang operator
new
tidak benar, itu dapat mendukung beberapa argumen, lihat balasan Chetan. Saya percaya masih ada kekhawatiran mendapatkan perpustakaan atau wadah STL untuk menggunakan afinitas yang ditentukan. Beberapa bidang dapat dikemas dan mungkin tidak nyaman untuk memastikan bahwa, misalnya,std::vector
realokasi dengan manajer konteks yang benar aktif. - Setiap utas dapat mengalokasikan dan kesalahan memori pribadinya sendiri, tetapi kemudian mengindeks ke daerah tetangga lebih rumit. (Pertimbangkan produk matriks-vektor yang jarang Anda dengan partisi baris dari matriks dan vektor; pengindeksan bagian yang tidak dimiliki memerlukan struktur data yang lebih rumit ketika tidak bersebelahan dalam memori virtual.)
Apakah ada solusi untuk alokasi / inisialisasi NUMA yang dianggap idiomatis? Sudahkah saya meninggalkan gotcha penting lainnya?
(Saya tidak bermaksud untuk C saya ++ contoh menyiratkan penekanan pada bahasa yang, namun C ++ bahasa mengkodekan beberapa keputusan tentang manajemen memori yang bahasa seperti C tidak, sehingga ada cenderung lebih tahan ketika menunjukkan bahwa C ++ programmer melakukan hal- hal-hal berbeda.)