Frage

Nur Wundern:Wenn ich einschränken, um einen Zeiger, Sag ich dem compiler, dass der Zeiger ist nicht ein alias für einen anderen Zeiger.Nehmen wir an ich habe eine Funktion wie:

// Constructed example
void foo (float* result, const float* a, const float* b, const size_t size)
{
     for (size_t i = 0; i < size; ++i)
     {
         result [i] = a [0] * b [i];
     }
}

Wenn der compiler davon ausgehen muss, dass result vielleicht überschneidungen mit a, es hat refetch ein jedes mal.Aber, wie a gekennzeichnet ist const, der compiler könnte annehmen, dass eine fixiert ist, und daher Holen Sie es einmal ist in Ordnung.

Die Frage ist, in einer situation wie dieser, was ist der empfohlene Weg, um die Arbeit mit den Schränken?Ich sicherlich nicht wollen, dass der compiler refetch a jedes mal, aber ich konnte nicht finden Sie gute Informationen restrict soll, hier zu arbeiten.

War es hilfreich?

Lösung

Ihre Zeiger const, erzählen, jemand ruft Ihre Funktion, dass Sie nicht berühren Sie die Daten, die ist zeigte auf, dass durch variable.Leider ist der compiler immer noch nicht wissen, ob das Ergebnis ist ein alias, der const-Zeiger.Sie können verwenden Sie immer eine nicht-const-Zeiger als const-Zeiger.Für Beispiel, eine Menge von Funktionen nehmen Sie sich einen const char (d.h.string) Zeiger als parameter, aber Sie können, wenn Sie es wünschen, übergeben Sie es einer nicht-const-Zeiger, die Funktion wird nur machen Sie ein Versprechen, dass es nicht mehr verwenden, dass insbesondere die Zeiger, etwas zu ändern.

Im Grunde, um näher auf Ihre Frage, würden Sie brauchen, um hinzuzufügen, zu beschränken auf a und b, um "Versprechen" der compiler, dass wer nutzt diese Funktion nicht übergeben wird und im Ergebnis wie ein alias für a oder b.Vorausgesetzt natürlich, Sie sind in der Lage, ein solches Versprechen.

Andere Tipps

Jeder scheint hier sehr verwirrt.Es gibt nicht ein einziges Beispiel einen const-Zeiger in eine beliebige Antwort so weit.

Die Erklärung const float* a ist nicht ein const-Zeiger, es ist const-Speicher.Der Mauszeiger ist noch veränderbar. float *const a ein const-Zeiger auf eine veränderliche float.

So sollte die Frage, gibt es einen Punkt in float *const restrict a (oder const float *const restrict a wenn Sie es vorziehen).

Ja, Sie benötigen zu beschränken. Zeiger auf-const bedeutet nicht, dass Sie nichts ändern können die Daten, nur, dass Sie nicht ändern können Sie es durch diesen Zeiger.

const meist ist nur ein Mechanismus, um zu Fragen, den compiler zu helfen Sie halten track von die Sachen, die Sie wollen-Funktionen zu ändern. const ist nicht ein Versprechen an den compiler, dass eine Funktion, die sich nicht wirklich ändern von Daten.

Im Gegensatz zu restrict, mit Zeiger-auf-const für veränderliche Daten ist im Grunde ein Versprechen zu anderen Menschen, nicht der compiler.Gießen entfernt const alle über dem Platz, wird nicht führen zu falschen Verhalten der Optimierer (AFAIK), es sei denn, Sie versuchen, Sie zu ändern, etwas, das der compiler setzen, die in nur-lese-Speicher (siehe unten zu static const Variablen).Wenn der compiler Sie nicht sehen können, die definition einer Funktion bei der Optimierung, es muss davon ausgehen, dass er entledigt sich const und ändert die Daten, die durch Zeiger (d.h.dass die Funktion nicht respektieren constness seiner Zeiger args).

Der compiler weiß, dass static const int foo = 15; kann es nicht ändern, obwohl, und wird zuverlässig inline Wert, selbst wenn Sie übergeben Ihre Adresse zu unbekannten Funktionen.(Dies ist der Grund, warum static const int foo = 15; ist nicht langsamer als #define foo 15 für einen optimierten compiler.Gute Compiler optimieren Sie wie ein constexpr Wann immer möglich.)


Denken Sie daran, dass restrict ist ein Versprechen an den compiler, dass die Dinge, die Sie über diesen Zeiger nicht überlappen, die mit nichts.Wenn das nicht stimmt, Ihrer Funktion nicht unbedingt das tun, was Sie erwarten.z.B.don ' T call foo_restrict(buf, buf, buf) zu arbeiten in-place.

Meiner Erfahrung nach (mit gcc und clang), restrict ist vor allem nützlich, auf Hinweise, die Sie speichern durch.Es tut nicht weh zu setzen restrict auf dem Quell-Zeiger, auch, aber in der Regel erhalten Sie alle asm-Verbesserung möglich, wenn es nur auf das Ziel pointer(s), wenn alle der speichert Ihre Funktion wird durch restrict Zeiger.

Wenn Sie irgendeine Funktionsaufrufe in der Schleife, restrict auf eine Quelle Zeiger lässt clang (aber nicht gcc) vermeiden Sie ein zu laden.Finden diese test-Fälle auf der Godbolt compiler explorer, speziell diese:

void value_only(int);  // a function the compiler can't inline

int arg_pointer_valonly(const int *__restrict__ src)
{
    // the compiler needs to load `*src` to pass it as a function arg
    value_only(*src);
    // and then needs it again here to calculate the return value
    return 5 + *src;  // clang: no reload because of __restrict__
}

gcc6.3 (für die x86-64-SysV ABI) entscheidet, zu halten src (der Zeiger) in einem call-erhaltene register über die Funktion aufrufen und neu laden *src nach dem Aufruf.Entweder gcc-algorithmen nicht vor Ort, dass die Optimierung Möglichkeit, oder beschlossen, es war es nicht Wert, oder die gcc-Entwickler mit Absicht nicht umsetzen, weil Sie denken, es ist nicht sicher.IDK was.Aber da Klang es tut, ich vermute, es ist wahrscheinlich rechtliche nach der C11-standard.

clang4.0 optimiert diese auf nur laden *src einmal, und halten Sie den Wert in einem Anruf erhaltenen register über die Funktion aufrufen.Ohne restrict, es nicht tun, weil die aufgerufene Funktion kann (als Nebeneffekt) ändern *src durch die ein Zeiger zeigt.

Der Aufrufer dieser Funktion bestanden haben könnten, die Adresse einer globalen Variablen, zum Beispiel.Aber jede änderung *src anders als durch die src Zeiger würde gegen das Versprechen, dass restrict gemacht für den compiler.Da wir nicht gehen src zu valonly(), der compiler kann davon ausgehen, modifiziert er nicht den Wert.

Die GNU-Dialekt von C erlaubt die Verwendung __attribute__((pure)) oder __attribute__((const)) zu erklären, dass eine Funktion hat keine Nebenwirkungen, so dass diese Optimierung ohne restrict, aber es gibt keine portable äquivalent in ISO C11 (AFAIK).Natürlich, so dass die Funktion zu inline (indem es in einer header-Datei oder mit LTO) können auch diese Art der Optimierung, und ist viel besser für die kleinen Funktionen, vor allem, wenn inside loops.


Compiler sind in der Regel ziemlich aggressives zu tun Optimierungen, die der standard ermöglicht es, selbst wenn Sie für einige überraschend Programmierer und brechen einige der bestehenden unsicher-code, was passiert ist, um zu arbeiten.(C ist so tragbar, dass viele Dinge sind Undefiniertes Verhalten in der Basis-standard;die meisten schöne Implementierungen definieren das Verhalten von vielen Dingen, die die standard-Blätter als UB.) C ist nicht eine Sprache, wo es sicher ist, zu werfen code am compiler, bis es das macht, was Sie wollen, ohne zu prüfen, dass Sie tun es der richtige Weg (ohne signed-integer-overflows, etc.)


Wenn Sie einen Blick auf die x86-64-asm Ausgabe für die Erstellung Ihrer Funktion (aus der Frage), können Sie leicht den Unterschied sehen.Ich habe es auf die Godbolt compiler explorer.

In diesem Fall setzen restrict auf a ausreichend ist, lassen clang Hebemaschine, die die Last des a[0], aber nicht gcc.

Mit float *restrict result, sowohl clang und gcc hissen der Last.

z.B.

# gcc6.3, for foo with no restrict, or with just const float *restrict a
.L5:
    vmovss  xmm0, DWORD PTR [rsi]
    vmulss  xmm0, xmm0, DWORD PTR [rdx+rax*4]
    vmovss  DWORD PTR [rdi+rax*4], xmm0
    add     rax, 1
    cmp     rcx, rax
    jne     .L5

vs.

# gcc 6.3 with   float *__restrict__ result
# clang is similar with const float *__restrict__ a but not on result.
    vmovss  xmm1, DWORD PTR [rsi]   # outside the loop
.L11:
    vmulss  xmm0, xmm1, DWORD PTR [rdx+rax*4]
    vmovss  DWORD PTR [rdi+rax*4], xmm0
    add     rax, 1
    cmp     rcx, rax
    jne     .L11

So in der Zusammenfassung, setzen __restrict__ auf allen Zeigern, die sind garantiert nicht zu überschneidungen mit etwas anderes.


BTW, restrict ist nur ein Schlüsselwort in C.Einige C++ - Compiler unterstützen __restrict__ oder __restrict als eine Erweiterung, so sollten Sie #ifdef es entfernt auf unbekannten Compiler.

Da

In der C-99-Standard (ISO/IEC 9899:1999 (E)) es gibt Beispiele von const * restrict, z.B. , in Kapitel 7.8.2.3:

Die strtoimax und strtoumax Funktionen

Synopsis

#include <inttypes.h>
intmax_t strtoimax(const char * restrict nptr,
                   char ** restrict endptr, int base);
--- snip ---

Daher, wenn man davon ausgeht, dass die Norm wäre so ein Beispiel, wenn const * überflüssig zu * restrict, dann sind Sie in der Tat, nicht redundant.

Als der vorherigen Antwort erwähnt, müssen Sie "einschränken".Ich wollte auch einen Kommentar zu Ihrem Szenario, dass "dies möglicherweise überschneidungen mit ein".Das ist nicht der einzige Grund, warum der compiler erkennt, dass "ein" ändern könnte.Es könnte auch geändert werden, durch einen anderen thread, der eine Zeiger auf "ein".Also, auch wenn sich Ihre Funktion ändert sich nicht, alle Werte, die der compiler wird immer noch davon ausgegangen, dass "ein" ändern könnte.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top