Skip to content
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

Add support for atomic min and max #25900

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions modules/internal/Atomics.chpl
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,7 @@ module Atomics {

use ChapelBase;
public use MemConsistency; // OK: to get and propagate memoryOrder
import AutoMath;

pragma "local fn" pragma "fast-on safe extern function"
extern proc chpl_atomic_thread_fence(order:memory_order);
Expand Down Expand Up @@ -742,6 +743,42 @@ module Atomics {
on this do atomic_fetch_xor(_v, val, c_memory_order(order));
}

@unstable("'fetchMin' on an atomic is unstable")
inline proc ref fetchMin(val:valType, param order: memoryOrder = memoryOrder.seqCst): valType {
var t = if orderIncludesRelease(order)
then fetchAdd(0, order)
else read(readableOrder(order));
while AutoMath.min(val, t) != t {
if compareExchangeWeak(t, val, order) {
return t;
}
}
return t;
}

@unstable("'min' on an atomic is unstable")
inline proc ref min(val:valType, param order: memoryOrder = memoryOrder.seqCst): void {
fetchMin(val, order=order);
}

@unstable("'fetchMax' on an atomic is unstable")
inline proc ref fetchMax(val:valType, param order: memoryOrder = memoryOrder.seqCst): valType {
var t = if orderIncludesRelease(order)
then fetchAdd(0, order)
else read(readableOrder(order));
while AutoMath.max(val, t) != t {
if compareExchangeWeak(t, val, order) {
return t;
}
}
return t;
}

@unstable("'max' on an atomic is unstable")
inline proc ref max(val:valType, param order: memoryOrder = memoryOrder.seqCst): void {
fetchMax(val, order=order);
}

/*
Waits until the stored value is equal to `val`. The implementation may
yield the running task while waiting.
Expand Down
8 changes: 8 additions & 0 deletions modules/internal/MemConsistency.chpl
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,14 @@ module MemConsistency {
}
}

// Given an input memory order, returns 'true' if that
// order has a release component
proc orderIncludesRelease(param order: memoryOrder) param : bool {
return order == memoryOrder.seqCst ||
order == memoryOrder.acqRel ||
order == memoryOrder.release;
}

inline proc c_memory_order(param order: memoryOrder) {
import HaltWrappers;
select order {
Expand Down
138 changes: 138 additions & 0 deletions test/types/atomic/ferguson/testMinMax.chpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,138 @@
use Random;

config const n = 100_000;
config const verbose = false;

proc testSimple(type t) {
const zero = 0:t;
const one = 1:t;
const two = 2:t;
const many = 100:t;

var myMin: atomic t = many;
assert(myMin.read() == many);
assert(myMin.fetchMin(many) == many);
assert(myMin.read() == many);
assert(myMin.fetchMin(two) == many);
assert(myMin.read() == two);
assert(myMin.fetchMin(one) == two);
assert(myMin.read() == one);
assert(myMin.fetchMin(two) == one);
assert(myMin.read() == one);
assert(myMin.fetchMin(zero) == one);
assert(myMin.read() == zero);

// check also non-fetching min
myMin.write(many);
assert(myMin.read() == many);
myMin.min(many);
assert(myMin.read() == many);
myMin.min(two);
assert(myMin.read() == two);
myMin.fetchMin(one);
assert(myMin.read() == one);
myMin.min(two);
assert(myMin.read() == one);
myMin.min(zero);
assert(myMin.read() == zero);

var myMax: atomic t = zero;
assert(myMax.read() == zero);
assert(myMax.fetchMax(zero) == zero);
assert(myMax.read() == zero);
assert(myMax.fetchMax(one) == zero);
assert(myMax.read() == one);
assert(myMax.fetchMax(two) == one);
assert(myMax.read() == two);
assert(myMax.fetchMax(one) == two);
assert(myMax.read() == two);
assert(myMax.fetchMax(many) == two);
assert(myMax.read() == many);

// check also non-fetching max
myMax.write(zero);
assert(myMax.read() == zero);
myMax.max(zero);
assert(myMax.read() == zero);
myMax.max(one);
assert(myMax.read() == one);
myMax.max(two);
assert(myMax.read() == two);
myMax.max(one);
assert(myMax.read() == two);
myMax.max(many);
assert(myMax.read() == many);
}

proc testNonFetchingMin(type t, Input: [] t) {
// use the non-fetching min version
var myMin : atomic t = Input[0];
forall x in Input {
myMin.min(x);
}
assert(myMin.read() == min reduce Input);
}

proc testFetchingMin(type t, Input: [] t) {
// use the fetching min version
var myMin : atomic t = Input[0];
forall x in Input {
myMin.fetchMin(x);
}
assert(myMin.read() == min reduce Input);
}

proc testNonFetchingMax(type t, Input: [] t) {
// use the non-fetching min version
var myMax : atomic t = Input[0];
forall x in Input {
myMax.max(x);
}
assert(myMax.read() == max reduce Input);
}

proc testFetchingMax(type t, Input: [] t) {
// use the fetching max version
var myMax : atomic t = Input[0];
forall x in Input {
myMax.fetchMax(x);
}
assert(myMax.read() == max reduce Input);
}

proc testMinMaxesForArray(type t, Input: [] t) {
testNonFetchingMin(t, Input);
testFetchingMin(t, Input);
testNonFetchingMax(t, Input);
testFetchingMax(t, Input);
}

proc testMinMaxes(type t) {
writeln("testMinMaxes(", t:string, ")");

testSimple(t);

var Up: [0..<n] t = [i in 0..<n] i:t;
var Down: [0..<n] t = [i in 0..<n by -1] i:t;
var Random: [0..<n] t;

fillRandom(Random, seed=121);

testMinMaxesForArray(t, Up);
testMinMaxesForArray(t, Down);
testMinMaxesForArray(t, Random);
}

proc main() {
testMinMaxes(int(8));
testMinMaxes(int(16));
testMinMaxes(int(32));
testMinMaxes(int(64));
testMinMaxes(uint(8));
testMinMaxes(uint(16));
testMinMaxes(uint(32));
testMinMaxes(uint(64));

testMinMaxes(real(32));
testMinMaxes(real(64));
}
10 changes: 10 additions & 0 deletions test/types/atomic/ferguson/testMinMax.good
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
testMinMaxes(int(8))
testMinMaxes(int(16))
testMinMaxes(int(32))
testMinMaxes(int(64))
testMinMaxes(uint(8))
testMinMaxes(uint(16))
testMinMaxes(uint(32))
testMinMaxes(uint(64))
testMinMaxes(real(32))
testMinMaxes(real(64))