16

If I got the C99 restrict keyword right, qualifying a pointer with it is a promise made that the data it references won't be modified behind the compiler's back through aliasing.

By contrast, the way I understand the const qualifier is as compiler-enforced documentation that a given object won't be modified behind the back of a human being writing code. The compiler might get a hint as a side effect, but as a programmer I don't really care.

In a similar way, would it be appropriate to consider a restrict qualifier in a function prototype as a requirement that the user ensures exclusive access ("avoid aliasing", or maybe something stronger) for the duration of the call? Should it be used as "documentation"?

Also, is there something to understand in the fact that restrict qualifies a pointer rather than the data it points to (as const does) ?

EDIT: I originally believed that restrict could have implications with threaded code, but this seems wrong so I remove references to threads from the question to avoid confusing readers.

4

6 回答 6

22

Chris Dodd has the correct description of the keyword. In certain platforms it can be very important for performance reasons, because it lets the compiler know that once it has loaded data through that pointer onto a register, it need not do so again. Without this guarantee, the compiler must reload the data through a pointer every time any other possibly-aliasing pointer is written through, which can cause a serious pipeline stall called a load-hit-store.

const and restrict are different concepts, and it is not the case that const implies restrict. All const says is that you will not write through that pointer within the scope of that function. A const pointer may still be aliased. For example consider:

int foo( const int *a, int * b )
{
   *b *= 2;
   return *a + *b; // induces LHS: *a must be read back immediately
                   // after write has cleared the store queue
}

While you cannot directly write to a in this function, it would perfectly legal for you to call foo like:

int x = 3;
foo( &x, &x );  // returns 12

restrict is a different guarantee: a promise that a != b in all calls to foo().

I've written about the restrict keyword and its performance implications at length, and so has Mike Acton. Although we talk about a specific in-order PowerPC, the load-hit-store problem exists on the x86 as well, but the x86's out-of-order execution makes that stall harder to isolate in a profile.

And just to emphasize: this is not an arcane or premature optimization, if you care about performance at all. restrict can lead to really significant speedups if used correctly.

于 2009-10-01T22:49:01.883 回答
14

The best 'intuition' to have about the restrict keyword is that its a guarantee (by the programmer to the compiler) that, for the lifetime of the pointer, memory accessed via that pointer will ONLY be accessed via that pointer and not via another pointer or reference or global address. So its important that its on a pointer as its a property of both the pointer and the memory, tying the two together until the pointer goes out of scope.

于 2009-10-01T22:29:46.537 回答
8

Most of what you know is wrong!

const does not guarantee that something won't change behind the compiler's back. All it does is stop you from writing to that spot. Something else might still be able to write to that location though, so the compiler canNOT assume it's constant.

As others have said, the restrict qualifier is about aliasing. In fact, during the first round of C standardization, there was a proposal for a "noalias" keyword. Unfortunately, the proposal was fairly poorly written -- it prompted the one and only time the Dennis Ritchie got involved during that process, when he wrote a letter that said something to the effect that "noalias must go. This is not open to negotiation."

Needless to say, 'noalias' didn't become part of C. When it came time to try again, the proposal was written enough better that restrict was included in the standard -- and even though noalias would probably have been a more meaningful name for it, that name was so tainted that I doubt anybody even considered trying to use it.

In any case, the primary intent of restrict is to tell the compiler that there will not be an alias of this item. One reason for this is to allow things to be stored in registers temporarily. For example, consider something like:

void f(int *a, int *b, int *c) { 
    for (int i=0; i<*a; i++)
        *b += c[i];
}

The compiler really wants to put i in a register, and load *a into a register, so when it comes time to decide whether to execute another iteration of the loop, it just compares the values in those to registers to each other. Unfortunately, it can't do that -- if somebody who used this function was completely insane, and called it with a==b, every time it writes to *b inside the loop, that new value is also the value of *a -- so it has to read *a from memory at every iteration of the loop, just in case whoever called it was completely insane. Using restrict tells the compiler it can generate code assuming that a and b will always be distinct, so writing to *a will never change *b (or vice versa).

于 2009-10-01T23:37:35.030 回答
5

Your understanding is largely correct. The restrict qualifier simply states that the data accessed by a so-qualified pointer is only accessed by that exact pointer. It applies to reads as wells as writes.

The compiler doesn't care about concurrent threads, it wasn't going to generate code any differently, and you may clobber your own data as you like. But it does need to know what pointer operations may change what global memory.

Restrict also carries with it an API warning to humans that a given function is implemented with the assumption of unaliased parameters.

No locking by the user is necessary as far as the compiler is concerned. It only wants to make sure it correctly reads data that was supposed to be clobbered, by code the compiler was supposed to generate, in case there is no restrict qualifier. Adding restrict frees it from that concern.

Finally, note that the compiler is likely to already be analyzing possible aliasing based on data types, at the higher optimization levels, so restrict is important mostly for functions with multiple pointers to the same type of data. You can take a lesson from this subject and make sure that any deliberate aliasing you do is done via a union.

We can see restrict in action:

void move(int *a, int *b) {     void move(int *__restrict a, int *__restrict b) {
    a[0] = b[0];                    a[0] = b[0];
    a[1] = b[0];                    a[1] = b[0];
}                               }
    movl    (%edx), %eax            movl    (%edx), %edx
    movl    %eax, (%ecx)            movl    %edx, (%eax)
    movl    (%edx), %eax            movl    %edx, 4(%eax)
    movl    %eax, 4(%ecx)

In the right column, with restrict, the compiler did not need to reread b[0] from memory . It was able to read b[0] and keep it in register %edx, and then just store the register twice to memory. In the left column, it didn't know if the store to a may have changed b.

于 2009-10-01T22:37:26.103 回答
1

Someone more familiar with the standard could probably give a better answer, but I'll give it a shot.

"Data won't be modified behind the compiler's back" sounds more like the opposite of "volatile" to me.

"const" means the data won't be modified in front of the programmer; that is, she can't modify the data through the signifier marked as "const" (I write "signifier" because in int const *pi, the name pi isn't const, but *pi is). The data might be modifiable via another signifier (non-const data can be passed to a function as const data, after all).

That "restrict" qualifies pointers is the key. Pointers are the only way to alias data in C, so they're the only way that you can access some piece of data via two different names. "restrict" is all about limiting data access to one access path.

于 2009-10-01T22:29:21.873 回答
1

This might be an example from an extremely narrow domain, but Altera's Nios II platform is a soft-core microcontroller that you can customize within an FPGA. Then, within the C source code for that micro, you can use a C-to-hardware tool to speed up inner loops using custom hardware, rather than in software.

In there, use of the __restrict__ keyword (which is the same as C99's restrict) allows the C2H tool to properly optimize the hardware acceleration of pointer operation in parallel instead of sequentially. At least in this case, the restrict is simply not meant for human consumption. See also Sun's page on restrict, where the first line says

Using the restrict qualifier appropriately in C programs may allow the compiler to produce significantly faster executables.

If anyone's interested in reading more on C2H, this PDF discusses optimizing C2H results. The section on __restrict__ is on page 20.

于 2009-10-01T22:29:45.087 回答