Eu estava navegando através de alguma documentação e perguntas / respostas e vi isso mencionado. Eu li uma breve descrição, afirmando que seria basicamente uma promessa do programador de que o ponteiro não será usado para apontar para outro lugar.
Alguém pode oferecer alguns casos realistas em que vale a pena usá-lo?
c
gcc
c99
restrict-qualifier
user90052
fonte
fonte
memcpy
vsmemmove
é um exemplo canônico.restrict
argumentos qualificados para permitirmemcpy
que, em princípio, uma implementação ingênua seja otimizada de forma agressiva, e 2) a simples chamadamemcpy
permite que o compilador assuma que os argumentos dados a ele não possuem um alias, o que poderia permitir alguma otimização em torno damemcpy
chamada.memcpy(anything, anything, 0);
um no-op e garantir que sep
é um ponteiro para pelo menosn
bytes graváveismemcpy(p,p,n)
; não terá efeitos colaterais adversos. Tais casos podem surgir ... #Respostas:
restrict
diz que o ponteiro é a única coisa que acessa o objeto subjacente. Elimina o potencial de aliasing de ponteiro, permitindo uma melhor otimização pelo compilador.Por exemplo, suponha que eu tenha uma máquina com instruções especializadas que possam multiplicar vetores de números na memória e que possua o seguinte código:
O compilador precisa manipular adequadamente se
dest
,src1
e sesrc2
sobrepõe, o que significa que deve fazer uma multiplicação por vez, do início ao fim. Por issorestrict
, o compilador pode otimizar esse código usando as instruções do vetor.A Wikipedia tem uma entrada aqui
restrict
, com outro exemplo, aqui .fonte
dest
sobrepor um dos vetores de origem. Por que haveria um problema sesrc1
e sesrc2
sobrepõem?O exemplo da Wikipedia é muito esclarecedor.
Mostra claramente como permite salvar uma instrução de montagem .
Sem restrição:
Pseudo montagem:
Com restringir:
Pseudo montagem:
O GCC realmente faz isso?
GCC 4.8 Linux x86-64:
Com
-O0
, eles são os mesmos.Com
-O3
:Para os não iniciados, a convenção de chamada é:
rdi
= primeiro parâmetrorsi
= segundo parâmetrordx
= terceiro parâmetroA saída do GCC foi ainda mais clara que o artigo da wiki: 4 instruções vs 3 instruções.
Matrizes
Até o momento, temos economia de instruções únicas, mas se o ponteiro representar matrizes a serem repetidas, um caso de uso comum, várias instruções podem ser salvas, como mencionado pelo supercat .
Considere, por exemplo:
Por causa disso
restrict
, um compilador inteligente (ou humano) pode otimizar isso para:que é potencialmente muito mais eficiente, pois pode ser otimizado para montagem em uma implementação decente da libc (como glibc): É melhor usar std :: memcpy () ou std :: copy () em termos de desempenho?
O GCC realmente faz isso?
GCC 5.2.1.Linux x86-64 Ubuntu 15.10:
Com
-O0
, ambos são iguais.Com
-O3
:com restringir:
Duas
memset
chamadas conforme o esperado.sem restrição: sem chamadas stdlib, apenas um loop de 16 iterações que eu não pretendo reproduzir aqui :-)
Não tive paciência para compará-los, mas acredito que a versão restrita será mais rápida.
C99
Vejamos o padrão por uma questão de integridade.
restrict
diz que dois ponteiros não podem apontar para regiões de memória sobrepostas. O uso mais comum é para argumentos de função.Isso restringe como a função pode ser chamada, mas permite mais otimizações em tempo de compilação.
Se o chamador não seguir o
restrict
contrato, comportamento indefinido.O projeto C99 N1256 6.7.3 / 7 "Qualificadores de tipo" diz:
e 6.7.3.1 "Definição formal de restrição" fornece detalhes sangrentos.
Regra estrita de alias
A
restrict
palavra-chave afeta apenas ponteiros de tipos compatíveis (por exemplo, doisint*
) porque as regras estritas de aliasing dizem que o aliasing de tipos incompatíveis é um comportamento indefinido por padrão, e assim os compiladores podem assumir que isso não acontece e otimizar.Veja: Qual é a regra estrita de alias?
Veja também
restrict
, mas o GCC possui__restrict__
uma extensão: o que a palavra-chave restringir significa em C ++?__attribute__((malloc))
, que diz que o valor de retorno de uma função não tem alias para nada: GCC: __attribute __ ((malloc))fonte
void zap(char *restrict p1, char *restrict p2) { for (int i=0; i<50; i++) { p1[i] = 4; p2[i] = 9; } }
, os qualificadores restritos permitiriam que o compilador reescrevesse o código como "memset (p1,4,50); memset (p2,9,50);". Restringir é muito superior ao aliasing baseado em tipo; é uma pena que os compiladores se concentrem mais nesse último.__restrict
. Caso contrário, os sublinhados duplos podem ser mal interpretados como uma indicação de que você está gritando.