Skip to content

Data format

Every lattice field has a data format, f.data_format(), which says how the field's data is laid out in device memory and how floating-point numbers are stored there. A data format has two parts:

struct JitDataFormat
{
  JitDataOrder order   = JitDataOrder::SiteFast;   // or SiteSlow
  JitStorage   storage = JitStorage::Native;       // or Half
};

The data format is a property of the field's data, not of its type: a LatticeFermion stored in half precision is still a LatticeFermion and is used in expressions like any other. Scalars (OScalar) have no data format.

Order

JitDataOrder gives the order of the site index relative to the spin, color and complex indices on the device:

Order Device layout
SiteFast (default) the site index runs fastest: for each component, the values of all sites are contiguous ("structure of arrays")
SiteSlow the site index runs slowest: all components of a site are contiguous ("array of structures")

SiteFast gives coalesced memory access on GPUs and is the default.

Storage

JitStorage says how floating-point numbers are stored on the device:

Storage Device storage
Native (default) at the precision of the field's type: float for LatticeFermionF, double for LatticeFermionD
Half as 16-bit IEEE half-precision numbers

A half-precision field takes half the device memory of a single-precision field and a quarter of a double-precision one, and moving it costs correspondingly less memory bandwidth. Kernels read the stored half values, convert them to the precision of the field's type, and compute at that precision. When a kernel writes to a half-precision field, it rounds the result to the nearest half value (ties to even).

Half precision has an 11-bit significand, about 3 decimal digits, and a limited range:

Value stored Read back
1.0/3.0 0.333251953125
1000.3 1000.5
65504 (largest half value) 65504
70000 inf
1e-6 1.0132789611816406e-06 (subnormal)
1e-8 0

Values above 65504 become infinite. Scale data such as gauge links or normalized vectors so that it stays within range; isfinite(f) tells whether a field holds only finite values.

Integer and boolean fields (LatticeInteger, LatticeBoolean, …) are always stored natively, see below.

The default data format

New fields get the default data format unless the constructor takes the format from somewhere else (see Construction and copying). At start-up the default is SiteFast and Native.

The command-line option

-default-data-order-site-slow

changes the default order to SiteSlow. There is no command-line option for half storage; use a scope or a constructor argument.

Within a block, set the default with a JitDataFormatScope:

JitDataFormat half(JitDataOrder::SiteFast, JitStorage::Half);
{
  JitDataFormatScope scope(half);
  multi1d<LatticeColorMatrix> u_half(Nd);   // all stored in half precision
}

The current default is qdp_jit_config_get_default_data_format().

Creating a field with a given format

Pass the format to the constructor:

LatticeFermion psi_h(JitDataFormat(JitDataOrder::SiteFast, JitStorage::Half));

This ignores the default. Note that JitDataFormat(order) alone means native storage, and JitDataFormat() means SiteFast, Native whatever the default is.

Where a field's format comes from

Construction Data format
T f;, T f = zero;, T f(shape); the default
T f(format) format
T f(g), T f = g (copy) g's format
LatticeFermionD f(g) with g a LatticeFermionF g's format
T f(expr), T f = expr (expression) the default, whatever the formats of the leaves

Assignment never changes the destination's format: f = g converts g's values to f's format. Assigning a half-precision field to a native one gives the rounded values at native precision; assigning a native field to a half-precision one rounds them.

For multi1d of fields, copying keeps each element's format: multi1d<LatticeFermion> b(a) and assignment to an empty array c = a give elements with the formats of a's elements. Assigning to an array that already has elements, d = a with d of size 3, assigns element by element and keeps d's formats.

The full rules, together with those for the layout and the shape, are in Construction and copying.

Mixing formats in an expression

The leaves of an expression can have different data formats. Each leaf is read in its own format and the result is written in the destination's format:

LatticeColorMatrix u_h(JitDataFormat(JitDataOrder::SiteSlow, JitStorage::Half));
LatticeFermion     psi_h(JitDataFormat(JitDataOrder::SiteFast, JitStorage::Half));
u_h   = u;
psi_h = psi;

LatticeFermion chi = u_h * psi_h + shift(psi_h, FORWARD, 0);   // native result

The result is exactly what the same expression gives with native fields that hold the rounded values of u_h and psi_h. The same holds for shifts in both directions, for subsets (chi[rb[0]] += …), for coarse-grid fields and for reductions: norm2, innerProduct and globalMax of a half-precision field equal those of a native field holding the same rounded values.

Host access works as for native fields: peekSite returns, and pokeSite stores, values at the precision of the field's type, rounded to half on the way in.

Integer and boolean fields

Half storage applies only to fields with floating-point words. Under a JitDataFormatScope with half storage, integer and boolean fields get the scope's order with native storage. Passing a half format to the constructor of an integer or boolean field explicitly aborts:

OLattice: half storage requires a floating-point word type

Reference

JitDataFormat

Member Description
JitDataOrder order JitDataOrder::SiteFast or JitDataOrder::SiteSlow
JitStorage storage JitStorage::Native or JitStorage::Half
JitDataFormat() SiteFast, Native
JitDataFormat(JitDataOrder o, JitStorage s = JitStorage::Native) the given order and storage
bool half() const storage == JitStorage::Half
operator==, operator!= both parts equal

Default and scope

Function Description
JitDataFormat qdp_jit_config_get_default_data_format() the default format for new fields
void qdp_jit_config_set_default_data_format(JitDataFormat fmt) set it; prefer JitDataFormatScope
JitDataOrder qdp_jit_config_get_default_data_order() the order part of the default
void qdp_jit_config_set_default_data_order(JitDataOrder o) set only the order part
JitDataFormatScope(JitDataFormat fmt) set the default for the lifetime of the scope, see Scopes
-default-data-order-site-slow command-line option: default order SiteSlow

Fields

Function Description
f.data_format() the field's data format
explicit OLattice(JitDataFormat fmt) a field stored in fmt, on the current layout with the current shape