„Wenn ein Arbeiter seine Arbeit gut machen will, muss er zuerst seine Werkzeuge schärfen.“ – Konfuzius, „Die Gespräche des Konfuzius. Lu Linggong“
Titelseite > Programmierung > Das obskure Schlüsselwort „restrict“ in C

Das obskure Schlüsselwort „restrict“ in C

Veröffentlicht am 04.11.2024
Durchsuche:256

The Obscure “restrict” Keyword in C

Einführung

C99 fügte unter anderem das Schlüsselwort „restrict“ hinzu, um einem Programmierer die Möglichkeit zu geben, anzugeben, dass ein Zeiger der einzige Zeiger auf ein bestimmtes Objekt in einem Bereich ist, und dem Compiler folglich einen „Hinweis“ zu geben ” dass es zusätzliche Optimierungen durchführen kann, wenn über diesen Zeiger auf das Objekt zugegriffen wird.

Das Problem

Um das Problem zu veranschaulichen, das „Restrict“ lösen sollte, betrachten Sie eine Funktion wie:

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

für den der Compiler x86-64-Code wie folgt generiert:

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

Sie fragen sich vielleicht, warum Zeile 3 generiert wird, da sie mit Zeile 1 überflüssig zu sein scheint. Das Problem ist, dass der Compiler nicht wissen kann, dass Sie so etwas nicht getan haben:

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

In update_ptrs() würden p und v alias das gleiche int sein, daher muss der Compiler auf Nummer sicher gehen und davon ausgehen, dass sich der Wert von *v zwischen den Lesevorgängen ändern kann. daher die zusätzliche mov-Anweisung.

Im Allgemeinen verwirren Zeiger in C die Optimierung, da der Compiler nicht wissen kann, ob sich zwei Zeiger gegenseitig aliasen. Bei leistungskritischem Code könnte das Eliminieren von Speicherlesevorgängen ein großer Gewinn sein wenn der Compiler dies sicher tun könnte.

Die Lösung

Um das oben genannte Problem zu lösen, wurde „restrict“ zu C hinzugefügt, damit Sie angeben können, dass ein bestimmter Zeiger der

einzige Zeiger auf ein Objekt im Gültigkeitsbereich des Zeigers ist, d. h. kein anderer Zeiger im gleichen Bereich Aliase Es.

Um „restrict“ zu verwenden, fügen Sie es

zwischen dem * und dem Namen des Zeigers in einer Deklaration ein. Eine zur Verwendung von „restrict“ umgeschriebene update_ptrs() wäre:

void update_ptrs_v2( int *restrict p, int *restrict q, int const *restrict v ) { *p = *v; *q = *v; }
void update_ptrs_v2( int *restrict p, int *restrict q,
                     int const *restrict v ) {
  *p  = *v;
  *q  = *v;
}
(Lesen von rechts nach links, z. B. ist v ein eingeschränkter Zeiger auf eine Konstante int; oder verwenden Sie cdecl.)

Durch das Hinzufügen von „restrict“ kann der Compiler jetzt Code generieren wie:


mov eax, [rdx] ; tmp = *v füge [rdi], eax hinzu; *p = tmp füge [rsi], eax hinzu; *q = tmp
void update_ptrs_v2( int *restrict p, int *restrict q,
                     int const *restrict v ) {
  *p  = *v;
  *q  = *v;
}
Jetzt konnte der Compiler die vorherige Zeile 3 der zusätzlichen mov-Anweisung eliminieren.

Das vielleicht bekannteste Beispiel für die Verwendung von Restrict ist die Standardbibliotheksfunktion memcpy(). Dies ist der schnellste Weg, einen Teil des Speichers zu kopieren,

wenn sich die Quell- und Zieladressen nicht überschneiden. Die etwas langsamere memmove()-Funktion existiert für den Einsatz, wenn sich die Adressen do überschneiden.

Fallstricke

Der Missbrauch von „restrict“ führt zu undefiniertem Verhalten, beispielsweise durch die Übergabe von Zeigern, die sich gegenseitig als Alias ​​an update_ptrs_v2() oder memcpy() ausführen. In

einigen Fällen kann der Compiler Sie warnen, aber nicht in allen Fällen. Verlassen Sie sich also nicht darauf, dass der Compiler Missbrauch erkennt. Beachten Sie, dass die Einschränkung für einen bestimmten Bereich gilt. Das Zuweisen eines eingeschränkten Zeigers zu einem anderen im selben Bereich

führt zu undefiniertem Verhalten:

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

Sie können jedoch problemlos einen eingeschränkten Zeiger einem uneingeschränkten Zeiger zuweisen:
void f( int *restrict d, int *restrict s ) {
  int *restrict p = s;    // undefined behavior

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

Auch wenn p uneingeschränkt ist, kann der Compiler dennoch dieselben Optimierungen durchführen.
void f( int *restrict d, int *restrict s ) {
  int *p = s;             // OK
Es ist auch in Ordnung, einen eingeschränkten Zeiger in einem inneren Bereich einem anderen in einem äußeren Bereich zuzuweisen (aber nicht umgekehrt):

void f( int *restrict d, int *restrict s ) { { // innerer Bereich int *restrict p = s; // OK // ... s = p; // undefiniertes Verhalten } }

Wann (und wann nicht) die Verwendung eingeschränkt werden sollte
void f( int *restrict d, int *restrict s ) {
  int *p = s;             // OK
Zuerst sollten Sie auf jeden Fall ein Profil Ihres Codes erstellen (und sich vielleicht sogar den generierten Assembler-Code ansehen), um zu sehen, ob die Verwendung von „Restrict“ tatsächlich zu einer

erheblichen

Leistungsverbesserung führt, um das Risiko potenzieller Fallstricke zu rechtfertigen. Die Diagnose von Fehlern, die durch den Missbrauch von Beschränkungen verursacht werden, ist

sehr schwierig. Zweitens ist es sicherer, wenn die Verwendung von „restrict“ auf die Implementierung einer Funktion beschränkt ist, bei der der Speicher, auf den über eingeschränkte Zeiger zugegriffen wird, von Sie

zugewiesen wurde. Zum Beispiel gegeben:

void sicherer( unsigned n ) { n = n % 2 != 0; // durch Aufrunden ausgeglichen werden int *const array = malloc( n * sizeof(unsigned) ); unsigned *restrict half_1st = array; unsigned *restrict half_2nd = array n/2; // ... free( array ); }

Der Code könnte sicher auf der ersten und zweiten Hälfte des Arrays arbeiten, da sie sich nicht überlappen (vorausgesetzt, Sie greifen nie auf half_1st[n/2] oder darüber hinaus zu).
void f( int *restrict d, int *restrict s ) {
  int *p = s;             // OK
Drittens ist die Verwendung von „restrict“ in den Parametern einer Funktion möglicherweise

weniger

sicher. Vergleichen Sie beispielsweise „safer()“ mit „update_ptrs_v2()“, wobei der

Aufrufer die Zeiger steuert. Soweit Sie wissen, hat der Anrufer es falsch verstanden und Zeiger auf diesen Alias ​​übergeben. Verschiedenes Nur

Zeiger auf Objekte (oder void) können mit „restrict:“ qualifiziert werden:

restrict int x; // Fehler: Objekt kann nicht eingeschränkt werden int beschränken *p; // Fehler: Zeiger auf Einschränkungsobjekt int (*restrict f)(); // Fehler: Zeiger auf Funktion Sie können „restrict“ für Strukturmitglieder verwenden, zum Beispiel:

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

besagt, dass Daten der einzige Zeiger auf diese Daten sind und dass links und rechts niemals auf denselben Knoten zeigen. Die Verwendung von „restrict“ für Strukturmitglieder ist jedoch sehr ungewöhnlich.

Schließlich hat C
struct node {
   void *restrict data;
   struct node *restrict left;
   struct node *restrict right;
};
Einschränkung. Warum nicht? Es gibt eine lange Antwort, aber die TL;DR-Version lautet:

Es kann eine Quelle schwer zu findender Fehler sein, die das C-Komitee nicht aus C importieren wollte. Cs verstärkte Verwendung von Zeigern, z. B. this, macht die sichere Verwendung von „restrict“ noch schwieriger.

  • Viele Compiler haben jedoch __restrict__ als Erweiterung.
  • Abschluss
In begrenzten Fällen kann die Verwendung von „Restrict“ zu Leistungsverbesserungen führen, es gibt jedoch mehrere erhebliche Fallstricke. Wenn Sie die Verwendung von „Restrict“ in Betracht ziehen, erstellen Sie zunächst ein Profil Ihres Codes.

Mit Bedacht verwenden.

Freigabeerklärung Dieser Artikel ist abgedruckt unter: https://dev.to/pauljlucas/the-obscure-restrict-keyword-in-c-2541. Bei Verstößen wenden Sie sich bitte an [email protected], um ihn zu löschen
Neuestes Tutorial Mehr>

Haftungsausschluss: Alle bereitgestellten Ressourcen stammen teilweise aus dem Internet. Wenn eine Verletzung Ihres Urheberrechts oder anderer Rechte und Interessen vorliegt, erläutern Sie bitte die detaillierten Gründe und legen Sie einen Nachweis des Urheberrechts oder Ihrer Rechte und Interessen vor und senden Sie ihn dann an die E-Mail-Adresse: [email protected] Wir werden die Angelegenheit so schnell wie möglich für Sie erledigen.

Copyright© 2022 湘ICP备2022001581号-3