In the C programming language, '''restrict''' is a keyword, introduced by the C99 standard, that can be used in pointer declarations. By adding this type qualifier, a programmer hints to the compiler that for the lifetime of the pointer, no other pointer will be used to access the object to which it points. This allows the compiler to make optimizations (for example, vectorization) that would not otherwise have been possible.
restrict 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.
Despite C++ supporting most features of C, restrict is not included in standard C++.
*ptrA += *val;
*ptrB += *val;
}
In the above code, the pointers ptrA, ptrB, and val might refer to the Pointer aliasing, so the compiler may generate less optimal code:
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
However, if the restrict keyword is used and the above function is declared as
then the compiler is allowed to assume that ptrA, ptrB, and val point to different locations and updating the memory location referenced by one pointer will not affect the memory locations referenced by 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.
The above assembly code is shorter because val is loaded only once. Without val being restricted, its possible for ptrA to alias with it. This would result in val changing on the first addition, when ptrA is updated, and thus needing to be reloaded. The above assembly code is also faster, since the compiler can rearrange the code more freely. 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. This reordering is enabled because pointers ptrA and ptrB were marked with restrict, which ensures that the user won't call the function with these pointers aliased, expecting val to be added twice to the integer they both refer to.
Note that the real generated code may have different behaviors, depending on how the compiler uses the additional information. The benefit with the above mini-example may be small, and in real-life cases large loops doing heavy memory access tends to be what is really helped by restrict.
As mentioned above, how incorrect code behaves is undefined. The compiler only ensures the generated code works properly if the code follows the declaration of intent, and the optimizations it enables will likely cause the code to behave incorrectly when the pointers are aliased. Compilers may detect and warn in some cases where pointers are aliased, but are not required to.
|
|