Frage

Ich war Surfen durch einige Unterlagen und Fragen / Antworten und sah es erwähnt. Ich las eine kurze Beschreibung, die besagen, dass es im Grunde ein Versprechen aus dem Programmierer sein würde, dass der Zeiger nicht verwendet werden, irgendwo anders zu zeigen.

Kann jemand einige realistische Fälle anbieten, wo es lohnt dies eigentlich mit?

War es hilfreich?

Lösung

restrict sagt, dass der Zeiger die einzige Sache ist, dass das zugrunde liegende Objekt zugreift. Sie eliminiert das Potenzial für Zeiger Aliasing, eine bessere Optimierung durch den Compiler ermöglicht.

Zum Beispiel: Angenommen ich eine Maschine mit speziellen Anweisungen, die Vektoren von Zahlen im Speicher vermehren kann, und ich habe den folgenden Code:

void MultiplyArrays(int* dest, int* src1, int* src2, int n)
{
    for(int i = 0; i < n; i++)
    {
        dest[i] = src1[i]*src2[i];
    }
}

Der Compiler ordnungsgemäß muss behandeln, wenn dest, src1 und src2 Überlappung, dh es zu einem Zeitpunkt eine Multiplikation tun muß, von Anfang bis zum Ende. Indem restrict, ist der Compiler diesen Code zu optimieren, indem die Vektorbefehle verwendet wird.

Wikipedia einen Eintrag auf restrict hat, mit einem anderen Beispiel hier .

Andere Tipps

Die Wikipedia Beispiel ist sehr zu beleuchten.

Es zeigt deutlich, wie es erlaubt eine Montageanleitung zu speichern.

Ohne beschränken:

void f(int *a, int *b, int *x) {
  *a += *x;
  *b += *x;
}

Pseudo assembly:

load R1 ← *x    ; Load the value of x pointer
load R2 ← *a    ; Load the value of a pointer
add R2 += R1    ; Perform Addition
set R2 → *a     ; Update the value of a pointer
; Similarly for b, note that x is loaded twice,
; because a may be equal to x.
load R1 ← *x
load R2 ← *b
add R2 += R1
set R2 → *b

Mit beschränken:

void fr(int *restrict a, int *restrict b, int *restrict x);

Pseudo assembly:

load R1 ← *x
load R2 ← *a
add R2 += R1
set R2 → *a
; Note that x is not reloaded,
; because the compiler knows it is unchanged
; load R1 ← *x
load R2 ← *b
add R2 += R1
set R2 → *b

Ist GCC es wirklich tun?

GCC 4.8 Linux x86-64:

gcc -g -std=c99 -O0 -c main.c
objdump -S main.o

Mit -O0, sind sie gleich.

Mit -O3:

void f(int *a, int *b, int *x) {
    *a += *x;
   0:   8b 02                   mov    (%rdx),%eax
   2:   01 07                   add    %eax,(%rdi)
    *b += *x;
   4:   8b 02                   mov    (%rdx),%eax
   6:   01 06                   add    %eax,(%rsi)  

void fr(int *restrict a, int *restrict b, int *restrict x) {
    *a += *x;
  10:   8b 02                   mov    (%rdx),%eax
  12:   01 07                   add    %eax,(%rdi)
    *b += *x;
  14:   01 06                   add    %eax,(%rsi) 

Für die Uneingeweihten, die Konvention Aufruf lautet:

  • rdi = erste Parameter
  • rsi = zweite Parameter
  • rdx = dritte Parameter

GCC Ausgang war noch deutlicher als der Wiki-Artikel: 4 Anweisungen vs 3 Anweisungen

.

Arrays

Bisher haben wir einzelne Anweisung Einsparungen, aber wenn Zeiger-Arrays darstellt geschlungen über zu, ein gemeinsamer Anwendungsfall, dann könnte eine Reihe von Anweisungen gespeichert werden, wie durch supercat .

Betrachten Sie zum Beispiel:

void f(char *restrict p1, char *restrict p2) {
    for (int i = 0; i < 50; i++) {
        p1[i] = 4;
        p2[i] = 9;
    }
}

Durch restrict, ein Smart-Compiler (oder Menschen), könnte zu optimieren, dass an:

memset(p1, 4, 50);
memset(p2, 9, 50);

, die möglicherweise sehr viel effizienter ist, da sie die Montage auf einer anständigen libc Implementierung optimiert werden können (wie glibc): Ist es besser std verwenden :: memcpy () oder std :: copy () in Bezug auf die Leistung?

Ist GCC es wirklich tun?

GCC 5.2.1.Linux x86-64 Ubuntu 15.10:

gcc -g -std=c99 -O0 -c main.c
objdump -dr main.o

Mit -O0, sind beide gleich.

Mit -O3:

  • mit beschränken:

    3f0:   48 85 d2                test   %rdx,%rdx
    3f3:   74 33                   je     428 <fr+0x38>
    3f5:   55                      push   %rbp
    3f6:   53                      push   %rbx
    3f7:   48 89 f5                mov    %rsi,%rbp
    3fa:   be 04 00 00 00          mov    $0x4,%esi
    3ff:   48 89 d3                mov    %rdx,%rbx
    402:   48 83 ec 08             sub    $0x8,%rsp
    406:   e8 00 00 00 00          callq  40b <fr+0x1b>
                            407: R_X86_64_PC32      memset-0x4
    40b:   48 83 c4 08             add    $0x8,%rsp
    40f:   48 89 da                mov    %rbx,%rdx
    412:   48 89 ef                mov    %rbp,%rdi
    415:   5b                      pop    %rbx
    416:   5d                      pop    %rbp
    417:   be 09 00 00 00          mov    $0x9,%esi
    41c:   e9 00 00 00 00          jmpq   421 <fr+0x31>
                            41d: R_X86_64_PC32      memset-0x4
    421:   0f 1f 80 00 00 00 00    nopl   0x0(%rax)
    428:   f3 c3                   repz retq
    

    Zwei memset Anrufe wie erwartet.

  • ohne einzuschränken: keine stdlib Anrufe, nur 16 Iteration breit Schleife Abrollen der ich beabsichtige nicht, hier zu reproduzieren: -)

Ich habe nicht die Geduld zu Benchmark sie habe, aber ich glaube, dass die Filterung der Version schneller sein wird.

C99

Lassen Sie sich auf Vollständigkeit halber auf dem Standard aus.

restrict sagt, dass zwei Zeiger nicht überlappende Speicherbereiche verweisen. Die häufigste Verwendung ist für Funktionsargumente.

Dies schränkt wie kann die Funktion aufgerufen werden, sondern ermöglicht eine Kompilierung-Optimierungen.

Wenn der Anrufer folgt nicht dem restrict Vertrag, nicht definiertes Verhalten.

Die C99 N1256 Entwurf 6.7. 3/7 "Typ-Qualifikation", sagt:

  

Die beabsichtigte Verwendung der Qualifier beschränken (wie die Speicherklasse Register) ist die Optimierung zu fördern, und das Löschen aller Instanzen der Qualifikation von allen Vorverarbeitung Übersetzungseinheiten ein konformes Programms Komponieren nicht seine Bedeutung ändern (dh beobachtbare Verhalten).

und 6.7.3.1 "formale Definition beschränkt" gibt die blutigen Details.

Strenge Aliasing Regel

Das restrict Schlüsselwort wirkt sich nur auf Zeiger kompatibler Typen (beispielsweise zwei int*), weil die strengen Aliasing Regeln besagt, dass inkompatible Typen Aliasing standardmäßig nicht definiertes Verhalten ist, und so kann Compiler annehmen, dass es nicht geschieht und optimieren entfernt.

Siehe: Was ist die strenge Aliasing-Regel

Siehe auch

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