Use cache-line-aware storage.

Flyology_Cachelines separates independently written values and groups values that share one owner. The standalone crate does not use the Flyology runtime.

CACHE 01

Add the standalone crate.

Add the Flyology organization index before the community index. Then add flyology_cachelines as a normal Alire dependency.

Add flyology_cachelines
alr index --reset-community
alr index --add=git+https://github.com/flyology-ada/alire-index.git \
  --name=flyology --before=community
alr with flyology_cachelines
alr build

The crate accepts GNAT 13 through 16. Its Alire manifest makes it available only on Linux and macOS.

The dependency does not prepare a custom runtime. It also does not depend on the flyology crate.

CACHE 02

Understand the spacing policy.

A destructive-interference region is the minimum spacing that the crate uses between independent writers. The Destructive_Interference_Size constant gives this compile-time value in storage elements.

The spacing policy is not a runtime cache query. It can exceed the physical cache-line size when adjacent-line prefetch can cause interference. For example, x86-64 uses 128-byte spacing and commonly reports a 64-byte physical line.

CACHE 03

Choose a representation from ownership.

Identify which task or shard writes each value before you choose a type. Values with independent writers need separate regions. Values with one owner can share a group.

Flyology_Cachelines.Padded
Use one complete region for each independently written value.
Flyology_Cachelines.Padded_Groups
Put an explicit number of same-owner values in each region.
Flyology_Cachelines.Fitted_Groups
Compute the largest same-owner group that fits in one region.

Padding consumes more storage and can reduce cache locality. Group values when one task processes them together. Do not group independent writers to reduce memory use.

CACHE 04

Isolate one value.

Instantiate the Flyology_Cachelines.Padded generic with a definite element type. Its Padded type aligns the wrapped value and rounds the object size to complete regions. Adjacent elements in an array therefore start in separate regions.

Store two independently written counters
with Flyology_Cachelines.Padded;

procedure Counters is
   type Counter is mod 2 ** 64 with Atomic;
   package Isolated is new Flyology_Cachelines.Padded (Counter);

   Values : array (1 .. 2) of Isolated.Padded :=
     [others => Isolated.Create (0)];
begin
   Values (1).Value := Values (1).Value + 1;
end Counters;

The example uses the Create constructor to initialize each wrapper.

Padded_Size_In_Storage_Elements reports the wrapper's object size. Use it when you calculate the storage cost of padding.

CACHE 05

Group values with one owner.

Instantiate Padded_Groups when the application defines a fixed group length. The instantiation fails if the payload exceeds one region or the aligned group does not occupy exactly one region.

Instantiate Fitted_Groups when the group length must follow the compiled spacing and element representation. Elements_Per_Group reports the selected length. The instantiation fails if one element cannot fit in a region.

Create automatically fitted worker groups
with Flyology_Cachelines.Fitted_Groups;

type Counter is mod 2 ** 64 with Atomic;
package Worker_Counters is new
  Flyology_Cachelines.Fitted_Groups (Counter);

Values : aliased Worker_Counters.Grouped_Array :=
  Worker_Counters.Create
    (Element_Count => 10_003,
     Initial_Value => 0);

Values (Worker_Counters.Elements_Per_Group + 1) := 1;

The Create overload constructs a Grouped_Array with enough physical groups for Element_Count. It initializes every logical element with Initial_Value.

Each physical group can contain values that share cache lines. Assign one owner to each group, or synchronize all access within that group.

CACHE 06

Traverse grouped arrays.

Both group generics define a Grouped_Array. The type stores isolated physical groups and presents one flat logical sequence.

Flat indexing and standard Ada iteration skip unused elements in the final partial group. The physical group boundaries remain available through the Groups component.

Use standard iteration for constant or mutable traversal. The opt-in Fast_View uses GNAT's implementation-defined Iterable aspect for mutable traversal.

A container used by Fast_View must be aliased. Keep the limited view local to the traversal because its access discriminant refers to that container. Creating the view performs no dynamic allocation and copies no elements.

CACHE 07

Query the host cache.

Hardware_Cache_Line_Size, L1_Data_Cache_Size, and L1_Data_Cache_Slots report host information. The host is inspected on the first query rather than during package elaboration, so a program that never asks pays nothing for it. Later calls reuse that inspection.

Each query returns a Cache_Query_Result. Check Available before you read Value. A failed or malformed host query does not return a guessed cache size.

A host whose cores are not identical has no single L1 data-cache capacity. The crate groups such a host's cores into core classes, one per distinct cache geometry. Every cache query takes a Core_Class, and a query without one describes Fastest_Core_Class, the class the host ranks highest. Core_Class_Count reports how many classes exist.

Core_Class_Cores counts physical cores and Core_Class_CPUs counts logical ones. The two differ under simultaneous multithreading, where sibling CPUs share one core's L1 data cache.

L2_Cache_Size reports a class's level 2 capacity and L2_Sharing_Cores reports how many cores share one such cache. Read both together: L2 is rarely private, so its capacity is not a per-core budget. Divide by the sharing count before you size a per-core working set.

Core_Class_Ordering reports what ordered the classes. Host_Reported means the host published a performance rank, as macOS performance levels and the Linux per-CPU capacity values do. Inferred means the host published no rank, so the classes are ordered by descending L1 data-cache capacity; that holds on current hybrid parts and carries no meaning on a host whose classes differ some other way. Unordered means the order carries no information. Check this value before you rely on the rank.

Check a detected cache-line size
declare
   Line : constant Flyology_Cachelines.Cache_Query_Result :=
     Flyology_Cachelines.Hardware_Cache_Line_Size;
begin
   if Line.Available then
      Ada.Text_IO.Put_Line (Line.Value'Image);
   end if;
end;

Use Value_Or only when the application has an explicit fallback policy. The fallback does not become a detected result.

CACHE 08

Synchronize access separately.

Alignment and padding control storage placement only. They do not make an operation atomic, establish memory ordering, or protect a compound update.

Use atomic types, protected objects, or exclusive task ownership as required by the stored type and access pattern. Apply the same synchronization that an unpadded value would require.

CACHE 09

Test the crate and measure the layout.

The focused test script checks native cache queries, representation, alignment, and the five architecture-specific public specifications. It also confirms that groups which exceed one region fail to compile.

Run cachelines verification
./flyology_cachelines/scripts/test.sh
./flyology_cachelines/scripts/docs.sh

The benchmark crate compares compact, padded, and grouped layouts. Run it on the intended compiler, hardware, task count, and access pattern. Do not treat one result as a general performance claim.

Use the generated cachelines API reference for exact declarations. The crate's README and source contain the architecture table and benchmark definitions.