Ich habe einige Dokumentationen und Fragen / Antworten durchgesehen und gesehen, dass sie erwähnt wurden. Ich las eine kurze Beschreibung und erklärte, dass es im Grunde ein Versprechen des Programmierers wäre, dass der Zeiger nicht verwendet wird, um auf eine andere Stelle zu zeigen.
Kann jemand einige realistische Fälle anbieten, in denen es sich lohnt, dies tatsächlich zu verwenden?
c
gcc
c99
restrict-qualifier
user90052
quelle
quelle
memcpy
vsmemmove
ist ein kanonisches Beispiel.restrict
Qualifizieren von Argumenten, ummemcpy
im Prinzip eine aggressive Optimierung einer naiven Implementierung zu ermöglichen, und 2) das bloße Aufrufenmemcpy
ermöglichen es dem Compiler anzunehmen, dass die ihm gegebenen Argumente keinen Alias darstellen, was eine gewisse Optimierung um denmemcpy
Aufruf herum ermöglichen könnte .memcpy(anything, anything, 0);
als No-Op angesehen werden und sicherstellen, dass ifp
ein Zeiger auf mindestensn
beschreibbare Bytes istmemcpy(p,p,n)
. wird keine nachteiligen Nebenwirkungen haben. Solche Fälle können auftreten ...Antworten:
restrict
sagt, dass der Zeiger das einzige ist, was auf das zugrunde liegende Objekt zugreift. Es beseitigt das Potenzial für Zeiger-Aliasing und ermöglicht eine bessere Optimierung durch den Compiler.Angenommen, ich habe eine Maschine mit speziellen Anweisungen, die Vektoren von Zahlen im Speicher multiplizieren können, und ich habe den folgenden Code:
Der Compiler Bedürfnisse richtig handhaben, wenn
dest
,src1
undsrc2
überlappen, was bedeutet es eine Multiplikation zu einem Zeitpunkt tun müssen, von Anfang bis zum Ende. Auf diese Weiserestrict
kann der Compiler diesen Code mithilfe der Vektoranweisungen optimieren.Wikipedia hat einen Eintrag auf
restrict
, mit einem anderen Beispiel hier .quelle
dest
sich einer der Quellvektoren überlappt. Warum sollte es ein Problem geben, wennsrc1
undsrc2
überlappen?Das Wikipedia-Beispiel ist sehr aufschlussreich.
Es zeigt deutlich, wie eine Montageanweisung gespeichert werden kann .
Ohne Einschränkung:
Pseudo-Assemblierung:
Mit Einschränkung:
Pseudo-Assemblierung:
Macht GCC das wirklich?
GCC 4.8 Linux x86-64:
Mit
-O0
sind sie gleich.Mit
-O3
:Für die Uneingeweihten lautet die aufrufende Konvention :
rdi
= erster Parameterrsi
= zweiter Parameterrdx
= dritter ParameterDie GCC-Ausgabe war noch deutlicher als der Wiki-Artikel: 4 Anweisungen gegen 3 Anweisungen.
Arrays
Bisher haben wir Einsparungen bei einzelnen Befehlen, aber wenn Zeiger Arrays darstellen, die durchlaufen werden sollen, ein häufiger Anwendungsfall, dann könnte eine Reihe von Befehlen gespeichert werden, wie von Supercat erwähnt .
Betrachten Sie zum Beispiel:
Wegen
restrict
, ein Smart - Compiler (oder Menschen), könnten diese optimieren:Dies ist möglicherweise viel effizienter, da es für eine anständige libc-Implementierung (wie glibc) für die Assembly optimiert werden kann: Ist es in Bezug auf die Leistung besser, std :: memcpy () oder std :: copy () zu verwenden?
Macht GCC das wirklich?
GCC 5.2.1.Linux x86-64 Ubuntu 15.10:
Mit
-O0
sind beide gleich.Mit
-O3
:mit einschränken:
Zwei
memset
Anrufe wie erwartet.ohne Einschränkung: keine stdlib-Aufrufe, nur eine 16 Iterationen breite Schleife, die ich hier nicht reproduzieren möchte :-)
Ich hatte nicht die Geduld, sie zu vergleichen, aber ich glaube, dass die eingeschränkte Version schneller sein wird.
C99
Schauen wir uns der Vollständigkeit halber den Standard an.
restrict
sagt, dass zwei Zeiger nicht auf überlappende Speicherbereiche zeigen können. Die häufigste Verwendung sind Funktionsargumente.Dies schränkt den Aufruf der Funktion ein, ermöglicht jedoch mehr Optimierungen zur Kompilierungszeit.
Wenn der Anrufer dem
restrict
Vertrag nicht folgt , undefiniertes Verhalten.Der C99 N1256 Entwurf 6.7.3 / 7 "Typqualifizierer" sagt:
und 6.7.3.1 "Formale Definition von Beschränkung" gibt die blutigen Details an.
Strikte Aliasing-Regel
Das
restrict
Schlüsselwort wirkt sich nur auf Zeiger kompatibler Typen aus (z. B. zweiint*
), da die strengen Aliasing-Regeln besagen, dass das Aliasing inkompatibler Typen standardmäßig ein undefiniertes Verhalten ist. Compiler können daher davon ausgehen, dass dies nicht der Fall ist, und optimieren.Siehe: Was ist die strenge Aliasing-Regel?
Siehe auch
restrict
, aber GCC hat__restrict__
als Erweiterung: Was bedeutet das Schlüsselwort einschränken in C ++?__attribute__((malloc))
, das besagt, dass der Rückgabewert einer Funktion auf nichts ausgerichtet ist: GCC: __attribute __ ((malloc))quelle
void zap(char *restrict p1, char *restrict p2) { for (int i=0; i<50; i++) { p1[i] = 4; p2[i] = 9; } }
würden die Einschränkungsqualifizierer den Compiler den Code als "memset (p1,4,50); memset (p2,9,50);" umschreiben lassen. Restrict ist typbasiertem Aliasing weit überlegen. Es ist eine Schande, dass sich Compiler mehr auf Letzteres konzentrieren.__restrict
. Andernfalls werden die doppelten Unterstreichungen möglicherweise falsch interpretiert, um anzuzeigen, dass Sie schreien.