Saya sedang menelusuri beberapa dokumentasi dan pertanyaan / jawaban dan melihatnya disebutkan. Saya membaca deskripsi singkat, menyatakan bahwa itu pada dasarnya adalah janji dari programmer bahwa pointer tidak akan digunakan untuk menunjuk ke tempat lain.
Adakah yang bisa menawarkan beberapa kasus realistis di mana nilainya benar-benar menggunakan ini?
c
gcc
c99
restrict-qualifier
user90052
sumber
sumber
memcpy
vsmemmove
adalah salah satu contoh kanonik.restrict
-kualifikasi argumen untukmemcpy
memungkinkan pada prinsipnya implementasi naif untuk dioptimalkan secara agresif, dan 2) hanya memanggilmemcpy
memungkinkan kompiler untuk mengasumsikan bahwa argumen yang diberikan kepadanya tidak alias, yang dapat memungkinkan beberapa optimasi di sekitarmemcpy
panggilan.memcpy(anything, anything, 0);
sebagai no-op, dan memastikan bahwa jikap
adalah pointer untuk setidaknyan
byte ditulis,memcpy(p,p,n)
; tidak akan memiliki efek samping yang merugikan. Kasus-kasus seperti itu dapat muncul ...Jawaban:
restrict
mengatakan bahwa pointer adalah satu-satunya hal yang mengakses objek yang mendasarinya. Ini menghilangkan potensi aliasing pointer, memungkinkan optimasi yang lebih baik oleh kompiler.Sebagai contoh, misalkan saya memiliki mesin dengan instruksi khusus yang dapat mengalikan vektor angka dalam memori, dan saya memiliki kode berikut:
Kompilator perlu menangani dengan benar jika
dest
,,src1
dansrc2
tumpang tindih, artinya harus melakukan satu perkalian pada satu waktu, dari awal hingga akhir. Dengan memilikirestrict
, kompiler bebas untuk mengoptimalkan kode ini dengan menggunakan instruksi vektor.Wikipedia memiliki entri aktif
restrict
, dengan contoh lain, di sini .sumber
dest
tumpang tindih salah satu vektor sumber. Mengapa akan ada masalah jikasrc1
dansrc2
tumpang tindih?The Wikipedia Contoh adalah sangat mencerahkan.
Itu jelas menunjukkan bagaimana hal itu memungkinkan untuk menyimpan satu instruksi perakitan .
Tanpa batasan:
Perakitan semu:
Dengan batasan:
Perakitan semu:
Apakah GCC benar-benar melakukannya?
GCC 4.8 Linux x86-64:
Dengan
-O0
, mereka sama.Dengan
-O3
:Untuk yang belum tahu, konvensi pemanggilan adalah:
rdi
= parameter pertamarsi
= parameter keduardx
= parameter ketigaOutput GCC bahkan lebih jelas daripada artikel wiki: 4 instruksi vs 3 instruksi.
Array
Sejauh ini kami memiliki penghematan instruksi tunggal, tetapi jika pointer mewakili array yang akan dilewati, kasus penggunaan umum, maka banyak instruksi dapat disimpan, seperti yang disebutkan oleh supercat .
Pertimbangkan misalnya:
Karena itu
restrict
, kompiler pintar (atau manusia), dapat mengoptimalkannya untuk:yang berpotensi jauh lebih efisien karena perakitan dapat dioptimalkan pada implementasi libc yang layak (seperti glibc): Apakah lebih baik menggunakan std :: memcpy () atau std :: copy () dalam hal kinerja?
Apakah GCC benar-benar melakukannya?
GCC 5.2.1.Linux x86-64 Ubuntu 15.10:
Dengan
-O0
, keduanya sama.Dengan
-O3
:dengan batasan:
Dua
memset
panggilan seperti yang diharapkan.tanpa batasan: tidak ada panggilan stdlib, hanya loop lebar iterasi 16 terbuka yang saya tidak ingin mereproduksi di sini :-)
Saya belum memiliki kesabaran untuk membandingkan mereka, tetapi saya percaya bahwa versi pembatasan akan lebih cepat.
C99
Mari kita lihat standar demi kelengkapan.
restrict
mengatakan bahwa dua petunjuk tidak dapat menunjuk ke wilayah memori yang tumpang tindih. Penggunaan paling umum adalah untuk argumen fungsi.Ini membatasi bagaimana fungsi dapat dipanggil, tetapi memungkinkan untuk lebih banyak waktu kompilasi optimasi.
Jika penelepon tidak mengikuti
restrict
kontrak, perilaku tidak terdefinisi.The C99 N1256 rancangan 6.7.3 / 7 "Jenis kualifikasi" mengatakan:
dan 6.7.3.1 "Definisi formal pembatasan" memberikan detail yang mengerikan.
Aturan aliasing yang ketat
Kata
restrict
kunci hanya memengaruhi pointer dari tipe yang kompatibel (misalnya duaint*
) karena aturan aliasing yang ketat mengatakan bahwa aliasing tipe yang tidak kompatibel adalah perilaku yang tidak terdefinisi secara default, sehingga kompiler dapat menganggap itu tidak terjadi dan mengoptimalkannya.Lihat: Apa aturan aliasing yang ketat?
Lihat juga
restrict
, tetapi GCC memiliki__restrict__
ekstensi: Apa arti kata kunci pembatasan dalam C ++?__attribute__((malloc))
, yang mengatakan bahwa nilai pengembalian suatu fungsi tidak alias apa pun: GCC: __attribute __ ((malloc))sumber
void zap(char *restrict p1, char *restrict p2) { for (int i=0; i<50; i++) { p1[i] = 4; p2[i] = 9; } }
, batasan kualifikasi akan membiarkan kompiler menulis ulang kode sebagai "memset (p1,4,50); memset (p2,9,50);". Pembatasan jauh lebih unggul dari alias berbasis tipe; itu memalukan kompiler lebih fokus pada yang terakhir.__restrict
. Kalau tidak, double-underline mungkin disalahartikan sebagai indikasi bahwa Anda berteriak.