Pergunta

eu estava navegando através de alguma documentação e perguntas / respostas e vi que mencionou. Eu li uma descrição breve, afirmando que seria basicamente uma promessa de que o programador que o ponteiro não será usado para ponto em outro lugar.

Alguém pode oferecer alguns casos realistas, onde o que vale realmente usando isso?

Foi útil?

Solução

restrict diz que o ponteiro é a única coisa que acessa o objeto subjacente. Ele elimina a possibilidade de aliasing ponteiro, permitindo uma melhor optimização pelo compilador.

Por exemplo, suponha que eu tenho uma máquina com instruções especializadas que podem se multiplicar vetores de números na memória, e eu tenho o seguinte código:

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

As necessidades do compilador para tratar adequadamente se dest, src1 e sobreposição src2, o que significa que deve fazer uma multiplicação de cada vez, do início ao fim. Por ter restrict, o compilador é livre para otimizar este código usando as instruções do vetor.

A Wikipedia tem uma entrada sobre restrict, com outro exemplo, aqui .

Outras dicas

O Wikipedia exemplo é muito esclarecedora.

Ele claramente mostra como permite salvar uma instrução de montagem .

Sem restringir:

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

Com restringir:

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

O GCC realmente fazê-lo?

GCC 4.8 Linux x86-64:

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

Com -O0, eles são os mesmos.

Com -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) 

Para os não iniciados, o convenção de chamada é:

  • rdi = primeiro parâmetro
  • rsi = segundo parâmetro
  • rdx = terceiro parâmetro

saída GCC foi ainda mais claro que o artigo wiki:. 4 instruções vs 3 instruções

Arrays

Até agora, temos poupança de instrução individuais, mas se ponteiro representam matrizes para ser enrolada ao longo, um caso de uso comum, então um monte de instruções poderiam ser salvas, como mencionado por supercat .

Considere, por exemplo:

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

Devido restrict, um compilador inteligente (ou humano), pode optimizar a que:

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

que é potencialmente muito mais eficiente, uma vez que pode ser montagem otimizada em uma implementação decente libc (como glibc): é melhor usar std :: memcpy () ou std :: copy () em termos de desempenho?

O GCC realmente fazê-lo?

GCC 5.2.1.Linux x86-64 Ubuntu 15.10:

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

Com -O0, ambos são o mesmo.

Com -O3:

  • com restringir:

    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
    

    chamadas memset Dois conforme esperado.

  • sem restringir: nenhuma chamada stdlib, apenas a 16 iteração ampla circuito desenrolando que não tenho a intenção de reproduzir aqui: -)

Eu não tive a paciência de referência-los, mas eu acredito que a versão restringir será mais rápido.

C99

Vamos olhar o padrão para sermos mais completos.

restrict diz que dois ponteiros não pode apontar para sobreposição de regiões de memória. O uso mais comum é para os argumentos da função.

Isto restringe como a função pode ser chamado, mas permite mais otimizações de tempo de compilação.

Se o chamador não siga o contrato restrict, comportamento indefinido.

O C99 N1256 projecto 6.7. 3/7 "Tipo qualificadores" diz:

O uso pretendido do restringir o qualificador (como a classe de armazenamento registo) é promover a otimização e excluir todas as instâncias do qualificador de todas as unidades de tradução de pré-processamento que compõem um programa de conformidade não muda o seu significado (ie, comportamento observável).

e 6.7.3.1 "definição formal de restringir" dá os detalhes.

regra aliasing estrita

A palavra-chave restrict afeta somente ponteiros de tipos compatíveis (por exemplo, dois int*) porque as regras aliasing estritas diz que aliasing tipos incompatíveis é um comportamento indefinido por padrão, e assim por compiladores pode supor que isso não aconteça e otimizar distância.

Veja: Qual é a regra aliasing estrita

?

Veja também

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top