it-swarm-id.com

Mengapa volatile ada?

Apa kata kunci volatile lakukan? Dalam C++ masalah apa yang dipecahkan?

Dalam kasus saya, saya tidak pernah secara sengaja membutuhkannya.

201
theschmitzer

volatile diperlukan jika Anda membaca dari satu titik di memori yang, katakanlah, proses/perangkat yang sepenuhnya terpisah/apa pun yang mungkin Anda tulis.

Saya dulu bekerja dengan ram dual-port dalam sistem multiprosesor dalam lurus C. Kami menggunakan perangkat keras yang dikelola nilai 16 bit sebagai semaphore untuk mengetahui kapan orang lain selesai. Pada dasarnya kami melakukan ini:

void waitForSemaphore()
{
   volatile uint16_t* semPtr = WELL_KNOWN_SEM_ADDR;/*well known address to my semaphore*/
   while ((*semPtr) != IS_OK_FOR_ME_TO_PROCEED);
}

Tanpa volatile, pengoptimal melihat loop sebagai tidak berguna (Orang itu tidak pernah menetapkan nilai! Dia gila, singkirkan kode itu!) Dan kode saya akan dilanjutkan tanpa memperoleh semaphore, menyebabkan masalah di kemudian hari.

247
Doug T.

volatile diperlukan ketika mengembangkan sistem tertanam atau driver perangkat, di mana Anda perlu membaca atau menulis perangkat keras yang dipetakan dengan memori. Isi register perangkat tertentu dapat berubah kapan saja, jadi Anda memerlukan kata kunci volatile untuk memastikan bahwa akses tersebut tidak dioptimalkan oleh kompiler.

78
ChrisN

Beberapa prosesor memiliki register titik apung yang memiliki lebih dari 64 bit presisi (mis. 32-bit x86 tanpa SSE, lihat komentar Peter). Dengan begitu, jika Anda menjalankan beberapa operasi pada angka presisi ganda, Anda benar-benar mendapatkan jawaban dengan presisi lebih tinggi daripada jika Anda memotong setiap hasil antara hingga 64 bit.

Ini biasanya bagus, tetapi itu berarti bahwa tergantung pada bagaimana kompiler ditugaskan register dan melakukan optimasi Anda akan memiliki hasil yang berbeda untuk operasi yang sama persis pada input yang sama persis. Jika Anda memerlukan konsistensi maka Anda dapat memaksa setiap operasi untuk kembali ke memori dengan menggunakan kata kunci yang mudah menguap.

Ini juga berguna untuk beberapa algoritma yang tidak masuk akal aljabar tetapi mengurangi kesalahan floating point, seperti penjumlahan Kahan. Secara aljabar itu nop, jadi sering kali akan salah dioptimalkan keluar kecuali beberapa variabel menengah yang mudah menguap.

68
tfinniga

Dari artikel "Volatile sebagai janji" oleh Dan Saks:

(...) objek volatil adalah objek yang nilainya mungkin berubah secara spontan. Artinya, ketika Anda mendeklarasikan objek menjadi volatil, Anda memberi tahu kompiler bahwa objek tersebut dapat berubah status meskipun tidak ada pernyataan dalam program yang tampaknya mengubahnya. "

Berikut ini tautan ke tiga artikelnya mengenai kata kunci volatile:

45
MikeZ

Anda HARUS menggunakan volatile ketika menerapkan struktur data bebas kunci. Kalau tidak, kompiler bebas untuk mengoptimalkan akses ke variabel, yang akan mengubah semantik.

Dengan kata lain, volatile memberi tahu kompiler bahwa akses ke variabel ini harus sesuai dengan operasi baca/tulis memori fisik.

Sebagai contoh, ini adalah bagaimana InterlockedIncrement dideklarasikan di Win32 API:

LONG __cdecl InterlockedIncrement(
  __inout  LONG volatile *Addend
);
23

Sebuah aplikasi besar yang saya kerjakan di awal 1990-an berisi penanganan pengecualian berbasis C menggunakan setjmp dan longjmp. Kata kunci yang mudah menguap diperlukan pada variabel yang nilainya perlu dipertahankan dalam blok kode yang berfungsi sebagai klausa "catch", agar vars tersebut tidak disimpan dalam register dan dihilangkan oleh longjmp.

10
Jeff Doar

Dalam Standar C, salah satu tempat untuk menggunakan volatile adalah dengan pengendali sinyal. Faktanya, dalam Standar C, yang dapat Anda lakukan dengan aman dalam penangan sinyal adalah memodifikasi variabel volatile sig_atomic_t, Atau keluar dengan cepat. Memang, AFAIK, itu adalah satu-satunya tempat di Standar C yang menggunakan volatile diperlukan untuk menghindari perilaku yang tidak terdefinisi.

ISO/IEC 9899: 2011 §7.14.1.1 Fungsi signal

¶5 Jika sinyal muncul selain dari memanggil fungsi abort atau raise, perilaku tidak akan ditentukan jika penangan sinyal merujuk ke objek apa pun dengan durasi penyimpanan statis atau thread yang tidak objek atom bebas kunci selain dengan menetapkan nilai ke objek yang dinyatakan sebagai volatile sig_atomic_t, atau penangan sinyal memanggil fungsi apa pun di pustaka standar selain fungsi abort, the _Exit, Fungsi quick_exit, Atau fungsi signal dengan argumen pertama sama dengan nomor sinyal yang sesuai dengan sinyal yang menyebabkan permintaan pawang. Lebih jauh, jika panggilan seperti itu ke fungsi signal menghasilkan pengembalian SIG_ERR, nilai errno tidak pasti.252)

252) Jika ada sinyal yang dihasilkan oleh penangan sinyal asinkron, perilaku tidak terdefinisi.

Itu berarti dalam Standar C, Anda dapat menulis:

static volatile sig_atomic_t sig_num = 0;

static void sig_handler(int signum)
{
    signal(signum, sig_handler);
    sig_num = signum;
}

dan tidak banyak lagi.

POSIX jauh lebih lunak tentang apa yang dapat Anda lakukan dalam penangan sinyal, tetapi masih ada batasan (dan salah satu batasannya adalah bahwa pustaka I/O Standar - printf() et al - tidak dapat digunakan dengan aman ).

10
Jonathan Leffler

Selain menggunakannya sebagaimana dimaksud, volatile digunakan dalam metaprogramming (templat). Itu dapat digunakan untuk mencegah overload yang tidak disengaja, karena atribut volatile (seperti const) mengambil bagian dalam resolusi overload.

template <typename T> 
class Foo {
  std::enable_if_t<sizeof(T)==4, void> f(T& t) 
  { std::cout << 1 << t; }
  void f(T volatile& t) 
  { std::cout << 2 << const_cast<T&>(t); }

  void bar() { T t; f(t); }
};

Ini legal; keduanya kelebihan berpotensi dipanggil dan melakukan hampir sama. Para pemain di volatile overload sah karena kita tahu bilah tidak akan meneruskan T yang tidak volatil. Versi volatile benar-benar lebih buruk, jadi jangan pernah memilih dalam resolusi kelebihan jika f non-volatile tersedia.

Perhatikan bahwa kode tidak pernah benar-benar bergantung pada volatile akses memori.

7
MSalters

Saya telah menggunakannya dalam membangun debug ketika kompiler bersikeras mengoptimalkan variabel yang saya ingin dapat melihat ketika saya melangkah melalui kode.

7
indentation

Mengembangkan untuk tertanam, saya memiliki loop yang memeriksa variabel yang dapat diubah dalam penangan interrupt. Tanpa "volatile", loop menjadi noop - sejauh yang dapat dikompilasi oleh kompiler, variabel tidak pernah berubah, sehingga mengoptimalkan pemeriksaan.

Hal yang sama akan berlaku untuk variabel yang dapat diubah di utas berbeda di lingkungan yang lebih tradisional, tetapi di sana kita sering melakukan panggilan sinkronisasi, jadi kompiler tidak begitu bebas dengan optimasi.

7
Arkadiy
  1. anda harus menggunakannya untuk mengimplementasikan spinlocks serta beberapa (semua?) struktur data bebas kunci
  2. gunakan dengan operasi atom/instruksi
  3. membantu saya sekali untuk mengatasi bug kompiler (kode yang dihasilkan salah selama optimasi)
6
Mladen Janković

Kata kunci volatile dimaksudkan untuk mencegah kompiler menerapkan optimisasi apa pun pada objek yang dapat berubah dengan cara yang tidak dapat ditentukan oleh kompiler.

Objek yang dinyatakan sebagai volatile dihilangkan dari optimisasi karena nilainya dapat diubah dengan kode di luar cakupan kode saat ini kapan saja. Sistem selalu membaca nilai saat ini dari objek volatile dari lokasi memori daripada menyimpan nilainya dalam register sementara pada titik yang diminta, bahkan jika instruksi sebelumnya meminta nilai dari objek yang sama.

Pertimbangkan kasus-kasus berikut

1) Variabel global yang dimodifikasi oleh rutinitas layanan interupsi di luar ruang lingkup.

2) Variabel global dalam aplikasi multi-utas.

Jika kami tidak menggunakan kualifikasi volatil, masalah berikut mungkin muncul

1) Kode mungkin tidak berfungsi seperti yang diharapkan ketika optimasi dihidupkan.

2) Kode mungkin tidak berfungsi seperti yang diharapkan ketika interupsi diaktifkan dan digunakan.

Volatile: Sahabat seorang programmer

https://en.wikipedia.org/wiki/Volatile_ (computer_programming)

4
roottraveller

Program Anda tampaknya berfungsi bahkan tanpa volatile kata kunci? Mungkin ini alasannya:

Seperti disebutkan sebelumnya kata kunci volatile membantu untuk kasus seperti

volatile int* p = ...;  // point to some memory
while( *p!=0 ) {}  // loop until the memory becomes zero

Tetapi tampaknya hampir tidak ada efek begitu fungsi eksternal atau non-inline dipanggil. Misalnya.:

while( *p!=0 ) { g(); }

Kemudian dengan atau tanpa volatile hasil yang hampir sama dihasilkan.

Selama g() dapat sepenuhnya digariskan, kompiler dapat melihat semua yang sedang terjadi dan karenanya dapat mengoptimalkan. Tetapi ketika program membuat panggilan ke tempat di mana kompiler tidak dapat melihat apa yang terjadi, tidak aman bagi kompiler untuk membuat asumsi apa pun lagi. Oleh karena itu kompiler akan menghasilkan kode yang selalu membaca dari memori secara langsung.

Tetapi waspadalah terhadap hari itu, ketika fungsi Anda g() menjadi sejajar (baik karena perubahan eksplisit atau karena kepintaran kompiler/linker) maka kode Anda mungkin rusak jika Anda lupa volatile kata kunci!

Karena itu saya sarankan untuk menambahkan kata kunci volatile bahkan jika program Anda tampaknya tidak berfungsi. Itu membuat niat lebih jelas dan lebih kuat dalam hal perubahan di masa depan.

2
Joachim

Pada hari-hari awal C, kompiler akan menafsirkan semua tindakan yang membaca dan menulis nilai-nilai sebagai operasi memori, yang akan dilakukan dalam urutan yang sama dengan membaca dan menulis muncul dalam kode. Efisiensi dapat sangat ditingkatkan dalam banyak kasus jika kompiler diberi sejumlah kebebasan untuk memesan ulang dan mengkonsolidasikan operasi, tetapi ada masalah dengan ini. Bahkan operasi sering ditentukan dalam urutan tertentu hanya karena itu perlu untuk menentukannya dalam beberapa dalam urutan, dan dengan demikian programmer memilih salah satu dari banyak alternatif yang sama-sama baik, itu tidak selalu terjadi. Terkadang penting bahwa operasi tertentu terjadi dalam urutan tertentu.

Detail urutan mana yang penting akan bervariasi tergantung pada platform target dan bidang aplikasi. Alih-alih memberikan kontrol yang terinci, Standar memilih model sederhana: jika urutan akses dilakukan dengan nilai yang tidak memenuhi syarat volatile, kompiler dapat menyusun ulang dan mengkonsolidasikannya sesuai keinginan. Jika suatu tindakan dilakukan dengan volatile - nilai kualifikasi, implementasi kualitas harus menawarkan jaminan pemesanan tambahan apa pun yang mungkin diperlukan dengan kode yang menargetkan platform dan bidang aplikasi yang dimaksud, tanpa harus menggunakan sintaksis non-standar .

Sayangnya, alih-alih mengidentifikasi jaminan apa yang dibutuhkan programmer, banyak penyusun memilih untuk menawarkan jaminan minimum yang diamanatkan oleh Standar. Ini membuat volatile jauh lebih tidak berguna daripada seharusnya. Pada gcc atau dentang, misalnya, seorang programmer yang perlu menerapkan dasar "hand-off mutex" [tugas di mana tugas yang telah memperoleh dan merilis sebuah mutex tidak akan melakukannya lagi sampai tugas lain selesai] harus melakukan satu dari empat hal:

  1. Masukkan akuisisi dan pelepasan mutex ke dalam fungsi yang kompilernya tidak bisa sebaris, dan yang tidak bisa diterapkan Seluruh Program Optimasi.

  2. Kualifikasi semua objek yang dijaga oleh mutex sebagai volatile - sesuatu yang seharusnya tidak perlu jika semua akses terjadi setelah memperoleh mutex dan sebelum melepaskannya.

  3. Gunakan level pengoptimalan 0 untuk memaksa kompiler menghasilkan kode seolah-olah semua objek yang tidak memenuhi syarat register adalah volatile.

  4. Gunakan arahan khusus gcc.

Sebaliknya, ketika menggunakan kompiler berkualitas lebih tinggi yang lebih cocok untuk pemrograman sistem, seperti icc, seseorang akan memiliki opsi lain:

  1. Pastikan bahwa volatile - penulisan yang memenuhi syarat dilakukan di mana pun diperlukan atau dibebaskan rilis.

Mengakuisisi "hand-off mutex" dasar memerlukan pembacaan volatile (untuk melihat apakah sudah siap), dan seharusnya tidak memerlukan penulisan volatile juga (pihak lain tidak akan mencoba untuk dapatkan kembali sampai diserahkan kembali) tetapi harus melakukan penulisan volatile yang tidak berarti masih lebih baik daripada opsi yang tersedia di bawah gcc atau dentang.

2
supercat

Selain fakta bahwa kata kunci yang mudah menguap digunakan untuk memberi tahu kompiler untuk tidak mengoptimalkan akses ke beberapa variabel (yang dapat dimodifikasi oleh utas atau rutin interupsi), itu juga bisa digunakan untuk menghapus beberapa bug kompiler - YA bisa ---.

Sebagai contoh saya bekerja pada platform yang disematkan dimana kompiler membuat beberapa asumsi yang salah mengenai nilai suatu variabel. Jika kode tidak dioptimalkan, program akan berjalan ok. Dengan optimasi (yang benar-benar diperlukan karena itu adalah rutinitas kritis) kode tidak akan berfungsi dengan benar. Satu-satunya solusi (meskipun tidak terlalu benar) adalah mendeklarasikan variabel 'salah' sebagai volatile.

2
INS

Satu penggunaan yang harus saya ingatkan adalah dalam fungsi pengendali sinyal, jika Anda ingin mengakses/memodifikasi variabel global (misalnya, tandai sebagai exit = true) Anda harus mendeklarasikan variabel itu sebagai 'volatil'.

1
bugs king