mirror of
https://github.com/YosysHQ/yosys
synced 2025-10-09 17:31:59 +00:00
Make mfp
const methods thread-safe.
In particular, we make the parent links relaxed atomics so concurrent `ifind()` calls are safe. This may appear to cause a tiny performance regression but as discussed in https://yosyshq.discourse.group/t/parallel-optmergepass-implementation/87/16 this is probably just noise.
This commit is contained in:
parent
7719beb4ae
commit
2f81c55389
2 changed files with 45 additions and 14 deletions
|
@ -13,6 +13,7 @@
|
||||||
#define HASHLIB_H
|
#define HASHLIB_H
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <stdexcept>
|
#include <stdexcept>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
@ -1355,8 +1356,18 @@ public:
|
||||||
template<typename K, typename OPS>
|
template<typename K, typename OPS>
|
||||||
class mfp
|
class mfp
|
||||||
{
|
{
|
||||||
mutable idict<K, 0, OPS> database;
|
idict<K, 0, OPS> database;
|
||||||
mutable std::vector<int> parents;
|
class AtomicParent {
|
||||||
|
public:
|
||||||
|
explicit AtomicParent(int p) : parent(p) {}
|
||||||
|
AtomicParent(const AtomicParent &other) : parent(other.get()) {}
|
||||||
|
AtomicParent &operator=(const AtomicParent &other) { set(other.get()); return *this; }
|
||||||
|
int get() const { return parent.load(std::memory_order_relaxed); }
|
||||||
|
void set(int p) { parent.store(p, std::memory_order_relaxed); }
|
||||||
|
private:
|
||||||
|
std::atomic<int> parent;
|
||||||
|
};
|
||||||
|
std::vector<AtomicParent> parents;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
typedef typename idict<K, 0>::const_iterator const_iterator;
|
typedef typename idict<K, 0>::const_iterator const_iterator;
|
||||||
|
@ -1367,12 +1378,14 @@ public:
|
||||||
|
|
||||||
// Finds a given element's index. If it isn't in the data structure,
|
// Finds a given element's index. If it isn't in the data structure,
|
||||||
// it is added as its own set
|
// it is added as its own set
|
||||||
int operator()(const K &key) const
|
int operator()(const K &key)
|
||||||
{
|
{
|
||||||
int i = database(key);
|
int i = database(key);
|
||||||
// If the lookup caused the database to grow,
|
// If the lookup caused the database to grow,
|
||||||
// also add a corresponding entry in parents initialized to -1 (no parent)
|
// also add a corresponding entry in parents initialized to -1 (no parent)
|
||||||
parents.resize(database.size(), -1);
|
if (parents.size() < database.size()) {
|
||||||
|
parents.emplace_back(-1);
|
||||||
|
}
|
||||||
return i;
|
return i;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1382,21 +1395,38 @@ public:
|
||||||
return database[index];
|
return database[index];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Why this method is correct for concurent ifind() calls:
|
||||||
|
// Consider the mfp state after the last non-const method call before
|
||||||
|
// a particular call to ifind(i). In this state, i's parent chain leads
|
||||||
|
// to some root R. Let S be the set of integers s such that ifind(s) = R
|
||||||
|
// in this state. Let 'orig_parents' be the value of 'parents' in this state.
|
||||||
|
//
|
||||||
|
// Now consider the concurrent calls to ifind(s), s ∈ S, before the next non-const method
|
||||||
|
// call. Consider the atomic writes performed by various ifind() calls, in any causally
|
||||||
|
// consistent order. The first atomic write can only set parents[k] to R, because the
|
||||||
|
// atomic read of parents[p] in the first while loop can only observe the value
|
||||||
|
// 'orig_parents[p]'. Subsequent writes can also only set parents[k] to R, because the
|
||||||
|
// parents[p] reads either observe 'orig_parents[p]' or R (and observing R ends the first
|
||||||
|
// while loop immediately). Thus all parents[p] reads observe either 'orig_parents[p]'
|
||||||
|
// or R, so ifind() always returns R.
|
||||||
int ifind(int i) const
|
int ifind(int i) const
|
||||||
{
|
{
|
||||||
int p = i, k = i;
|
int p = i, k = i;
|
||||||
|
|
||||||
while (parents[p] != -1)
|
while (true) {
|
||||||
p = parents[p];
|
int pp = parents[p].get();
|
||||||
|
if (pp < 0)
|
||||||
|
break;
|
||||||
|
p = pp;
|
||||||
|
}
|
||||||
// p is now the representative of i
|
// p is now the representative of i
|
||||||
// Now we traverse from i up to the representative again
|
// Now we traverse from i up to the representative again
|
||||||
// and make p the parent of all the nodes along the way.
|
// and make p the parent of all the nodes along the way.
|
||||||
// This is a side effect and doesn't affect the return value.
|
// This is a side effect and doesn't affect the return value.
|
||||||
// It speeds up future find operations
|
// It speeds up future find operations
|
||||||
while (k != p) {
|
while (k != p) {
|
||||||
int next_k = parents[k];
|
int next_k = parents[k].get();
|
||||||
parents[k] = p;
|
const_cast<AtomicParent*>(&parents[k])->set(p);
|
||||||
k = next_k;
|
k = next_k;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1411,7 +1441,7 @@ public:
|
||||||
j = ifind(j);
|
j = ifind(j);
|
||||||
|
|
||||||
if (i != j)
|
if (i != j)
|
||||||
parents[i] = j;
|
parents[i].set(j);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ipromote(int i)
|
void ipromote(int i)
|
||||||
|
@ -1419,15 +1449,15 @@ public:
|
||||||
int k = i;
|
int k = i;
|
||||||
|
|
||||||
while (k != -1) {
|
while (k != -1) {
|
||||||
int next_k = parents[k];
|
int next_k = parents[k].get();
|
||||||
parents[k] = i;
|
parents[k].set(i);
|
||||||
k = next_k;
|
k = next_k;
|
||||||
}
|
}
|
||||||
|
|
||||||
parents[i] = -1;
|
parents[i].set(-1);
|
||||||
}
|
}
|
||||||
|
|
||||||
int lookup(const K &a) const
|
int lookup(const K &a)
|
||||||
{
|
{
|
||||||
return ifind((*this)(a));
|
return ifind((*this)(a));
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,6 +21,7 @@
|
||||||
#define YOSYS_COMMON_H
|
#define YOSYS_COMMON_H
|
||||||
|
|
||||||
#include <array>
|
#include <array>
|
||||||
|
#include <atomic>
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <tuple>
|
#include <tuple>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue