For a multigrid method I am trying to create some data structure like this
record MGData
{
const n : int;
var v : [1..n] real;
}
proc main()
{
const levels = 10;
const n = 2**(levels+1);
var d : [1..levels] MGData; // Array of MGData
for i in 1..levels
{
const m : int = n / 2**(i-1);
d[i] = new MGData(m);
}
}
This of course does not work. What is the correct way ?
I have an integer n which I would like to be constant and allocate array v of size n.
For readers who aren't clear why the original doesn't work, it's because the array d is initialized before the loop with the default initialzer for MGData, which sets n to 0 and therefore has a length-0 array. Then, because v's domain is anonymous, it is considered constant/immutable, and can't be resized later.
One way to address this is as you did—by making sure to initialize d at its declaration point to have each array have the size you want, after which they won't be resized again. This is a good approach if it makes sense for your code because it tells the compiler that the array won't be resized, which can be useful semantic information for optimizations.
Another way to do this is to hoist the array's domain into a variable field, permitting the domain to be reassigned and the array to be resized. Here's an example of that (ATO)
record MGData
{
var D = {1..0};
var v : [D] real;
}
proc main()
{
const levels = 10;
const n = 2**(levels+1);
var d : [1..levels] MGData; // Array of MGData
for i in 1..levels
{
const m : int = n / 2**(i-1);
d[i] = new MGData(D={1..m});
}
writeln(d);
}
What is the difference in terms of memory fragmentation between the two please? Which one would you recommended it you know the dimensions early enough to do either? Thanks Brad.
There's no difference in memory layout between the two. In each case, each array's data will be consecutive and on the heap, whether the outer array d's buffer of records or the inner arrays buffers-of-reals for the v fields.
One other approach to the original challenge could be to make the record MGData into a class MGData, in which case the elements of d could be pointed to different instances of that class at different times. The main downsides of this would that each object MGData would be heap-allocated, requiring an extra dereference to get from d to the individual MGData objects, and the need to deal with nilability and memory management. The upside would be additional flexibility (any element of d could be re-assigned to any other at any time). That said, my impression of multigrid patterns (assuming that's what MG stands for here) is that once the hierarchy is set up, the arrays likely won't change size (?), in which case this flexbility may not be terribly useful (?).
record MGData
{
var D = {1..0};
var v : [D] real;
}
var d : MGData;
d.D = {1..2}; // d.v has 2 elements
d.v = [45, 33];
d.D = {1..3}; // d.v has 3 elements?
writeln(d); // shows that the array stores 45.0, 33.0, 0.0