Boost.Multi
Introduction
Multi is a modern C++ library that provides manipulation and access of data in multidimensional arrays for both CPU and GPU memory.
Multidimensional array data structures are essential in various fields of computing, including data analysis, image processing, and scientific simulations. When combined with GPUs, they also play a significant role in Artificial Intelligence and Machine Learning.
In many simulations, space is divided into a grid, and values are stored at each grid cell. For example, arrays can represent fields of physical values, such as temperature variations within a block of material, or the velocity field of a fluid. The state of a game can be represented by placing pieces in a board made of squares. Arrays can also be used to numerical matrices, vectors, and more generally, tensors, to perform linear algebra operations. They can represent other abstract data structures, like networks, graph connections, or store data in multiple columns. Arrays are amenable to bulk processing in computing, and also to slicing, and reductions.
Here are 3 examples of arrays of different dimensionalities:
1D ├─0───┼─1───┼─2───┼ 2D ┼─0──┼─1──┼─2──┼ 3D ┼─0──┼─1──┼─2──┤ 4D, ... ND
| a | b | c | 0 f | g | h | 0 m | n | o |2──┤
└─────┴─────┴─────┘ ┼────┼────┼────┤ ┼────┼────┼────┤ u |
1 i | j | k | 1 p | q | r |───┤
┴────┴────┴────┘ ┴────┴────┴────┘ x |
┴────┴────┴────┘
Note that the depicted 3D case has only two planes, the first index (e.g. 1) selects a plane (e.g. bottom).
These depicted 1D, 2D, and 3D arrays containing letters and can be represented, within this library, as C++ objects declared as:
#include <boost/multi.hpp>
multi::array<char, 1> A1D = {'a', 'b', 'c'};
multi::array<char, 2> A2D = { {'f', 'g', 'h'}, {'i', 'j', 'k'} };
multi::array<char, 3> A3D = /*... nested list of letters (chars) */ ;
and their elements, when retrieved by their coordinates (indices) are such that, for example:
assert( A1D[1] == 'b' );
assert( A2D[1][0] == 'i' ); // A2D[1, 0] available since C++23
assert( A3D[1][0][2] == 'u' ); // A3D[1, 0, 2] available since C++23
The library features logical access recursively across dimensions and to elements through indices and iterators. The internal data structure layout (when mapped to computer memory) is stride-based, which makes it compatible with low-level C libraries.
The library interface is designed to be compatible with standard algorithms and ranges (STL) and special memory (including GPUs) and follows modern C++ design principles.
The following combination of features of this library are not present in the Standard or generally in other libraries:
-
Value semantics of multidimensional array containers and well-defined referential semantics of subarrays to avoid unnecessary copies if possible.
-
Availability of different access patterns to the elements in the multidimensional structure, as nested sequences or as a single sequence of elements. A D-dimensional array can be interpreted either as an (STL-compatible) sequence of (D-1)-dimensional subarrays or as a flattened one-dimensional (also STL-compatible) sequence of elements.
-
Interoperability with both legacy C and modern C++ libraries (e.g., STL, ranges, Thrust --CUDA and AMD GPUs--, Boost).
-
Memory management and allocation to exploit modern memory spaces, including GPU memory, mapped memory, and fancy pointers.
-
Lazy arrays and evaluation of array expressions well integrated with the rest of the library.
Do not confuse this library with
Boost.MultiArray
or with standard std::mdspan (accepted in C++23)
This library shares some of their goals and is compatible and complementary to them;
but it is designed at a different level of generality and with other priorities (such as the features listed above).
The code is entirely independent and has fundamental implementation and semantics differences.
For example, std::mdspan does not provide iterators, and it is not well integrated with the rest of the STL.
Problems such as deep assignment, or const-propagation are not fully solved, and ownership is delegated to a separate proposal std::mdarray (C++26).
The library’s primary concern is with the storage and logic structure of data. It doesn’t make specific algebraic or geometric assumptions about the arrays and their elements. (although they are still good building blocks for implementing on top of this mathematical and numerical algorithms, such as representing algebraic dense matrices in the 2D case, or tensors in the general case.)
The library does not throw exceptions and provides basic exception guarantees (such as no memory leaks) in their presence (e.g., thrown from allocations). Indexing errors and other logical errors result in undefined behavior, which this library attempts to reflect via assertions.
Requirements
Multi is a header-only library and C++17 or later is required.
The code requires any modern C++ compiler (or CUDA compiler) with standard C++17 support; for reference, (at least) any of: LLVM’s clang (5.0+) (libc++ and libstdc++), GNU’s g++ (7.1+), Nvidia’s nvcc (11.5+) and nvc++ (22.7+), Intel’s icpx (2022.0.0+) and icc (2021.1.2+, deprecated), Baxter’s circle (build 202+), Zig in c++ mode (v0.9.0+), Edison Design’s EDG (6.5+) and Microsoft’s MSVC (+14.1).
(Multi code inside CUDA kernel can be compiled with nvcc and with clang (in CUDA mode).
Inside HIP code, it can be compiled with AMD’s clang rocm (5.0+).)