Array assignment

Which one of the following has the least overhead

var c : [0..<n] real;

// blah, blah, blah assignment something in each element of c

// either
c = [i in 0..<n] someFunc(c[i]);
// or
forall i in 0..<n do c[i] = someFunc(c[i]);
// or
c = for i in 0..<n do someFunc(c[i]);
// or
for i in 0..<n do c[i] = someFunc(c[i]);

Any other suggestions are welcome. What about a foreach?

Thanks

Hi Damian —

In your case, where c has already been initialized before encountering these loop idioms, the first two approaches should be more or less equivalent in terms of performance, as should the final two. If you were to see otherwise in practice, that's something we'd be interested in hearing about.

In more detail, the first two loops should result in multicore parallel execution, whereas the last two will necessarily be serial by virtue of the for loop. I'm assuming in the following that someFunc() is simple and side-effect free such that calling it multiple times in parallel is safe and would not results in synchronization.

For large values of n, I'd typically pick one of the first two to get the benefits of parallelism. A few other ways to write it with parallelism would be to use promotion, like so:

c = someFunc(c);

or a forall expression, like:

c = forall i in 0..<n do someFunc(c[i]);

Just in case it's unclear [i in 0..<n] is shorthand for forall i in 0..<n for a range iterand like this. More generally, [i in expr] means forall i in expr when expr supports a parallel iterator and foreach i in expr when it does not.

A reason to pick one of the latter two serial loops would be if n was sufficiently small that you thought spinning up tasks and dividing work across cores was overkill for this computation. As you note, though, falling back to a foreach rather than a for could be beneficial if there was a chance that someFunc() was amenable to vectorization, in which case I'd write:

foreach i in 0..<n do c[i] = someFunc(c[i]);

Generally speaking, there should only be upside to using a foreach instead of a for if someFunc() is parallel-safe, as I'm assuming here.

When in doubt, I'd typically use one of the parallel versions (and specifically, the promoted version for elegance), following an "express all parallelism" philosophy.

-Brad

Thanks for the insight. I only ever write thread safe code. So I assume that means parallel-safe too at the routine level.

I do not understand your comments on promotion. Surely that must mean that the routine is overloaded to support parallel performance? For instance, given vectors x and y, I cannot just write

x = max(x, y)

As an asside, I notice that I cannot write

c = foreach i in Range do .....;

Is that intentional?

Did you see my comment that a foreach is not treated as a keyword by the markup within a Chapel block?

Hi Damian —

You should be able to do just that. See this ATO example for a demonstration.

For more information, Chapel's concept of promoting scalar routines with array arguments is documented here:

It is the state of things that foreach is only a statement-level loop today, and can't be used at the expression level as you are doing here. But that's not where we want to end up. This issue captures our desire to support foreach at the expression level as well: Add support for `foreach` loop expressions · Issue #19336 · chapel-lang/chapel · GitHub

Did you see my comment that a foreach is not treated as a keyword by the markup within a Chapel block?

No, I didn't. Where did you make that comment? (I do see now that it isn't...)

-Brad