IMPORTANT: To view this page as Markdown, append `.md` to the URL (e.g. /docs/manual/basics.md). For the complete Mojo documentation index, see llms.txt.
Skip to main content
Version: Nightly
For the complete Mojo documentation index, see llms.txt. Markdown versions of all pages are available by appending .md to any URL (e.g. /docs/manual/basics.md).

@align

The @align decorator specifies a minimum memory alignment for values of a struct type.

If you already work with low-level memory, SIMD, or GPUs, you can think of @align as a way to make alignment a property of the type itself, enforced by the compiler. If not, the short version is this: alignment controls where values are placed in memory, and some hardware requires or benefits from specific alignments.

Most Mojo code doesn't need explicit alignment. You only need @align when your types need placement at specific boundaries, such as when interacting with GPU buffers, using SIMD instructions, or avoiding cache-line contention in concurrent code.

Without @align, alignment requirements must be tracked manually and enforced at allocation sites. With @align, the requirement becomes part of the type, and the compiler ensures it's respected everywhere the type is used.

What alignment means​

Every byte in memory has an address. An address is N-byte aligned if its address is evenly divisible by N.

Examples:

  • 8-byte aligned addresses: 0, 8, 16, 24, etc.
  • 64-byte aligned addresses: 0, 64, 128, 192, etc.

Hardware often loads memory in fixed-size chunks, such as cache lines. When a value begins at an aligned address, it fits cleanly within those chunks. When it doesn't, hardware may need extra memory accesses, or may reject the access entirely.

Alignment affects where a value begins in memory, and it also affects how large the value is: Mojo rounds a struct's size up to a multiple of its alignment.

Basic usage​

Add @align(N) to your struct definition, where N is a positive power of 2 and the number represents the minimum required alignment in bytes:

from std.sys import align_of

@align(64)
struct CacheAligned:
var data: Int

def main():
print(align_of[CacheAligned]()) # Prints 64

In this example, CacheAligned is aligned to 64 bytes, even though Int normally requires only 8-byte alignment.

Determining alignment​

The actual alignment of a struct is the maximum of:

  • The value specified by @align(N), if present.
  • The struct's natural alignment (the maximum alignment of its fields).
  • The alignment requirements of any embedded aligned fields.

You can't reduce alignment below the natural alignment of the struct. The @align decorator specifies a minimum, not an override:

from std.sys import align_of

@align(4)
struct TryToReduce:
var x: Int # Int has 8-byte natural alignment

def main():
print(align_of[TryToReduce]()) # Prints 8

When a struct contains an aligned field, the outer struct inherits that alignment:

from std.sys import align_of

@align(64)
struct CacheAligned:
var x: Int

struct Container:
var aligned: CacheAligned
var other: Int

def main():
print(align_of[Container]()) # Prints 64

Stack and heap behavior​

Both stack and heap allocations respect @align:

from std.sys import align_of
from std.memory import alloc, dealloc

@fieldwise_init
@align(64)
struct CacheAligned:
var data: Int

def use_aligned():
# Stack allocation
var stack_value = CacheAligned(42)

# Heap allocation
var heap_alloc = alloc[CacheAligned]({count = 1})
dealloc(heap_alloc^)

You don't need to manually request alignment when allocating values of an aligned type.

Alignment and arrays​

The @align decorator guarantees alignment of the base address of a value, including the base pointer of an array. It pads the size of the struct to be a multiple of the alignment. Mojo lays out array elements using size_of[T]() which rounds up to a multiple of align_of[T]():

from std.sys import align_of, size_of
from std.memory import alloc, dealloc

@align(64)
struct CacheAligned:
var data: Int # 8 bytes

def demonstrate_array_stride():
var allocation = alloc[CacheAligned]({count = 4})
var arr = allocation.unsafe_ptr()

print(align_of[CacheAligned]()) # 64
print(size_of[CacheAligned]()) # 64

# All elements of arr are guaranteed to be 64-byte aligned

dealloc(allocation^)

Parameterized structs​

Alignment also works with parameterized structs. All instances of a parameterized type share the same alignment requirement. Because of this, under certain circumstances, you may find that the alignment isn't tuned by its types:

from std.sys import align_of

@fieldwise_init
@align(128)
struct AlignedType[T: Copyable & Deinitable]:
var value: Self.T

def main():
print(align_of[AlignedType[Int8]]()) # 128
print(align_of[AlignedType[Int64]]()) # 128

Compare this with an alignment of 4, where the maximum of the decorator value (N) and the type's natural alignment produces a different result:

from std.sys import align_of

@fieldwise_init
@align(4)
struct AlignedType[T: Copyable & Deinitable]:
var value: Self.T

def main():
print(align_of[AlignedType[Int8]]()) # 4
print(align_of[AlignedType[Int64]]()) # 8

Interaction with RegisterPassable​

When @align is present, single-field register-passable structs aren't flattened. This preserves the alignment requirement:

from std.sys import align_of, size_of

@align(32)
struct AlignedTrivial(RegisterPassable):
var value: Int

def main():
print(align_of[AlignedTrivial]()) # 32
print(size_of[AlignedTrivial]()) # 32

Requirements and errors​

Mojo's @align decorator has the following requirements:

  • The alignment value must be a positive power of 2.
  • The maximum supported alignment is 2^29 bytes.
  • The value must be known at compile time.
  • The decorator requires exactly one argument.

Invalid uses produce compile-time errors:

@align(0)
struct Bad1:
var x: Int

@align(3)
struct Bad2:
var x: Int

@align(1073741824)
struct Bad3:
var x: Int

@align
struct Bad4:
var x: Int

@align(64, 128)
struct Bad5:
var x: Int

@align("64")
struct Bad6:
var x: Int

Special case: @align(1)​

Using @align(1) is valid and produces no warning. It doesn't reduce alignment below the natural alignment of the struct. This can be useful as a fallback value in parametric code:

from std.sys import align_of

@align(1)
struct MinimalAlign:
var x: Int

def main():
print(align_of[MinimalAlign]()) # Prints 8

Real-world example: hardware descriptors​

Some hardware accelerators require aligned descriptors for correctness. For example, NVIDIA's Tensor Memory Accelerator requires 64-byte aligned descriptors.

Before @align, this required explicit allocation tricks:

# Verbose and error-prone
var tensormap = my_custom_stack_allocation[1, TensorMap, alignment=64]()[0]

With @align, the type encodes your alignment requirement:

@align(64)
struct TensorMap:
# Descriptor fields
pass

var tensormap = TensorMap()
var heap_tensormap = alloc[TensorMap]({count = 1})

Alignment is enforced automatically everywhere the type is used.

Both stack and heap allocations respect @align.

Parametric alignment​

The alignment value can also be a struct parameter, enabling parameterized aligned types:

from std.sys import align_of

@align(Self.alignment)
struct AlignedBuffer[alignment: Int]:
var data: Int

def main():
print(align_of[AlignedBuffer[64]]()) # Prints 64
print(align_of[AlignedBuffer[128]]()) # Prints 128

The alignment is validated when the struct is instantiated, so invalid values like AlignedBuffer[3] will produce a compile-time error.