Issue #
Pass only used fields from derived data types as parameters to promote data hiding.
Relevance #
Derived data types (such as structs in C) are convenient constructs to group and move around related variables. While in many cases this is an effective method to organize data, the compilers can have a hard time optimizing this code because increased visibility of data also renders optimizations more complex.
Functions having derived data types used as parameters should make use of most if not all its fields. Ensuring that all fields from derived types passed as function parameters are used in the function body has several benefits: promotes data hiding, makes inputs and outputs more explicit, helps to prevent unintended variable modifications, and also contributes to improve compiler and static analyzer code coverage.
In parallel programming, derived data types are often discouraged when offloading to the GPU because they may inhibit compiler analyses and optimizations due to pointer aliasing. Also, it can cause unnecessary data movements impacting performance or incorrect data movements impacting correctness and even crashes impacting code quality.
Actions #
Pass the used fields as separate parameters instead of the whole derived type.
Code example #
In the following example, a struct containing two arrays is passed to the foo
function, which only uses one of the arrays:
#include <stdlib.h>
typedef struct
{
int A[1000];
int B[1000];
} data;
int foo(data *d)
{
int result = 0;
for (int i = 0; i < 1000; i++)
{
result += d->A[i];
}
return result;
}
void bar()
{
data *d = (data *)malloc(sizeof(data));
for (int i = 0; i < 1000; i++)
{
d->A[i] = d->B[i] = 1;
}
int result = foo(d);
}
This can be easily addressed by only passing the required array and rewriting the function body accordingly:
#include <stdlib.h>
typedef struct
{
int A[1000];
int B[1000];
} data;
int foo(int *A)
{
int result = 0;
for (int i = 0; i < 1000; i++)
{
result += A[i];
}
return result;
}
void bar()
{
data *d = (data *)malloc(sizeof(data));
for (int i = 0; i < 1000; i++)
{
d->A[i] = d->B[i] = 1;
}
int result = foo(d->A);
free(d);
}
Related resources #
- PWR012 examples at GitHub
- PWR001: Declare global variables as function parameters
- PWR002: Declare scalar variables in the smallest possible scope
- PWR008: Declare the intent for each procedure parameter
- PWD006: Missing deep copy of non-contiguous data to the GPU
References #
- Data Hiding in C – Stephen Friederichs [last checked October 2020]
- Information hiding [last checked October 2020]