restringir - restrict

Na linguagem de programação C , restricté uma palavra - chave que pode ser usada em declarações de ponteiro . Ao adicionar esse qualificador de tipo , um programador sugere ao compilador que, durante a vida útil do ponteiro, apenas o próprio ponteiro ou um valor diretamente derivado dele (como pointer + 1) será usado para acessar o objeto para o qual ele aponta.

restrictlimita os efeitos do aliasing do ponteiro , auxiliando nas otimizações . Se a declaração de intenção não for seguida e o objeto for acessado por um ponteiro independente, isso resultará em um comportamento indefinido . O uso desse qualificador de tipo permite que o código C obtenha o mesmo desempenho que o mesmo programa escrito em Fortran . Foi introduzido no padrão C99 .

C ++ não tem suporte padrão para restrict, mas muitos compiladores têm equivalentes que geralmente funcionam em C ++ e C, como GCC 's e Clang 's __restrict__, e Visual C ++ 's __declspec(restrict). Além disso, __restricté compatível com esses três compiladores. A interpretação exata dessas palavras-chave alternativas varia de acordo com o compilador:

  • Em compiladores de estilo Unix, como GCC e Clang, __restricte __restrict__significam exatamente o mesmo que sua contraparte C. As extensões incluem permitir que sejam aplicados a tipos de referência e this.
  • No Visual C ++, vários qualificadores sem alias são fornecidos:
    1. __declspec(restrict)aplica-se à declaração da função e sugere que o ponteiro retornado não tem um alias.
    2. __restricté usado no mesmo lugar que restrict, mas a dica sem alias não se propaga como em restrict. Ele também é estendido para tipos de união .

Otimização

Se o compilador souber que existe apenas um ponteiro para um bloco de memória, ele pode produzir um código melhor otimizado. Por exemplo:

void updatePtrs(size_t *ptrA, size_t *ptrB, size_t *val)
{
  *ptrA += *val;
  *ptrB += *val;
}

No código acima, os ponteiros ptrA, ptrBe val podem referir-se ao mesmo local de memória , portanto, o compilador pode gerar código menos ideal:

; Hypothetical RISC Machine.
ldr r12, [val]     ; Load memory at val to r12.
ldr r3, [ptrA]     ; Load memory at ptrA to r3.
add r3, r3, r12    ; Perform addition: r3 = r3 + r12.
str r3, [ptrA]     ; Store r3 to memory location ptrA, updating the value.
ldr r3, [ptrB]     ; 'load' may have to wait until preceding 'store' completes.
ldr r12, [val]     ; Have to load a second time to ensure consistency.
add r3, r3, r12
str r3, [ptrB]

No entanto, se a restrictpalavra-chave for usada e a função acima for declarada como

void updatePtrs(size_t *restrict ptrA, size_t *restrict ptrB, size_t *restrict val);

em seguida, o compilador é permitido supor que ptrA, ptrBe valapontam para diferentes locais e atualizar o local de memória referenciado por um ponteiro não afetará as posições de memória referenciados por outros ponteiros. O programador, não o compilador, é responsável por garantir que os ponteiros não apontem para locais idênticos. O compilador pode, por exemplo, reorganizar o código, primeiro carregando todos os locais da memória e, em seguida, executando as operações antes de comprometer os resultados de volta na memória.

ldr r12, [val]  ; Note that val is now only loaded once.
ldr r3, [ptrA]  ; Also, all 'load's in the beginning ...
ldr r4, [ptrB]
add r3, r3, r12
add r4, r4, r12
str r3, [ptrA]  ; ... all 'store's in the end.
str r4, [ptrB]

O código de montagem acima é mais curto porque valé carregado apenas uma vez. Além disso, como o compilador pode reorganizar o código com mais liberdade, ele pode gerar um código que executa mais rapidamente. Na segunda versão do exemplo acima, todas as storeoperações ocorrem após as loadoperações, garantindo que o processador não precise bloquear no meio do código para esperar até que as storeoperações sejam concluídas.

Observe que o código real gerado pode ter comportamentos diferentes. O benefício com o mini-exemplo acima tende a ser pequeno e, em casos da vida real, grandes loops que realizam um acesso pesado à memória tendem a ser o que realmente ajuda com a restrição.

Conforme mencionado acima, como o código incorreto se comporta é indefinido , o compilador apenas garante que o código gerado funcione corretamente se o código seguir a declaração de intenção.

Avisos do compilador

Para ajudar a evitar código incorreto, alguns compiladores e outras ferramentas tentam detectar quando argumentos sobrepostos foram passados ​​para funções com parâmetros marcados restrict. O CERT C Coding Standard considera o uso indevido restricte as funções de biblioteca marcadas com ele (EXP43-C) uma provável fonte de bugs de software, embora em novembro de 2019 nenhuma vulnerabilidade tenha sido causada por isso.

Referências

links externos