Views and Items#
What is a View, and why is Item register-resident?
A View is a lightweight, non-owning handle over memory that lets you
index it like a small tensor — v(component, sample) — while the bytes
themselves live somewhere else (a Chunk, or any raw pointer you hand
it); an Item is the opposite number, a small, all-static-shape value
(one vector or matrix) that the compiler is free to keep entirely in a
thread’s own registers, with no memory address at all.
View: a typed, zero-copy tensor#
aether::View (aether/view/View.h) wraps a pointer plus a shape
(aether::extents, aether/layout/Extents.h) plus a layout mapping
(aether/layout/Layout.h) that turns a multi-index into a flat offset.
It owns nothing and copies nothing — building one is just arithmetic. You
build one with aether::make_view<T, Es..., aether::dyn>(...) over a
Chunk or a raw pointer (see Memory ownership: Chunk, Array, View); the trailing
aether::dyn mode is the dynamic SAMPLE dimension every batched view
carries.
The default layout, aether::layout_right, is what makes a View
structure-of-arrays (SoA): for a 3-component view over N samples,
v(c, i) maps to flat offset c*N + i — every component’s N
samples are stored contiguously, one component block after another,
rather than one sample’s 3 components interleaved (array-of-structures).
That layout is what lets a GPU kernel read the SAME component from many
threads’ samples in one coalesced memory transaction.
Item: a register-resident value#
aether::Item<T, Es...> (aether/view/Item.h) is what one sample’s
worth of data materializes into: Item<double,3> (aliased Vec3d) is
a 3-vector, Item<double,3,3> (Mat33d) a 3x3 matrix. Every extent in
Es... must be static — no aether::dyn mode is allowed, because an
Item never carries a batch dimension; that is exactly what makes it
small and shape-known enough for a compiler to keep it in registers rather
than spilling it to memory, on host or device alike. Reading v[i].get()
off a View at one SampleIndex is how you turn a batched sample into
a register-resident Item to work with directly.
A runnable example#
The SoA layout claim above is not just a description — it is a checkable
identity, &v(c, i) - v.data() == c*N + i, over a full sweep of every
component and sample:
TEST_F(ViewTest, SoaProofPointerArithmeticMatchesClosedFormOverAFullSweep)
{
// THE SoA PROOF: &v(c,i) - v.data() == c*N + i.
constexpr std::size_t C = 3, N = 11;
auto chunk = aether::Chunk::allocate(aether::Device(kDLCPU), C * N * sizeof(double));
auto v = aether::make_view<double, C, aether::dyn>(chunk, N);
for (std::size_t c = 0; c < C; ++c) {
for (std::size_t i = 0; i < N; ++i) {
EXPECT_EQ(&v(c, i) - v.data(), static_cast<std::ptrdiff_t>(c * N + i));
}
}
}
From here, Expression templates covers what happens when you combine
several Views/Items with +/-/* instead of reading
them one at a time.