Skip to main content

PWR016: Use separate arrays instead of an Array-of-Structs

Issue

Using separate arrays instead of an Array-of-Structs is recommended to maximize locality of reference.

Actions

Convert the Array-of-Structs (AoS) into separate plain arrays.

Relevance

Using an Array-of-Structs (AoS) leads to inefficient use of the memory subsystem unless data from all of its fields is used. One example case where using an AoS is justified would be iterating over an array of points, each point being a struct containing the coordinates that are consumed on each iteration. However, most structs contain fields that will not be accessed every time: data locality can be enhanced by breaking the struct and creating an array for each individual field.

Code example

C

The following example shows a loop processing the x and y coordinates for an array of points:

typedef struct {
int x;
int y;
int z;
} point;

void example() {
point points[1000];
for (int i = 0; i < 1000; i++) {
points[i].x = 1;
points[i].y = 1;
}
}

This could seem like an example where using an Array-of-Structs is justified. However, since the z coordinate is never accessed, the memory subsystem is not used optimally. This could be avoided by creating one array for each coordinate:

void example() {
int points_x[1000];
int points_y[1000];
int points_z[1000];
for (int i = 0; i < 1000; i++) {
points_x[i] = 1;
points_y[i] = 1;
}
}

Fortran

The following example shows a loop processing the x and y coordinates for an array of points:

program main
implicit none

type point
integer :: x
integer :: y
integer :: z
end type point

contains

subroutine foo()
implicit none
type(point) :: points(1000)
integer :: i

do i = 1, 1000
points(i)%x = 1
points(i)%y = 1
end do
end subroutine foo

end program main

This could seem like an example where using an Array-of-Structs is justified. However, since the z coordinate is never accessed, the memory subsystem is not used optimally. This could be avoided by creating one array for each coordinate:

subroutine foo()
implicit none
integer :: points_x(1000), points_y(1000), points_z(1000)
integer :: i

do i = 1, 1000
points_x(i) = 1
points_y(i) = 1
end do
end subroutine foo

References