setting the speed for the future of games programming
vectorc

contentsclose
 

ALIASING

Aliasing is a problem that affects all compilers and their ability to generate fast code.  It is a particularly serious problem when vectorizing, so if you want to make the best possible use of MMX, 3D Now! and SSE, then you will have to understand a bit about reducing the effect of aliasing on Vector C's code generator.

When source code makes 2 or more accesses to memory, the optimizer needs to know whether these accesses can be to the same memory or not, otherwise it is impossible for the optimizer to re-order or combine these memory accesses. Re-ordering and combining memory accesses is what VectorC needs to do combine more than one operation together into one instruction.

Here is an example:-

void fn_add (short *a, short *b, short *c)
    {
    for (i=0; i<100; i++)
        {
        a [i] = b[i] + c [i];
        }
    }
In this example, VectorC may be able to unroll this loop and parallelize the additions, but it doesn't know if the pointers 'a', 'b', and 'c' point to overlapping regions of the same data. If they did, then a write to the array 'a' would affect a later read of array 'b' or 'c'. If the reads and writes were re-ordered, this may not work the same way. It is impossible for VectorC to vectorize this loop without risking stopping your code from working. The solution to this problem is to use the 'restrict' keyword on the arguments of the function. This looks like:-
void fn_add (short restrict *a, short restrict *b,
             short restrict *c)
    {
    for (i=0; i<100; i++)
        {
        a [i] = b[i] + c [i];
        }
    }
This tells the compiler that a, b and c cannot possibly point to the same region of memory and so it is safe to re-order the additions, reads and writes. Of course, you should only do this if you are sure that a, b and c will never point to the same regions of memory. The 'restrict' keyword is not always supported by other compilers. Also it is possible within VectorC to use the 'restrict' keyword with pointers that are global variables, local variables or fields in structures.
 
The 'restrict' keyword means that data pointed to by a pointer cannot be accessed by any other pointer within the same function.  It also means that the pointer points to no variables that are accessed within the function.


Alternatively, you can use the command-line option '/noaliasarguments' (or '/vec:noaliasarguments' if you are using the Visual C compatibility mode). This option tells VectorC that you never call functions where 2 arguments could point to overlapping regions of memory. However, it is quite common to call functions with 2 identical pointer values, so this may not be the best solution.

Another, not so obvious case, is that of global variables. Consider the following function, similar to the one above, but this time 'a', 'b' and 'c' are a global variables.

int *a, *b, *c;

void fn_add (int n)
    {
    for (i=0; i<n; i++)
        {
        a [i] = b[i] + c [i];
        }
    }

In this case, it is perfectly possible that 'n' is 1 and 'a' points to itself or to 'b' or to 'c'. This may seem odd in this case, but if you wrote this and VectorC compiled code that does not work in this case, you would be upset. Therefore, the compiler must re-load the pointers 'a', 'b' and 'c' from memory every iteration through the loop. It also cannot vectorize or do the additions in parallel.

Again, you can put the 'restrict' keyword in the pointer declarations of 'a', 'b' and 'c'.
Or, you can add the '/noaliasatomicarithmetic' ('/vec:noaliasatomic' if you are using the Visual C compatibility mode). This option means that if you use an array access (e.g. 'a [i]') or pointer arithmetic ('*(a+i)'), then you are never accessing a variable with an atomic type (integer, float or pointer). This is quite a sensible command-line option that will help the optimizer throughout your code, but be carefull that you follow the rules. A good example of when the rule is broken is with a routine that treats a variable as an array of bytes.  In this case, make sure that the routine is in a file that doesn't have this command-line option set.

If a variable is not addressable (a local variable which is never used with &), then it cannot be aliased and so you do not have a problem. You may wish to copy values from global variables to local variables to make it easier for VectorC to allocate those values in registers.  If you use the 'register' keyword on a variable, VectorC will warn you if the address of that variable is used.

However, VectorC is actually capable of allocating global variables in registers temporarily and it can also work out simple aliasing problems. For example, it will have no aliasing problems with this loop:

int a [50], b [50], c [50];

void fn_add (int n)
    {
    int i;
    for (i=0; i<n; i++)
        {
        a [i] = b[i] + c [i];
        }
    }



Command-line aliasing options

There are a few options that can be applied on the command line to tell the compiler to assume certain types of aliasing do not occur in this source file. Use these options with care as they may break your code. However, it may be easier to use some of these options than to change large parts of code with the "restrict" keyword.

Command-line option Microsoft compatible option Effect
/noaliasarguments /vec:noaliasarguments No pointers in function arguments point to the same data. Described here.
/noaliasatomicarithmetic /vec:noaliasatomic No pointers to which arithmetic is applied can point to values with atomic type (integer, float, pointer). Described here.
/noalias /vec:noalias Assume all pointers have "restrict" applied to them. This will often not produce correct code, but can help with single functions to increase vectorization.
/nomemorder /vec:nomemorder Allow re-ordering of all memory accesses. This is likely to produce incorrect code, but can help with single functions to increase vectorization.


top

contentsclose