How to Atomically Update a Maximum Value

How to atomically update a maximum value?

It doesn't seem to be possible in a single operation, but you can make a loop, that tries to do this, until it finally succeeds or value in atomic variable becomes bigger than value:

template<typename T>
void update_maximum(std::atomic<T>& maximum_value, T const& value) noexcept
{
T prev_value = maximum_value;
while(prev_value < value &&
!maximum_value.compare_exchange_weak(prev_value, value))
{}
}

updating maximum value atomically

The strategy used to atomically update max value in a thread safe manner is correct.

Whether or not memory ordering is correct is impossible to tell because of code you are not showing.
If the atomic max value isn't used in any context other than reporting a value (i.e. no dependencies on other memory operations), you'll probably get away with std::memory_order_relaxed.

As I mentioned in my comment, on X86 the compiler is likely to produce the same assembly instructions regardless the use of memory ordering parameters.
X86 is a strongly ordered CPU which means that (by default) #LoadLoad and #LoadStore reordering is not allowed. therefore you won't find a (sane) compiler that will issue a memory fence around a seq_cst load.
(#StoreLoad reordering is still allowed by default, but to prevent that for seq_cst ordering is typically handled at the store side).

As for compare_exchange_weak (a read-modify-write operation), this requires the cache line to be locked in order to be atomic; you will see these assembly instructions on X86: lock cmpxchg
Since this also serves as a full memory barrier, it eliminates the need for additional fences.

Note that if you use std::memory_order_relaxed on any atomic operation, the compiler still has the freedom to apply compile time reordering

Updating a maximum value from multiple threads

Following a suggestion in a comment, I found a solution that does not require locking and instead uses the compare-and-exchange functionality found in std::atomic / boost::atomic. I am limited to C++03 so I would use boost::atomic in this case.

BOOST_STATIC_ASSERT(sizeof(int) == sizeof(float));
union FloatPun { float f; int i; };

std::vector< boost::atomic<int> > coord_max(128);
#pragma omp parallel for
for (int i = 0; i < limit; ++i) {
int j = get_coord(i);
FloatPun x, maxval;
x.f = compute_value(j, i);

maxval.i = coord_max[j].load(boost::memory_order_relaxed);
do {
if (maxval.f >= x.f) break;
} while (!coord_max[j].compare_exchange_weak(maxval.i, x.i,
boost::memory_order_relaxed));
}

There is some boilerplate involved in putting float values in ints, since it seems that atomic floats are not lock-free. I am not 100% use about the memory order, but the least restrictive level 'relaxed' seems to be OK, since non-atomic memory is not involved.

AtomicInteger and Math.max

If Java 8 is available you can use:

AtomicInteger value = new AtomicInteger(0);
Integer anotherCalculatedValue = ...;
value.getAndAccumulate(anotherCalculatedValue, Math::max);

Which from the specification will:

Atomically updates the current value with the results of
applying the given function to the current and given values,
returning the previous value.

how to update the maximum value of a slider?

In your alertesEnFonctionDuResultat(), after you update gain, you can add:

// et on change la valeur maximale du slider
yourSlider.maximumValue = Float(initialGains)

Anyway I think you should think again at what you are doing in your app...
For example, why here you use a text and not the value of your slider?

// on met a jour la valeur des gains initiale
initialGains = generateurDeGains + Int(sliderValueLabel.text!)!

And again, why here you update your slider maximumValue every time the slider move?

// on donne la valeur maximale du slider qui est celle des gains générés aleatoirement
sender.maximumValue = Float(generateurDeGains)

Calculate max value in an atomic FindAndModify operation

EDIT: Upon further reflection, my original answer was correct, but wasteful. Specifically, the first step is not necessary, so here's a revised version:

You can emulate this process in two steps:

  1. findAndModify with _id and max_value $lte the value you're currently attempting to insert. Since _id is unique, you know that only zero or one documents can match this query -- assuming that a document with that _id exists, it is zero in the case where the max_value is greater than what you're inserting, and one in the case where it is less than or equal. In the update, $push the new value, and $set max_value.

  2. If and only if step #1 failed, findAndModify again with _id, and $push the new value to the array. Since step #1 failed, we know that the current max_value is greater than the new value, so we can ignore it and just $push the new value.

Here's sample Python code to implement this:

# the_id is the ObjectId of the document we want to modify
# new_value is the new value to append to the list
rslt1 = rslt2 = None

rslt1 = db.collection.find_and_modify(
{'_id': the_id, 'max_value': {'$lte': new_value}},
{'$push': {'array': new_value}, '$set': {'max_value': new_value}})

if rslt1 is None:
rslt2 = db.collection.find_and_modify(
{'_id': the_id},
{'$push': {'array': new_value}})

# only one of these will be non-None; this
# picks whichever is non-None and assigns
# it to rslt
rslt = rslt1 or rslt2

(This original answer works, but the updated version above is more efficient.)

You can emulate this process in three steps:

  1. findAndModify a document with the given _id and with max_value $gt the current value you're attempting to insert. Since _id is unique, you know that only zero or one documents can match this query -- assuming that a document with that _id exists, it is zero in the case where the max_value is less than what you're inserting, and one in the case where it is greater. The update portion for this findAndModify will $push the new value to the array.

  2. If and only if step #1 failed, findAndModify again with _id and max_value $lte the value you're currently attempting to insert. In the update, $push the new value, and $set max_value.

  3. If and only if step #2 failed, findAndModify again with _id, and $push the new value to the array. This covers the case where between steps #1 and #2, another thread upped the max_value to a value greater than the value you're currently inserting.

Here's sample Python code to implement this:

# the_id is the ObjectId of the document we want to modify
# new_value is the new value to append to the list
rslt1 = rslt2 = rslt3 = None
rslt1 = db.collection.find_and_modify(
{'_id': the_id, 'max_value': {'$gt': new_value}},
{'$push': {'array': new_value}})

if rslt1 is None:
rslt2 = db.collection.find_and_modify(
{'_id': the_id, 'max_value': {'$lte': new_value}},
{'$push': {'array': new_value}, '$set': {'max_value': new_value}})

if rslt1 is None and rslt2 is None:
rslt3 = db.collection.find_and_modify(
{'_id': the_id},
{'$push': {'array': new_value}})

# only one of these will be non-None; this
# picks whichever is non-None and assigns
# it to rslt
rslt = rslt1 or rslt2 or rslt3


Related Topics



Leave a reply



Submit