restrict

From Wikipedia, the free encyclopedia
Jump to navigation Jump to search

In the C programming language, as of the C99 standard, restrict is a keyword that can be used in pointer declarations. The restrict keyword is a declaration of intent given by the programmer to the compiler. It says that for the lifetime of the pointer, only the pointer itself or a value directly derived from it (such as pointer + 1) will be used to access the object to which it points. This limits the effects of pointer aliasing, aiding optimizations. If the declaration of intent is not followed and the object is accessed by an independent pointer, this will result in undefined behavior. The use of the restrict keyword in C, in principle, allows non-obtuse C to achieve the same performance as the same program written in Fortran.[1]

C++ does not have standard support for restrict, but many compilers have equivalents that usually work in both C++ and C, such as the GCC's and Clang's __restrict__, and Visual C++'s __restrict and __declspec(restrict).

Optimization[edit]

If the compiler knows that there is only one pointer to a memory block, it can produce better optimized code. For instance:

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

In the above code, the pointers ptrA, ptrB, and val might refer to the same memory location, so the compiler may generate less optimal code:

load R1  *val  ; Fetch from memory the value at address val
load R2  *ptrA ; Fetch from memory the value at address ptrA
add R2 += R1    ; Perform addition
store R2  *ptrA  ; Update the value in memory location at ptrA
load R2  *ptrB ; 'load' may have to wait until preceding 'store' completes
load R1  *val  ; Have to load a second time to ensure consistency
add R2 += R1
store R2  *ptrB

However, if the restrict keyword is used and the above function is declared as

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

then the compiler is allowed to assume that ptrA, ptrB, and val point to different locations and updating one pointer will not affect the other pointers. The programmer, not the compiler, is responsible for ensuring that the pointers do not point to identical locations. The compiler can e.g. rearrange the code, first loading all memory locations, then performing the operations before committing the results back to memory.

load R1  *val  ; Note that val is now only loaded once
load R2  *ptrA ; Also, all 'load's in the beginning ...
load R3  *ptrB
add R2 += R1
add R3 += R1
store R2  *ptrA  ; ... all 'store's in the end.
store R3  *ptrB

The above assembly code is shorter because val is loaded only once. Also, since the compiler can rearrange the code more freely, the compiler can generate code that executes faster. In the second version of the above example, the store operations are all taking place after the load operations, ensuring that the processor won't have to block in the middle of the code to wait until the store operations are complete.

References[edit]

  • "ISO/IEC 9899:TC2 Committee Draft" (PDF). ISO. May 6, 2005: 108–112. Retrieved 2008-12-22.
  1. ^ Ulrich Drepper (October 23, 2007). "Memory part 5: What programmers can do". What every programmer should know about memory. lwn.net. ...The default aliasing rules of the C and C++ languages do not help the compiler making these decisions (unless restrict is used, all pointer accesses are potential sources of aliasing). This is why Fortran is still a preferred language for numeric programming: it makes writing fast code easier. (In theory the restrict keyword introduced into the C language in the 1999 revision should solve the problem. Compilers have not caught up yet, though. The reason is mainly that too much incorrect code exists which would mislead the compiler and cause it to generate incorrect object code.)

External links[edit]