Pertanyaan Kapan penghalang memori kompiler-only (seperti std :: atomic_signal_fence) berguna?


Gagasan a pagar kompiler sering muncul ketika saya membaca tentang model memori, hambatan, pemesanan, atom, dll, tetapi biasanya itu dalam konteks juga dipasangkan dengan Pagar CPU, seperti yang diharapkan.

Kadang-kadang, bagaimanapun, saya membaca tentang konstruksi pagar yang hanya berlaku untuk kompilator. Contoh dari ini adalah C ++ 11 std::atomic_signal_fence fungsi, yang menyatakan pada cppreference.com:

std :: atomic_signal_fence setara dengan std :: atomic_thread_fence, kecuali tidak ada CPU   instruksi untuk pemesanan memori dikeluarkan. Hanya penataan ulang dari   instruksi oleh kompilator ditekan sebagai perintah pesanan.

Saya memiliki lima pertanyaan yang terkait dengan topik ini:

  1. Seperti yang tersirat dari namanya std::atomic_signal_fence, adalah interupsi asinkron (seperti ulir yang digantikan oleh kernel untuk mengeksekusi penangan sinyal) hanya kasus di mana a hanya-compiler pagar berguna?

  2. Apakah kegunaannya berlaku untuk semua arsitektur, termasuk sangat teratur yang seperti x86?

  3. Bisakah a spesifik contoh diberikan untuk menunjukkan kegunaan dari suatu hanya-compiler pagar?

  4. Ketika menggunakan std::atomic_signal_fence, apakah ada perbedaan antara menggunakan acq_rel dan seq_cst pemesanan? (Saya berharap tidak ada bedanya.)

  5. Pertanyaan ini mungkin tercakup oleh pertanyaan pertama, tapi saya cukup penasaran untuk bertanya secara khusus tentang hal itu: Apakah itu pernah diperlukan untuk menggunakan pagar dengan thread_local mengakses? (Jika itu akan terjadi, saya harapkan hanya-compiler pagar seperti atomic_signal_fence menjadi alat pilihan.)

Terima kasih.


32
2017-08-26 17:07


asal


Jawaban:


Untuk menjawab semua 5 pertanyaan:


1) Pagar compiler (dengan sendirinya, tanpa pagar CPU) hanya berguna dalam dua situasi:

  • Memaksa kendala urutan memori  antara satu thread dan handler interrupt asynchronous terikat ke thread yang sama (seperti pengatur sinyal).

  • Memaksa kendala urutan memori  antara beberapa utas ketika dijamin bahwa setiap utas akan dijalankan pada inti CPU yang sama. Dengan kata lain, aplikasi hanya akan berjalan inti tunggal sistem, atau aplikasi mengambil tindakan khusus (melalui afinitas prosesor) untuk memastikan bahwa setiap utas yang berbagi data terikat ke inti yang sama.


2) Model memori dari arsitektur yang mendasari, apakah itu sangat-atau lemah-memerintahkan, tidak memiliki pengaruh apakah pagar kompiler diperlukan dalam situasi.


3) Ini dia pseudo-code yang menunjukkan penggunaan pagar kompilator, dengan sendirinya, untuk menyinkronkan akses memori antara sebuah thread dan handler sinyal async terikat ke thread yang sama:

void async_signal_handler()
{
    if ( is_shared_data_initialized )
    {
        compiler_only_memory_barrier(memory_order::acquire);
        ... use shared_data ...
    }
}

void main()
{
// initialize shared_data ...
    shared_data->foo = ...
    shared_data->bar = ...
    shared_data->baz = ...
// shared_data is now fully initialized and ready to use
    compiler_only_memory_barrier(memory_order::release);
    is_shared_data_initialized = true;
}

Catatan penting: Contoh ini mengasumsikan itu async_signal_handler terikat ke utas yang sama yang menginisialisasi shared_datadan mengatur is_initialized flag, yang berarti aplikasi ini single-threaded, atau memasang masker sinyal thread yang sesuai. Jika tidak, pagar compiler tidak akan memadai, dan a Pagar CPU juga dibutuhkan.


4) Mereka harus sama.  acq_rel dan seq_cst keduanya harus menghasilkan pagar compiler penuh (bidirectional), tanpa ada instruksi CPU yang terkait dengan pagar. Konsep "konsistensi sekuensial" hanya berperan ketika beberapa core dan thread terlibat, dan atomic_signal_fence hanya berhubungan dengan satu utas eksekusi.


5) Tidak. (Kecuali tentu saja, data thread-local diakses dari penangan sinyal asynchronous yang mungkin diperlukan pagar compiler.) Jika tidak, pagar tidak boleh diperlukan dengan data thread-local karena compiler (dan CPU) hanya diperbolehkan untuk mengatur ulang akses memori dengan cara yang tidak mengubah perilaku yang dapat diamati dari program sehubungan dengan itu poin urutan dari perspektif single-threaded. Dan orang dapat secara logis memikirkan statika ulangan-lokal dalam program multi-berulir agar sama dengan statika global dalam program single-threaded. Dalam kedua kasus, data hanya dapat diakses dari satu utas, yang mencegah perlombaan data terjadi.


21
2017-08-27 00:04



Sebenarnya ada beberapa idiom pemrograman C yang tidak dapat diakses tetapi berguna di mana pagar kompilator berguna, bahkan dalam kode multicore (terutama dalam kode pra-C11). Situasi yang umum adalah di mana program melakukan beberapa akses yang biasanya dibuat mudah berubah (karena mereka berbagi variabel), tetapi Anda ingin compiler untuk dapat memindahkan akses di sekitar. Jika Anda tahu bahwa aksesnya bersifat atom pada platform target (dan Anda mengambil beberapa tindakan pencegahan lainnya), Anda dapat membiarkan akses tidak berubah, tetapi berisi pergerakan kode menggunakan penghalang kompilator.

Untungnya, sebagian besar pemrograman seperti ini dibuat usang dengan atom-atom santai C11 / C ++ 11.


2
2017-11-05 09:19