Mastering Memory Alignment in C++
The Importance of Portability
When writing C++ code, it’s essential to ensure that our programs are portable across different platforms. One crucial aspect of portability is memory alignment, which can be a challenge to get right. In this article, we’ll explore the importance of memory alignment and how to achieve it using the std::align()
function.
Implementing a Utility Function
To write fully portable C++ code, we need to use std::align()
to check the alignment of an object. We can implement a small utility function called is_aligned()
to achieve this:
bool is_aligned(void* ptr, std::size_t alignment) {
assert(ptr!= nullptr);
assert(std::has_single_bit(alignment)); // Power of 2
auto s = std::numeric_limits<std::size_t>::max();
auto aligned_ptr = ptr;
std::align(alignment, 1, aligned_ptr, s);
return ptr == aligned_ptr;
}
This function takes a pointer and an alignment value as arguments and returns true
if the pointer is already aligned, and false
otherwise.
Memory Allocation and Alignment
When allocating memory using new
or std::malloc()
, the returned memory is guaranteed to be correctly aligned for the specified type. We can verify this using our is_aligned()
function:
auto* p = new int{};
assert(is_aligned(p, 4ul)); // True
In fact, new
and malloc()
always return memory that is suitably aligned for any scalar type.
The Role of std::max_align_t
The <cstddef>
header provides a type called std::max_align_t
, whose alignment requirement is at least as strict as all scalar types. This type is useful when writing custom memory allocators. Even if we only request memory for char
on the free store, it will be aligned suitably for std::max_align_t
.
Custom Alignment Requirements
We can specify custom alignment requirements that are stricter than the default alignment when declaring a variable using the alignas
specifier. For example:
alignas(64) int x{};
alignas(64) int y{};
// x and y will be placed on different cache lines
We can also specify custom alignment when defining a type:
struct alignas(64) CacheLine {
std::byte data[64];
};
By mastering memory alignment in C++, we can write more efficient and portable code that takes advantage of the underlying hardware architecture.