Table of contents:
Issue
Pass only required 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, a poor design can make the program harder to reason about and make data accessible from more places than needed. This goes against data hiding best practices and in some cases it can also impact compiler and static analyzer code coverage.
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, cause unnecessary data movements impacting performance or incorrect data movements impacting correctness and even crashes impacting code quality.
Actions
Pass the required 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 consumes one of those 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]
Join the Early Access of Parallelware Analyzer
Register for free access to the latest versions and support until the official release.