Creating an array of records containing some array

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.

1 Like

I could do it like this

var d = [i in 1..levels] new MGData(n / 2**(i-1));

Are there any other ways to do this ?

Hi @cpraveen

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):slight_smile:

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);
}
1 Like

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 (?).

-Brad

1 Like

So with @bradcray ‘s definition of MGData, in the following code:

var d : MGData;

d.D = {1..2}; // d.v has 2 elements

d.D = {1..3}; // d.v has 3 elements?

do the original 2 entries of d.v get discarded?

Thanks.

–shiv–

@00shiv

No, they're preserved. In Chapel, when arrays are resized, values at a given index are preserved for each index that's in both the old and new domain.

Here's an example showing that (ATO):

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

-Brad

Thanks @bradcray . I recall seeing this in the manual and I post the link here in case it is useful to someone else: Association of arrays to domains

1 Like