New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Implement noinit
for arrays
#15703
Comments
This is probably mentioned in some other thread, but note that in configurations where we do dynamic registration (ugni) we'll still want noinit to at least fault in the memory to get good numa affinity. Today we do:
If we don't do any init we'll end up with memory registration faulting in memory, which leaves everything on numa domain 0. So we'll probably want to touch the first byte in each page to fault in still. edit: yeah, Greg mentioned this in #8792 (comment) |
This part makes sense to me.
I am not so sure about this part. It would mean that the array knows (at runtime) that it was initialized with The second concern that I have with it is that generally speaking we would like to know at compile time whether or not something is default initialized. But, I wouldn't want to store that information in the array type - because arrays should generally behave the same / be passable to the same functions etc - regardless of how they were initialized. As a result, the way the array would know at runtime that it was initialized with Should code doing this kind of thing use a different type (like var D = {1..10};
var A: [D] real = noinit; // A's elements won't be initialized here
A.resizeArrayAndDomain(D, {1..20}, initNewElts=false);
// A.resizeArrayAndDomain might be declared like this
// MyDomain must match (pointer-identity wise) the array's domain
proc _array.resizeArrayAndDomain(ref MyDomain, NewDomain, param initNewElts=true) |
Copied from #15673 (comment) -
The reason that I am proposing this direction is that, without it, the noinit support cannot help to solve issue #15673. In particular, a data structure author might want to use a Note that the direction of leaving the allocated array memory totally uninitialized leads us to having 2 different things:
Now, I would expect that how one operates on these would differ in general. For variables declared with In contrast, for storage that is uninitialized, I am proposing that the user explicitly use low-level Another way to put it is that a pattern like |
To address #8792 (comment) and #15703 (comment) - I am proposing that the array implementation simply have a function that can be called to indicate that initialization is complete. For example: { var A:[1..n] R = noinit;
for i in 1..n {
moveInitialize(A[i], new R(i));
}
A.elementInitializationComplete();
// above call does any memory registration that is necessary.
// Additionally, indicates that now when A is out of scope, it
// should deinitialize the individual elements.
// R(1) and R(2) are deinitialized here, then array memory freed
} If the call is not made, the user is responsible for explicitly deinitializing the elements. For example, in this case, the array is created and used without ever actually initializing all elements. {
var A:[1..2] R = noinit;
moveInitialize(A[2], new R(2));
explicitDeinit(A[2]);
// here array memory is freed but no R elements are deinitialized
} We could consider having 2 different calls (for "register the memory now" and for "mark all elements initialized") but as far as I know, typical usage will initialize the entire array. Anyway the 2 different calls could be added later if needed. |
While I understand that we have been reserving the What if, instead of writing: var D = {1..10};
var A: [D] real = noinit; // A's elements won't be initialized here we wrote: var D = {1..10};
D.buildArray(int, initElts=false); (This exact pattern happens to compile and run now). Arguably this would reduce confusion with the difference between a |
My motivation for wanting this behavior was to have a simple way to avoid spending time initializing array elements when growing an array in cases where I knew that I didn't need the new elements to be initialized and didn't want to spend the time initializing them. Having the
I'm not seeing why this would need to be known at compile-time. I was imagining the array descriptor would store a non-param field indicating whether or not it was noinit, and then passing that flag along to the various ddata allocation/reallocation routines to say whether or not to default initialize any new elements.
Could the presence of a noinitializer mean "call it since it's there" and the absence of it mean "there's no need to call it for this data structure (e.g., "I am a POD record and don't need anything special to be done to me to support noinit"?)
I can see that argument, though there are cases where I'd like to use noinit (like the shootout codes) where I probably wouldn't use the longer form (because I think it's less attractive). We've also had DOE users request noinit from the early days, where I think having a solution that feels natural / integrated into the language seems preferable if it's tractable (e.g., I believe some users proposed things equivalent to the All that said, I'm not opposed to having longer-form / less-sugared ways of creating arrays, and at times have definitely wanted them for other reasons. I'm just not convinced that we should support this form instead of noinit or as the only way of getting to noinit.
I understand the difference between those two behaviors, but don't understand how the different syntactic form reduces the confusion. In both cases, isn't the user essentially choosing between two behaviors, simply using syntax in one case and a boolean argument in the other? |
@bradcray - thanks for your thoughts.
That's right. The potential for confusion I was seeing is one of terminology. If
It might be the case that More broadly though, calling Now, if for certain types, we call |
PR #16064 makes some progress here by enabling no-init for arrays of non-POD only. This is probably not enough as we have users requesting a way to more easily initialize an array of non-nilable class values. I personally think that supporting |
Adjust domain map implementations to no longer require pragma For issue #15673. Also for issues #15703 and #8792. This PR: * adjusts the domain map interface to include `dsiElementDeinitializationComplete` * removes `pragma "no auto destroy"` from domain map implementations * adds a user-facing capability to `noinit` arrays of POD elements The goal of this PR is to resolve issue #15673 by adjusting the domain map implementations to no longer require the use of a `pragma "no auto destroy"`. Originally I was expecting to do that with a `noinit` syntactic construction but it turns out that `noinit` is not strictly necessary in this case since the `buildArray(..., initElts=false)` pattern already covers it. Either way, the array in question has memory allocated for elements but the elements are totally uninitialized. In particular, to remove the need for `pragma "no auto destroy"`, this PR adds a `deinitElts:bool` field to DefaultRectangular to indicate if the elements should be deinitialized when the array is destroyed. It adjusts `dsiElementInitializationComplete` to set `deinitElts` to `true`. It adds `dsiElementDeinitializationComplete` to set `deinitElts` to `false` (and also allow similar behavior for other array types). It adjusts `dsiDestroyArr` for to check `this.deinitElts`. Even though `noinit` is not used in the distribution implementations, this PR implements noinit for arrays of POD elements. For example, ``` chapel var A:[1..10] int = noinit; // allocates space for elements but does not initialize them for i in 1..10 { A[i] = i; // `=` is sufficient for initializing trivially copyable types } ``` Users should also indicate when (if ever) the array's elements are completely initialized so that the memory can be registered to support communication. That is the subject of issue #16173. In order to easily test no-init of arrays, this PR adds a flag `--allow-noinit-array-not-pod` that will allow noinit of arrays of non-POD elements. It uses this in several tests along with a version of a low-level move module added to the test system. Issue #16172 discusses the design of a user-facing low-level move module. Reviewed by @vasslitvinov - thanks! - [x] full local futures testing - [x] primers pass with verify/valgrind and do not leak
PR #16525 adds a future for this case which does not work as this issue requests yet. I imagine it won't be too hard to adjust; the array resizing code can check to see if the array has been initialized yet. |
Add noinit technote Follow-up to PR #16064. The 1.23 release includes a very basic and restricted version of `noinit` because we were able to agree on the behavior of that case. The `noinit` feature is discussed in #15703 and #8792. The future work is discussed in #16172, #16173. Reviewed by @lydia-duncan - thanks!
As a Chapel programmer who is sometimes more concerned with performance than safety, I'd like the ability to declare my arrays to be
noinit
to avoid the overhead required to initialize them in cases when I know that I will safely write to them before reading from them (this is arguably a specialized instance of issue #8792, particularly as arrays become less and less special).For example, I'd like to be able to do:
The text was updated successfully, but these errors were encountered: