A C++20 array and expression template library with some J/APL features
![]() |
před 1 rokem | |
---|---|---|
.github | před 1 rokem | |
bench | před 1 rokem | |
box | před 1 rokem | |
config | před 1 rokem | |
docs | před 1 rokem | |
examples | před 1 rokem | |
ra | před 1 rokem | |
test | před 1 rokem | |
.gitattributes | před 4 roky | |
CMakeLists.txt | před 5 roky | |
LICENSE | před 9 roky | |
README.md | před 1 rokem | |
SConstruct | před 5 roky | |
TODO | před 1 rokem |
ra-ra is a C++20, header-only multidimensional array library in the spirit of Blitz++.
Multidimensional arrays can be indexed in multiple dimensions. For example, vectors can be seen as rank 1 and matrices are arrays of rank 2. C has built-in multidimensional array types, but even in modern C++ there's very little you can do with those, and anything practical requires a separate library.
ra-ra implements expression templates. This is a C++ technique (pioneered by Blitz++) to delay the execution of expressions involving large array operands, and in this way avoid the unnecessary creation of large temporary array objects.
ra-ra is small (about 6k loc), easy to extend, and generic — there are no arbitrary type restrictions or limits on rank or argument count.
In this example (examples/readme.cc), we add each element of a vector to each row of a matrix, and then print the result.
#include "ra/ra.hh"
#include <iostream>
int main()
{
ra::Big<float, 2> A {{1, 2}, {3, 4}}; // compile-time rank, dynamic shape
A += std::vector<float> {10, 20}; // rank-extending op with STL object
std::cout << "A: " << A << std::endl; // shape is dynamic, so it will be printed
}
⇒
A: 2 2
11 12
23 24
Please check the manual online at lloda.github.io/ra-ra, or have a look at the examples/ folder.
ra-ra offers:
<ranges>
.where
with bool selector, or pick
with integer selector).constexpr
is suported as much as possible. For example:
constexpr ra::Small<int, 3> a = { 1, 2, 3 };
static_assert(6==ra::sum(a));
Performance is competitive with hand written scalar (element by element) loops, but probably not with cache-tuned code such as your platform BLAS, or with code using SIMD. Please have a look at the benchmarks in bench/.
The library itself is header-only and has no dependencies other than a C++20 compiler and the standard library.
The test suite in test/ runs under either SCons (CXXFLAGS=-O3 scons
) or CMake (CXXFLAGS=-O3 cmake . && make && make test
). Running the test suite will also build and run the examples (examples/) and the benchmarks (bench/). You can also build each of these separately. The performance of ra-ra depends heavily on the optimization level, so although the test suite should pass with -O0
, that can take a long time.
RA_USE_BLAS=1
in the environment.-fsanitize=address
by default, and this can cause significant slowdown. Disable by passing -fno-sanitize=address
to the compiler.ra-ra requires support for -std=c++20
, including <source_location>
. The most recent versions tested are:
f632865f35fefe9e8cf00558cdc75e17f9fb9c2a
(-std=c++2b
)f632865f35fefe9e8cf00558cdc75e17f9fb9c2a
(-std=c++2b
)f632865f35fefe9e8cf00558cdc75e17f9fb9c2a
(-std=c++20
)It's not practical for me to test Clang systematically, so some snags with that are likely.
()
or []
indistinctly. Multi-argument []
requires __cpp_multidimensional_subscript > 202110L
(in gcc 12 with -std=c++2b
).Please see the TODO file for a concrete list of known bugs.
I do numerical work in C++, so I need a library of this kind. At the time of C++11 when I started writing it, most C++ array libraries seemed to support only vectors and matrices, or small objects for vector algebra.
Blitz++ was a major inspiration as a generic library. But it was a heroic feat to write such a library in C++ in the late 90s. Variadic templates, lambdas, perfect forwarding, etc. make things much easier, for the library writer as well as for the user.
From APL and J I've taken the rank extension mechanism, and perhaps an inclination for carrying each feature to its logical end.
ra-ra wants to remain simple. I try not to second-guess the compiler and I don't stress performance as much as Blitz++ did. However, I'm wary of adding features that could become an obstacle if I ever tried to make things fast(er). I believe that the implementation of new traversal methods, or perhaps the optimization of specific expression patterns, should be possible without having to turn the library inside out.