Best practices for performance

PWR003: Explicitly declare pure functions

Issue #

Mark pure functions as such to allow for optimizations. In computer programming, a pure function is a function that has the following properties:

  1. Its only side effect is returning a value. It does not modify any memory except for local variables.
  2. Its return value is the same for the same arguments. It does not depend on any system resource that may change between calls.

Some programming languages provide built-in keywords to declare properties about functions, such as Fortran pure keyword. Others need extensions implemented by compilers/tools, for instance the GNU C compiler const attribute (note that it also has a pure attribute not compliant with the definition presented here).

Relevance #

Explicitly declaring properties about functions called in the code provides valuable hints that can enable optimizations or simplify analysis. Determining whether a function is pure can be costly for a developer and even infeasible for automated analysis tools. 

One of the biggest challenges of parallel programming is to do data scoping correctly and decide on how to manage all the variables of the code properly. Data flow analysis can be simplified through the identification of pure functions, which are free of side effects. Thus, declaring functions as pure states that invoking those functions won’t introduce race conditions, which facilitates analysis both to developers and compilers/tools.

Actions #

Add the appropriate annotations to your code explicitly. For instance:

  • In the GCC compiler for the C programming language, use the keyword “__attribute__((const))”.
  •  In the Fortran programming language, use the built-in keyword “pure”.

Code example #

The example C code shown below has a function foo with calls to two functions. On the one hand, foo_pure is a pure function because it is free of side effects other than the returned value. On the other hand, foo_impure is not pure because it performs write operations on the memory locations written through a pointer-type argument of the function, which means that it has side effects.

#ifdef __GNUC__
  #define PURE __attribute__((const))
  #define PURE

PURE int foo_pure(int a, int b) {
  return a + b;

int foo_impure(int a, int *b) {
  *b = a + 1;
  return a + *b;

void bar() {
  int result[10];
  int b = 1;

  for (int i = 0; i < 10; i++) {
    result[i] = foo_pure(i, b);   // No side effects

  for (int i = 0; i < 10; i++) {
    result[i] = foo_impure(i, &b);  // Side effects on variable b

Related resources #

References #