Aussie AI

Auto-Vectorization and Restricted Pointers

  • Book Excerpt from "Generative AI in C++"
  • by David Spuler, Ph.D.

Auto-Vectorization and Restricted Pointers

Modern C++ compilers attempt to automatically vectorize simple loops. Basic loop structures can be unrolled by optimizers, either partially or fully, and then sent to hardware acceleration automatically.

One of the most important hints to the compiler is a “restrict” designation on pointer variables. Ironically, the benefit of restrict is to limit what you can code, but also to allow unrestricted use of the pointers by the optimizer.

The purpose of the restrict attribute is a type specifier to tell the C++ compiler that a given pointer or array variable is not an “alias” for any other pointer. There are various loop transformations and vectorization optimizations that cannot be performed if the compiler has to be conservative and assume that aliasing could occur.

One of the main uses of restrict is on pointer or array function parameters, because arrays are pointers in this context. For example, if we have two function parameters (e.g. vector addition), declaring both parameters as restrict tells the compiler that the two pointers will never point to the other vector.

Note that this use of the word “aliasing” refers to two pointers referring to the same object or array (i.e. the pointers are aliases of each other). There is another unrelated but similar use of the term in C++ “aliases” for declarations, which means one function or type with two alias names.

The “restrict” keyword is merely a hint to the optimizer, and recalcitrant C++ compilers are free to ignore the advice. In fact, “restrict” isn't even valid C++, because it's part of C, but not yet in the C++ standard. Nevertheless, various compilers support it or similar extensions like __restrict__, so it can be used in C++ programs.

Restricted pointers don't always need to be marked as such. In some usages, the use of “const” can allow the compiler to infer non-aliasing of parameters, but it probably doesn't hurt to declare it with “restrict” as well. Note also that the C++ compiler is free to assume non-aliasing of pointers of different types, because it is undefined behavior if they are aliases. This is known as the “strict aliasing rule” and this assumption can be disabled in GCC via the option “-fno-strict-aliasing”.

The C++ compiler doesn't really check if you are lying (to yourself). If you tell the compiler that pointers are restricted, and then pass in two aliased pointers, the behavior of your program is “undefined” and there aren't likely to be any compilation errors or runtime warnings. So, don't do that.

The correct declaration of a “restrict” pointer is:

    int * restrict ptr;  // Correct

This is actually incorrect:

    int restrict * ptr;   // Wrong
    restrict int * ptr;   // Also wrong

The syntax for array parameters has the keyword inside the square brackets:

   void myfunc(int arr[restrict]);

Read-only functions. Note that read-only functions don't really need to use the restrict keyword. For example, the calculation of a vector dot product of two arrays doesn't really have an aliasing problem, since neither of the vectors are changed.

Restricted references. The “restrict” type specifier can be used on references, as well as pointers and arrays. This is helpful for some of the issues with aliasing between references in pass-by-reference function parameters. But this usage of restrict for references isn't very important for auto-vectorization optimizations.

Restricted “this” pointer. GCC also supports specifying that the class object “this” pointer is unaliased by marking the function body with the “__restrict__” keyword. This is placed after the closing right parenthesis of the function parameters (i.e. similar to a const member function declaration). The declaration looks like:

    void MyClass::myfunc(int x) __restrict__;

Portability of restrict. The portability of the “restrict” keyword is still somewhat lacking. The C99 standard added the “restrict” keyword to the C language, but it was not added to C++, and has not been widely supported by C++ compilers. The reason that restrict is standardized in C and not C++ is not an oversight; there are a large number of problematic issues in merging it into various C++ language features.

Nevertheless, various non-standard features have been added to C++ compilers and can improve their levels of vectorization optimizations. GCC has supported two different keywords, __restrict__ and __restrict, with the same intended meaning. MSVS has __declspec(restrict) which defines a restricted storage class, and also supports __restrict.

Some compilers have other related keywords for different types of aliasing. Microsoft also has __declspec(noalias) which has a slightly different meaning to restricted pointers, in that it tells the optimizer that a function does not modify global state in a hidden way, other than via its parameters, which is a different type of aliasing. GCC also has a “mayalias” attribute that permits a pointer to be aliased to suppress some warnings (and presumably also won't be optimized very much!).

 

Next:

Up: Table of Contents

Buy: Generative AI in C++: Coding Transformers and LLMs

Generative AI in C++ The new AI programming book by Aussie AI co-founders:
  • AI coding in C++
  • Transformer engine speedups
  • LLM models
  • Phone and desktop AI
  • Code examples
  • Research citations

Get your copy from Amazon: Generative AI in C++