Atomic min and max

I need to compute the minimum of a bunch of floating point numbers, but I only need the actual minimum if the number is bigger than 0.5 (say). I anticipate that most of the time the number will be less than 0.5, so I would like to terminate as soon as one of the tasks knows this. However I would prefer to use forall so that the actual number of threads is picked by Chapel. Ideally I would just write:

var minval : atomic real = 1;
forall i in 1..n {
if minval.read() > 0.5 {

minval.min (foo (i));
}
}

Unfortunately there seems to be no min or max for atomic numbers. Is there a simple way around this? Should I just use sync variables with the forall loop?
Thanks.
–shiv–

I think I found a reasonable work around (haven’t actually tried this):
var progress : atomic bool = true;
var minval : real = 1;
forall i in 1..n with (min reduce minval) {
if progress.read () {

  minval reduce= foo (i);
  if minval < 0.5 {
    progress.set (false);
  }

}
}

Add support for atomic min and max by mppf · Pull Request #25900 · chapel-lang/chapel · GitHub added atomic min and max along with some test code and there is documentation in the spec in https://chapel-lang.org/docs/language/spec/task-parallelism-and-synchronization.html#Atomics.fetchMin

Thanks! Sorry I missed this in the docs. The min/max accesses are all marked as “unstable”. Does that imply that I should not be using then by any chance?

–shiv–

Being marked "unstable" means that for whatever reason those symbols have not been reviewed for inclusion in the "stable language". Their implementation is fine, their name/API/design just hasn't gone through review/inclusion in the language.

I wouldn't be hesitant to use them. Its possible in the future those may be renamed/moved/removed, but for those symbols in particular I think that is unlikely (and if it does happen there will probably be a deprecation process, they won't just disappear one day).

-Jade

Great. Thanks Jade.
--shiv--

@00shiv

FWIW, I agree with Jade that these seem very unlikely to go away. I interpret the "unstable" in this case as "added after the Chapel 2.0 release and hasn't gone through the stabilization process that's now in place with Chapel's editions" rather than anything more significant. For these features specifically, I strongly suspect that if we'd implemented and reviewed them prior to 2.0, they'd be stable already.

I wanted to comment on your desire for an early exit if one of the tasks finds an element less than 0.5. This is a request we get every so often, that we often refer to as a "eureka"—which effectively says "If one task determines something, it should cause the other tasks to stop as soon as possible." This is a fairly challenging feature to implement in the language cleanly and well, so at present, it hasn't been. The two general approaches might be summarized as:

  1. Have the task hitting the eureka have some way of aggressively tearing down its sibling tasks via the runtime. This is the approach that most users have requested in the past, but it requires a different view of Chapel tasks than we support today—specifically, one in which tasks can be identified/named and put into groups (to distinguish the sibling tasks from others and avoid tearing down the whole program). There's a tension between supporting such features and the lightweight nature of Chapel tasks (at least, in their default, Qthreads, implementation), so it remains an idea in search of a champion.

  2. Have the tasks use some shared state to track whether any of them have hit a eureka, and have them check that state proactively from time-to-time as they compute. This one is much more tractable to implement (your sketch above could be viewed as a form of it), but it gets into interesting policy questions like "How often should a task delay its work to go see whether anyone else has told it to stop?" given that such checks slow down the main computational loop, particularly since the answer might vary depending on whether the tasks are all running on the same locale or distributed, not to mention the nature of the computation itself.

For those interested, in the following issue, you can read (some of) our past musings on it including some ideas about how to implement such patterns manually: Support eureka pattern (break in forall loop) · Issue #12700 · chapel-lang/chapel · GitHub

-Brad

Thanks for the link. It was very helpful.

–shiv–