Apa yang dilakukan register
kata kunci dalam bahasa C? Saya telah membaca bahwa ini digunakan untuk mengoptimalkan tetapi tidak didefinisikan dengan jelas dalam standar apa pun. Apakah masih relevan dan jika demikian, kapan Anda akan menggunakannya?
273
register
variabel.Jawaban:
Ini adalah petunjuk bagi kompiler bahwa variabel akan banyak digunakan dan Anda merekomendasikannya disimpan dalam register prosesor jika memungkinkan.
Kebanyakan kompiler modern melakukannya secara otomatis, dan lebih baik dalam memilih mereka daripada kita manusia.
sumber
register
variabel diambil; ini adalah satu - satunya efek wajib dariregister
kata kunci. Bahkan ini cukup untuk meningkatkan optimisasi, karena menjadi sepele untuk mengatakan bahwa variabel hanya dapat dimodifikasi dalam fungsi ini.Saya terkejut bahwa tidak ada yang menyebutkan bahwa Anda tidak dapat mengambil alamat variabel register, bahkan jika kompiler memutuskan untuk menyimpan variabel dalam memori daripada dalam register.
Jadi menggunakan
register
Anda tidak menang apa-apa (kompiler tetap akan memutuskan sendiri di mana menempatkan variabel) dan kehilangan&
operator - tidak ada alasan untuk menggunakannya.sumber
register
berguna untuk ini bahkan jika kompiler tidak memasukkannya ke dalam register.const
ini juga menjadi tidak berguna karena tidak memenangkan apa pun, Anda hanya kehilangan kemampuan untuk mengubah variabel.register
dapat digunakan untuk memastikan tidak ada yang mengambil alamat variabel di masa depan tanpa berpikir. Saya tidak pernah punya alasan untuk menggunakannyaregister
.Ini memberitahu kompiler untuk mencoba menggunakan register CPU, bukan RAM, untuk menyimpan variabel. Register ada di CPU dan jauh lebih cepat diakses daripada RAM. Tapi itu hanya saran untuk kompiler, dan mungkin tidak bisa ditindaklanjuti.
sumber
Saya tahu pertanyaan ini tentang C, tetapi pertanyaan yang sama untuk C ++ ditutup sebagai duplikat yang tepat dari pertanyaan ini. Karena itu jawaban ini mungkin tidak berlaku untuk C.
Draf terbaru standar C ++ 11, N3485 , mengatakan ini dalam 7.1.1 / 3:
Dalam C ++ (tetapi tidak dalam C), standar tidak menyatakan bahwa Anda tidak dapat mengambil alamat variabel yang dideklarasikan
register
; Namun, karena variabel yang disimpan dalam register CPU sepanjang masa hidupnya tidak memiliki lokasi memori yang terkait dengannya, upaya untuk mengambil alamatnya tidak valid, dan kompiler akan mengabaikanregister
kata kunci untuk mengizinkan pengambilan alamat.sumber
Belum relevan selama setidaknya 15 tahun karena pengoptimal membuat keputusan yang lebih baik tentang ini daripada yang Anda bisa. Bahkan ketika itu relevan, itu jauh lebih masuk akal pada arsitektur CPU dengan banyak register, seperti SPARC atau M68000 daripada di Intel dengan kekurangan register, sebagian besar yang disediakan oleh kompiler untuk keperluannya sendiri.
sumber
Sebenarnya, register memberi tahu kompiler bahwa variabel tidak alias dengan hal lain dalam program (bahkan char).
Itu dapat dieksploitasi oleh kompiler modern dalam berbagai situasi, dan dapat membantu kompiler sedikit dalam kode kompleks - dalam kode sederhana kompiler dapat mengetahui hal ini sendiri.
Kalau tidak, itu tidak ada gunanya dan tidak digunakan untuk alokasi register. Biasanya tidak menyebabkan penurunan kinerja untuk menentukannya, selama kompiler Anda cukup modern.
sumber
register
melarang mengambil alamat sama sekali, karena jika tidak, akan berguna untuk memberi tahu kompiler kasus di mana kompiler akan dapat menerapkan optimasi register meskipun alamat variabel diteruskan ke fungsi eksternal (variabel harus dimasukkan ke memori untuk panggilan tertentu , tetapi begitu fungsi kembali, kompiler dapat kembali memperlakukannya sebagai variabel yang alamatnya tidak pernah diambil).bar
adalahregister
variabel, compiler mungkin di waktu luang nya gantifoo(&bar);
denganint temp=bar; foo(&temp); bar=temp;
, tetapi mengambil alamatbar
akan dilarang di sebagian besar konteks lain tidak akan tampak seperti aturan yang terlalu rumit. Jika variabel bisa disimpan dalam register, substitusi akan membuat kode lebih kecil. Jika variabel tetap harus disimpan dalam RAM, substitusi akan membuat kode lebih besar. Membiarkan pertanyaan apakah membuat substitusi hingga kompiler akan menghasilkan kode yang lebih baik dalam kedua kasus.register
kualifikasi pada variabel global, apakah kompiler mengizinkan alamat untuk diambil atau tidak, akan memungkinkan beberapa optimasi yang bagus dalam kasus-kasus di mana fungsi yang digariskan yang menggunakan variabel global disebut berulang kali dalam satu lingkaran. Saya tidak bisa memikirkan cara lain untuk membiarkan variabel itu disimpan dalam register antara iterasi loop - bisakah Anda?Faktanya, standar ini didefinisikan dengan jelas oleh standar C. Mengutip draft N1570 bagian 6.7.1 paragraf 6 (versi lain memiliki kata-kata yang sama):
&
Operator unary tidak dapat diterapkan pada objek yang didefinisikan denganregister
, danregister
tidak dapat digunakan dalam deklarasi eksternal.Ada beberapa aturan lain (cukup tidak jelas) yang khusus untuk
register
objek-berkualitas:Mendefinisikan objek array denganregister
perilaku tidak terdefinisi.Koreksi: Adalah sah untuk mendefinisikan objek array
register
, tetapi Anda tidak dapat melakukan sesuatu yang berguna dengan objek semacam itu (pengindeksan ke dalam array memerlukan pengambilan alamat elemen awalnya)._Alignas
specifier (baru di C11) tidak dapat diterapkan ke objek tersebut.va_start
makro-register
terkualifikasi, perilaku tidak terdefinisi.Mungkin ada beberapa yang lain; unduh konsep standar dan cari "daftar" jika Anda tertarik.
Seperti namanya, arti asli
register
adalah untuk meminta objek disimpan dalam register CPU. Tetapi dengan perbaikan dalam mengoptimalkan kompiler, ini menjadi kurang bermanfaat. Versi modern dari standar C tidak merujuk ke register CPU, karena mereka tidak lagi (perlu) menganggap bahwa ada hal seperti itu (ada arsitektur yang tidak menggunakan register). Kebijaksanaan umum adalah bahwa menerapkanregister
deklarasi objek lebih cenderung memperburuk kode yang dihasilkan, karena itu mengganggu alokasi register kompilator sendiri. Mungkin masih ada beberapa kasus di mana itu berguna (katakanlah, jika Anda benar-benar tahu seberapa sering variabel akan diakses, dan pengetahuan Anda lebih baik daripada apa yang bisa diketahui oleh kompiler pengoptimal modern).Efek nyata utama
register
adalah mencegah setiap upaya untuk mengambil alamat objek. Ini tidak terlalu berguna sebagai petunjuk optimasi, karena hanya dapat diterapkan pada variabel lokal, dan kompilator pengoptimalisasi dapat melihat sendiri bahwa alamat objek seperti itu tidak diambil.sumber
register
objek array yang memenuhi syarat, jika itu yang Anda pikirkan.register
objek array; lihat poin pertama yang diperbarui dalam jawaban saya. Adalah sah untuk mendefinisikan objek seperti itu, tetapi Anda tidak dapat melakukan apa pun dengannya. Jika Anda menambahkanregister
definisis
dalam contoh Anda , program ini ilegal (pelanggaran kendala) dalam C. C ++ tidak menempatkan batasan yang samaregister
, sehingga program tersebut akan menjadi valid C ++ (tetapi menggunakanregister
akan sia-sia).register
kunci dapat melayani tujuan yang berguna jika sah untuk mengambil alamat variabel seperti itu tetapi hanya dalam kasus di mana semantik tidak akan terpengaruh dengan menyalin variabel ke sementara ketika alamat diambil, dan memuat ulang dari sementara pada titik urutan berikutnya. Itu akan memungkinkan kompiler untuk berasumsi bahwa variabel dapat disimpan dengan aman di register di semua akses pointer asalkan itu memerah di setiap tempat alamatnya diambil.Waktu cerita!
C, sebagai bahasa, adalah abstraksi komputer. Hal ini memungkinkan Anda untuk melakukan hal-hal, dalam hal apa yang dilakukan komputer, yaitu memanipulasi memori, melakukan matematika, mencetak sesuatu, dll.
Tetapi C hanyalah abstraksi. Dan akhirnya, apa yang diambil dari Anda adalah bahasa Assembly. Assembly adalah bahasa yang dibaca CPU, dan jika Anda menggunakannya, Anda melakukan hal-hal dalam hal CPU. Apa yang dilakukan CPU? Pada dasarnya, ia membaca dari memori, melakukan matematika, dan menulis ke memori. CPU tidak hanya menghitung angka dalam memori. Pertama, Anda harus memindahkan nomor dari memori ke memori di dalam CPU yang disebut register. Setelah selesai melakukan apa pun yang perlu Anda lakukan untuk nomor ini, Anda dapat memindahkannya kembali ke memori sistem normal. Mengapa menggunakan memori sistem sama sekali? Registrasi jumlahnya terbatas. Anda hanya mendapatkan sekitar seratus byte dalam prosesor modern, dan prosesor populer yang lebih tua bahkan lebih terbatas secara fantastis (The 6502 memiliki 3 register 8-bit untuk penggunaan gratis Anda). Jadi, operasi matematika rata-rata Anda seperti:
Banyak dari itu ... bukan matematika. Operasi pemuatan dan penyimpanan tersebut dapat memakan waktu hingga setengah waktu pemrosesan Anda. C, sebagai abstraksi komputer, membebaskan programmer khawatir menggunakan dan menyulap register, dan karena jumlah dan jenis bervariasi antara komputer, C menempatkan tanggung jawab alokasi register hanya pada kompiler. Dengan satu pengecualian.
Saat Anda mendeklarasikan variabel
register
, Anda mengatakan kepada kompiler, "Yo, saya bermaksud agar variabel ini banyak digunakan dan / atau berumur pendek. Jika saya jadi Anda, saya akan mencoba menyimpannya dalam register." Ketika standar C mengatakan kompiler tidak harus benar-benar melakukan apa-apa, itu karena standar C tidak tahu komputer apa yang Anda kompilasi, dan mungkin seperti 6502 di atas, di mana ketiga register diperlukan hanya untuk mengoperasikan , dan tidak ada register cadangan untuk menyimpan nomor Anda. Namun, ketika dikatakan Anda tidak dapat mengambil alamat, itu karena register tidak memiliki alamat. Mereka adalah tangan prosesor. Karena kompiler tidak harus memberi Anda alamat, dan karena tidak dapat memiliki alamat sama sekali, beberapa optimasi sekarang terbuka untuk kompiler. Bisa, katakanlah, menyimpan nomor dalam register selalu. Tidak Anda tidak perlu khawatir tentang tempat penyimpanannya di memori komputer (di luar keharusan untuk mendapatkannya kembali). Bahkan bisa menghukumnya menjadi variabel lain, memberikannya ke prosesor lain, memberinya lokasi yang berubah, dll.tl; dr: Variabel berumur pendek yang mengerjakan banyak matematika. Jangan mendeklarasikan terlalu banyak sekaligus.
sumber
Anda mengacaukan algoritma pewarnaan graf canggih dari kompiler. Ini digunakan untuk alokasi register. Yah, kebanyakan. Ini bertindak sebagai petunjuk untuk kompiler - itu benar. Tetapi jangan diabaikan secara keseluruhan karena Anda tidak diizinkan untuk mengambil alamat variabel register (ingat kompiler, sekarang pada belas kasihan Anda, akan mencoba untuk bertindak berbeda). Yang dengan cara memberitahu Anda untuk tidak menggunakannya.
Kata kunci itu digunakan lama sekali, panjang kembali. Ketika hanya ada sedikit register yang bisa menghitung semuanya menggunakan jari telunjuk Anda.
Tetapi, seperti yang saya katakan, usang tidak berarti Anda tidak dapat menggunakannya.
sumber
Hanya sedikit demo (tanpa tujuan dunia nyata) untuk perbandingan: saat menghapus
register
kata kunci sebelum setiap variabel, kode ini memakan waktu 3,41 detik pada i7 (GCC) saya, denganregister
kode yang sama selesai dalam 0,7 detik.sumber
Saya telah menguji kata kunci register di bawah QNX 6.5.0 menggunakan kode berikut:
Saya mendapat hasil sebagai berikut:
-> 807679611 siklus berlalu
-> Sistem ini memiliki 3300830000 siklus per detik
-> Siklus dalam hitungan detik adalah ~ 0,244600
Dan sekarang tanpa register int:
Saya mendapatkan:
-> 1421694077 siklus telah berlalu
-> Sistem ini memiliki 3300830000 siklus per detik
-> Siklus dalam hitungan detik adalah ~ 0,430700
sumber
Mendaftar akan memberi tahu kompiler bahwa pembuat kode percaya bahwa variabel ini akan ditulis / dibaca cukup untuk membenarkan penyimpanannya di salah satu dari beberapa register yang tersedia untuk penggunaan variabel. Membaca / menulis dari register biasanya lebih cepat dan memerlukan set kode op yang lebih kecil.
Saat ini, ini tidak terlalu berguna, karena kebanyakan pengoptimal kompiler lebih baik daripada Anda dalam menentukan apakah register harus digunakan untuk variabel itu, dan untuk berapa lama.
sumber
Selama tahun tujuh puluhan, pada awal bahasa C, kata kunci register telah diperkenalkan untuk memungkinkan programmer memberikan petunjuk kepada kompiler, mengatakan bahwa variabel akan digunakan sangat sering, dan bahwa itu harus bijaksana untuk simpan nilainya di salah satu register internal prosesor.
Saat ini, pengoptimal jauh lebih efisien daripada programmer untuk menentukan variabel yang lebih mungkin untuk disimpan dalam register, dan pengoptimal tidak selalu mempertimbangkan petunjuk programmer.
Jadi banyak orang salah merekomendasikan untuk tidak menggunakan kata kunci register.
Mari kita lihat alasannya!
Kata kunci register memiliki efek samping yang terkait: Anda tidak dapat merujuk (mendapatkan alamat) variabel tipe register.
Orang yang menyarankan orang lain untuk tidak menggunakan register menganggap ini sebagai argumen tambahan.
Namun, fakta sederhana mengetahui bahwa Anda tidak dapat mengambil alamat variabel register, memungkinkan kompiler (dan pengoptimalnya) mengetahui bahwa nilai variabel ini tidak dapat dimodifikasi secara tidak langsung melalui pointer.
Ketika pada titik tertentu dari aliran instruksi, variabel register memiliki nilainya yang ditetapkan dalam register prosesor, dan register tersebut belum digunakan karena untuk mendapatkan nilai variabel lain, kompiler tahu bahwa ia tidak perlu memuat ulang nilai variabel dalam register itu. Hal ini memungkinkan untuk menghindari akses memori mahal yang tidak berguna.
Lakukan tes Anda sendiri dan Anda akan mendapatkan peningkatan kinerja yang signifikan di loop batin Anda.
c_register_side_effect_performance_boost
sumber
Pada kompiler C yang didukung, ia mencoba untuk mengoptimalkan kode sehingga nilai variabel disimpan dalam register prosesor yang sebenarnya.
sumber
Kompiler Visual C ++ Microsoft mengabaikan
register
kata kunci ketika optimasi alokasi register global (flag / compiler Oe) diaktifkan.Lihat mendaftar kata kunci di MSDN.
sumber
Daftarkan kata kunci memberitahu kompiler untuk menyimpan variabel tertentu dalam register CPU sehingga dapat diakses dengan cepat. Dari sudut pandang programmer, kata kunci register digunakan untuk variabel yang banyak digunakan dalam suatu program, sehingga kompiler dapat mempercepat kode. Meskipun itu tergantung pada kompiler apakah akan menyimpan variabel dalam register CPU atau memori utama.
sumber
Register menunjukkan kepada kompiler untuk mengoptimalkan kode ini dengan menyimpan variabel tertentu dalam register kemudian dalam memori. itu adalah permintaan untuk kompiler, kompiler mungkin atau mungkin tidak mempertimbangkan permintaan ini. Anda dapat menggunakan fasilitas ini jika beberapa variabel Anda sedang diakses sangat sering. Misalnya: Perulangan.
Satu hal lagi adalah bahwa jika Anda mendeklarasikan variabel sebagai register maka Anda tidak bisa mendapatkan alamatnya karena tidak disimpan dalam memori. itu mendapat alokasi dalam register CPU.
sumber
gcc 9.3 asm output, tanpa menggunakan flag optimisasi (semua yang ada dalam jawaban ini merujuk pada kompilasi standar tanpa flag optimisasi):
Kekuatan ini
ebx
digunakan untuk perhitungan, yang berarti perlu didorong ke stack dan dikembalikan pada akhir fungsi karena disimpan sementara.register
menghasilkan lebih banyak baris kode dan 1 memori tulis dan 1 memori dibaca (walaupun secara realistis, ini bisa dioptimalkan ke 0 R / Ws jika perhitungan telah dilakukanesi
, yang adalah apa yang terjadi menggunakan C ++ 'sconst register
). Tidak menggunakanregister
penyebab 2 tulis dan 1 baca (walaupun store to load forwarding akan terjadi pada baca). Ini karena nilainya harus ada dan diperbarui secara langsung pada tumpukan sehingga nilai yang benar dapat dibaca berdasarkan alamat (penunjuk).register
tidak memiliki persyaratan ini dan tidak dapat diarahkan.const
danregister
pada dasarnya kebalikan darivolatile
dan menggunakanvolatile
akan mengesampingkan optimisasi const pada ruang lingkup file dan blok danregister
optimisasi pada ruang lingkup blok.const register
danregister
akan menghasilkan output yang identik karena const tidak melakukan apapun pada C pada block-scope, jadi hanyaregister
optimisasi yang berlaku.Pada dentang,
register
diabaikan tetapiconst
optimisasi masih terjadi.sumber