>  기사  >  백엔드 개발  >  C의 모호한 "restrict" 키워드

C의 모호한 "restrict" 키워드

WBOY
WBOY원래의
2024-09-11 06:36:02379검색

The Obscure “restrict” Keyword in C

pengenalan

Antara lain, C99 menambah kata kunci sekatan sebagai cara untuk pengaturcara menentukan bahawa penunjuk ialah sahaja penunjuk kepada objek tertentu dalam skop dan, akibatnya, berikan pengkompil "petunjuk ” bahawa ia boleh melakukan pengoptimuman tambahan apabila mengakses objek melalui penuding itu.

Masalahnya

Untuk menggambarkan masalah yang ingin diselesaikan sekatan, pertimbangkan fungsi seperti:

void update_ptrs( int *p, int *q, int const *v ) {
  *p += *v;
  *q += *v;
}

yang mana pengkompil akan menjana kod x86-64 seperti:

mov eax, [rdx]  ; tmp = *v   // 1
add [rdi], eax  ; *p += tmp
mov eax, [rdx]  ; tmp = *v   // 3
add [rsi], eax  ; *q += tmp

Anda mungkin tertanya-tanya mengapa ia menjana baris 3 kerana ia kelihatan berlebihan dengan baris 1. Masalahnya ialah pengkompil tidak dapat mengetahui bahawa anda tidak melakukan sesuatu seperti ini:

int x = 1, v = 2;
update_ptrs( &v, &x, &v );   // x = 5, v = 4

Dalam update_ptrs(), p dan v akan alias int sama, jadi pengkompil perlu memainkannya dengan selamat dan menganggap bahawa nilai *v boleh berubah antara bacaan, maka arahan mov tambahan.

Secara amnya, penunjuk dalam C mengelirukan pengoptimuman kerana pengkompil tidak dapat mengetahui sama ada dua penunjuk alias antara satu sama lain. Dalam kod kritikal prestasi, menghapuskan memori dibaca boleh menjadi kemenangan besar jika pengkompil boleh melakukannya dengan selamat.

Penyelesaian

Untuk menyelesaikan masalah yang disebutkan di atas, had telah ditambahkan pada C untuk membolehkan anda menentukan bahawa penunjuk yang diberikan ialah sahaja penunjuk kepada objek dalam skop penunjuk, iaitu, tiada penuding lain dalam alias skop yang sama ia.

Untuk menggunakan sekatan, anda memasukkannya antara * dan nama penuding dalam pengisytiharan. Update_ptrs() yang ditulis semula untuk menggunakan sekatan ialah:

void update_ptrs_v2( int *restrict p, int *restrict q,
                     int const *restrict v ) {
  *p += *v;
  *q += *v;
}

(Baca dari kanan ke kiri, cth., v ialah penunjuk terhad kepada int pemalar; atau gunakan cdecl.)

Dengan menambahkan sekatan, pengkompil kini boleh menjana kod seperti:

mov eax, [rdx]  ; tmp = *v
add [rdi], eax  ; *p += tmp
add [rsi], eax  ; *q += tmp

Kini, pengkompil dapat menghilangkan baris 3 sebelumnya bagi arahan mov tambahan.

Mungkin contoh paling terkenal di mana sekatan digunakan ialah fungsi perpustakaan standard memcpy(). Ini adalah cara terpantas untuk menyalin sebahagian daripada memori jika alamat sumber dan destinasi tidak bertindih. Fungsi memmove() yang sedikit perlahan wujud untuk digunakan apabila alamat lakukan bertindih.

Perangkap

Penyalahgunaan sekatan mengakibatkan tingkah laku yang tidak ditentukan, sebagai contoh, dengan memberikan petunjuk yang melakukan alias antara satu sama lain kepada update_ptrs_v2() atau memcpy(). Dalam beberapa kes, pengkompil boleh memberi amaran kepada anda, tetapi tidak dalam semua kes, jadi jangan bergantung pada pengkompil untuk menangkap penyalahgunaan.

Perhatikan bahawa sekatan adalah untuk skop tertentu. Menugaskan satu penuding terhad kepada yang lain dalam skop yang sama menghasilkan tingkah laku yang tidak ditentukan:

void f( int *restrict d, int *restrict s ) {
  int *restrict p = s;    // undefined behavior

Walau bagaimanapun, anda boleh menetapkan penunjuk terhad kepada penuding tidak terhad dengan baik:

void f( int *restrict d, int *restrict s ) {
  int *p = s;             // OK

Walaupun p tidak terhad, pengkompil masih boleh melakukan pengoptimuman yang sama.

Adalah OK untuk memberikan penuding terhad dalam skop dalaman kepada yang lain dalam skop luar (tetapi bukan sebaliknya):

void f( int *restrict d, int *restrict s ) {
  {                       // inner scope
    int *restrict p = s;  // OK
    // ...
    s = p;                // undefined behavior
  }
}

Bila (dan Bila Tidak) Gunakan sekatan

Pertama sekali, anda mesti memprofilkan kod anda (dan mungkin juga melihat pada kod pemasangan yang dijana) untuk melihat sama ada menggunakan sekatan benar-benar membuat peningkatan prestasi ketara untuk mewajarkan risiko kemungkinan perangkap. Mendiagnosis pepijat yang disebabkan oleh penyalahgunaan sekatan adalah sangat sukar dilakukan.

Kedua, jika penggunaan sekatan terhad kepada pelaksanaan fungsi di mana memori yang diakses melalui penunjuk terhad telah diperuntukkan oleh anda, maka ia adalah lebih selamat. Contohnya, diberikan:

void safer( unsigned n ) {
  n += n % 2 != 0;  // make even by rounding up
  int *const array = malloc( n * sizeof(unsigned) );
  unsigned *restrict half_1st = array;
  unsigned *restrict half_2nd = array + n/2;
  // ...
  free( array );
}

kod boleh beroperasi pada bahagian pertama dan kedua tatasusunan dengan selamat kerana ia tidak bertindih (dengan andaian anda tidak pernah mengakses separuh_1[n/2] atau seterusnya).

Ketiga, jika sekatan digunakan dalam parameter fungsi, maka ia berpotensi kurang selamat. Contohnya, bezakan safer() dengan update_ptrs_v2() di mana pemanggil mengawal penunjuk. Untuk pengetahuan semua anda, pemanggil mendapatnya salah dan memberikan petunjuk alias itu.

Macam-macam

Hanya penunjuk kepada objek (atau batal) boleh layak dengan sekatan:

restrict int x;       // error: can't restrict object
int restrict *p;      // error: pointer to restrict object
int (*restrict f)();  // error: pointer-to-function

Anda boleh menggunakan restrict untuk ahli struct, contohnya:

struct node {
   void *restrict data;
   struct node *restrict left;
   struct node *restrict right;
};

mengatakan bahawa data akan menjadi satu-satunya penunjuk kepada data itu dan kiri dan kanan itu tidak akan sekali-kali menghala ke nod yang sama. Walau bagaimanapun, penggunaan restrict untuk ahli struct adalah sangat jarang berlaku.

Akhir sekali, C++ tidak mempunyai sekatan. kenapa tidak Terdapat jawapan yang panjang, tetapi versi TL;DR ialah:

  • C++ 위원회가 C에서 가져오기를 원하지 않는 찾기 어려운 버그의 소스가 될 수 있습니다.
  • C++에서 포인터 사용이 늘어나면서 제한을 안전하게 사용하는 것이 더욱 어려워졌습니다.

그러나 많은 컴파일러에는 __restrict__ 확장 기능이 있습니다.

결론

제한된 경우에 제한을 사용하면 성능이 향상될 수 있지만 몇 가지 중요한 함정이 있습니다. 제한 사용을 고려하고 있다면 먼저 코드를 프로파일링하세요.

현명하게 사용하세요.

위 내용은 C의 모호한 "restrict" 키워드의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

성명:
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.