Help with memory allocation/deallocation and matrix/vector libraries for scientific computations

Dear all,
No doubt that ADA since its very beginning got almost everything right as it regards syntax and semantics. If I start listing things I adore about this Language, it will be boring, so I would prefer to discuss with you a few things that might and I repeat “might” need improvement.

1. Pointers.
Deallocation could be made simpler and easier to remember without hurting overall functionality.
procedure Free is new Ada.Unchecked_Deallocation(…)

is probably too cumbersome. Perhaps Storage pool is the most elegant alternative. Are there any more modern approaches? I apologise for my ignorance here but the standard keeps changing and documentation with examples around the web is not that easy to find for ADA, free books are quite old.

2. Matrix/Vector libraries
Applications of Real_Arrays are limited due to static allocations by default, no easy/dynamic stack storage sizing, and temporary objects when overloading operators. Are any other library alternatives ?

Any plans from ADA headquarters for native support of Matrix/Vector operations similar to Fortran without temporaries and Fortran efficiency? Same for complex numbers?

Thank you all in advance.

There’s a library called Deepend for storage pools, but it’s not on GitHub.

For tensors I wrote an Alire crate that supports matrix and element-wise operations, matrix decompositions (QR, Cholesky), continuous statistical distributions, etc. See the documentation:

The SIMD implementation running on the CPU can be found in the package Orka.Numerics.Doubles.Tensors.CPU in the orka_tensors_cpu Alire crate (not yet in the public index, so git clone the repo). The interface type Tensor and docs can be found in the Orka.Numerics.Tensors package in the orka_numerics crate.

The CPU implementation uses eager evaluation and uses the stack (increase the Storage_Size of the task if needed), but I’m finishing up a GPU implementation that uses lazy evaluation (it builds and dispatches a Compute Shader right before the data is fetched from the tensor) and can store large buffers (possibly up to 2 GB on discrete GPUs).

1 Like

Thank you very much for your email, however, it is not clear if this is restricted to stack matrices or heap matrices of arbitrary size. Is it on 4x4 matrices used for translating/rotating an object in OpenGL?

Tensor objects can be of arbitrary size. The maximum size of the CPU implementation depends on the Storage_Size of the task, while for the GPU implementation it depends on the maximum size of an SSBO. On an Intel GPU that could mean a vector of 33554432 32-bit floats due to the 128MB limit on Intel.

(For translations/rotations of objects in OpenGL there are separate packages for 4x4 matrices in the orka_transforms crate)

Thanks for the clarification, but in several applications and in most applications of commercial interest you are not aware in advance what the runtime requirements in terms of memory will be, so you cannot set the stack size in advance, except if the stack size is set to the whole RAM memory. Why ADA is so fixated on stack allocated arrays? And why should I avoid using the heap? Of course I have read all the arguments but for large arrays, is there such a great performance difference? For instance performing vector only operations on the stack and on the heap for vectors of size N=100,000,000 lets say:

X := A+B+C * C * A * B

what is the performance of this using tensors allocated on heap and on stack?

1 Like

In Ada there are many cases where in other languages solving a problem would have required pointers. Not having to use pointers avoids having to do memory management and certain problems like null pointers (unless you use not null access types), memory leaks, etc. Also in safety critical or embedded systems you may not be able to do or want perform allocations at runtime for various reasons.

So it’s mostly because the language does not force you to always use pointers and because of the systems/industries where it is used. If you use Ada on a desktop or server then these things do not make sense and it would be okay to use pointers if you need them.

One thing to remember is that functions copy the data they return; if a function returns a large amount of data it may make sense to return something like a (smart) pointer instead.

Thanks Onox,

The problem is that without allocation on heap you run out of stack quickly and this is not pleasant at all. They could still provide 2 Array types, one allocated on the Heap and another one like Real_Arrays on the stack. Why not? Why??? Heap_Real_Arrays would be wonderful. And memory allocation/deallocation could be organized by them internally with an optimal memory management tool.

I am in the learning process and I confess that although I like it a lot, the syntax is extremely hard to learn. Of course it is easier than learning EMACS. But the generics, generic instantiation and other stuff are a mess to remember how to. C++ and similar languages are much easier in this respect.

It is too hard to do something simple with ADA and I am not sure if this (strong syntax) promotes safety. Safety problems are most of the time due to null pointers or read/write out of the allocated memory due to wrong indexing. But this is due that no one of these stupid languages as C++,C predicted the need of built in arrays/matrices with bound checking in debug mode and not let this to the user. Furthermore, the tutorials on ADA do not cover everything. I lost one hour today to see how I can instantiate the Generic_Real_Arrays with my own Real type 16 digits. GNAT doesn’t have static pro compilation indication of syntax errors in real time. I am trying to assess the speed in some basic mathematical kernels and compare it with the much simpler syntax of Fortran. I will let you know.

In the meantime, is there any complete tutorial for C++ programmers, where everything is covered and not just strings which is only interesting for informatic students? Emphasis on mathematical array libraries is what I am seeking.

An array type in Ada does not dictate how it should be stored in memory:

type Real_Vector_Ptr is not null access Real_Vector;

Vector_Heap  : Real_Vector_Ptr := new Real_Vector'((1 .. 1_000 => 1.0));
Vector_Stack : Real_Vector     := (1 .. 1_000 => 1.0);

See https://people.cs.kuleuven.be/~dirk.craeynest/ada-belgium/events/16/160130-fosdem/09-ada-memory.pdf for more info about memory management.

If you take a look at the package spec of Ada.Numerics.Generic_Real_Arrays (in its .ads file) then you see that it has 1 generic parameter: type Real is digits <>;.
This means Real is a floating-point type. See Ada Programming/Generics - Wikibooks, open books for an open world for a list of generic formal types that can be used by packages.

So you specify your type and then instantiate the package:

type My_Own_Type is digits 8;

package GRA is new Ada.Numerics.Generic_Real_Arrays (My_Own_Type);
use GRA;

The instantiated package (GRA in this case) can be treated like a regular package.

See Books | Ada Programming Language for a list of physical and online books.

You can find some libraries for mathematics at GitHub - ohenley/awesome-ada: A curated list of awesome resources related to the Ada and SPARK programming language

2 Likes

Just curious… What do you mean by this?

This not true when using an extended return statement.

1 Like

True, but if you do V : T := F; where F uses an extended return statement, the data is still put on the stack and if T can be large, then a Storage_Error may be raised. Hence the need for returning a small amount of data like smart pointer.

It depends on the location of the target object, V in your example.
If Vis on the stack, then, yes the stack is used. If V is not on the stack (a library variable ?), no stack is involved.