This repository has been archived by the owner on Sep 3, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
A.Broadcast.Listener.Solution.To.EuclidProver.Scheduling.Algorithm.v2.h
161 lines (133 loc) · 4.92 KB
/
A.Broadcast.Listener.Solution.To.EuclidProver.Scheduling.Algorithm.v2.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
//#include "../boost/multiprecision/cpp_int.hpp"
//#include "../EuclidProverLibDLL/EuclidProverLibDLL.h"
//#include "../EuclidProverLibDLL/TestCases.h"
#include <vector>
#include <chrono>
#include <iostream>
#include <thread>
#include <unordered_map>
#include <algorithm>
#include <cstdint>
/*
Q:Is there a Listener/Broadcast configuration or architecture which could be a solution?
Yes, a Listener/Broadcast (or Publisher/Subscriber) architecture can be a solution to avoid contention in accessing the unordered_map. In this pattern, Axioms (listeners/subscribers) register themselves to receive updates from a central source (broadcast/publisher) when a change occurs, instead of directly accessing the shared data structure. This reduces contention and avoids the need for fine-grained locking.
Here's a high-level example of how you can implement a Listener/Broadcast architecture:
Define an AttributeListener interface and a TaskListClass class:
cpp
*/
class AttributeListener {
public:
virtual void onAttributeChanged(uint64_t key, uint64_t new_value) = 0;
};
/*
Make your Axioms inherit from AttributeListener:
cpp
*/
class AxiomClass : public AttributeListener {
public:
AxiomClass(const uint64_t key) :
key(key)
{
}
void onAttributeChanged(const uint64_t key, uint64_t new_value) override final {
if (this->key == key) {
attribute = new_value;
}
}
uint64_t GetGUID_UInt64() const {
return guid;
}
std::vector<AxiomClass> GetProofStack__AxiomClassVec() {
return ProofStack_AxiomClassVec;
}
// Copy constructor
explicit AxiomClass(const AxiomClass& other) :
guid(other.guid),
key(other.key),
attribute(other.attribute),
ProofStack_AxiomClassVec(other.ProofStack_AxiomClassVec)
{
}
// Move constructor
explicit AxiomClass(AxiomClass&& other) noexcept :
guid(std::move(other.guid)),
key(std::move(other.key)),
attribute(std::move(other.attribute)),
ProofStack_AxiomClassVec(std::move(other.ProofStack_AxiomClassVec))
{
}
// Copy assignment operator
AxiomClass& operator=(const AxiomClass& other)
{
if (this != &other)
{
*this = AxiomClass(other);
}
return *this;
}
// Move assignment operator
AxiomClass& operator=(const AxiomClass&& other) noexcept
{
if (this != &other)
{
*this = AxiomClass(std::move(other));
}
return *this;
}
// Destructor
~AxiomClass() = default;
private:
const uint64_t guid;
const uint64_t key;
uint64_t attribute = 1;
std::vector<AxiomClass> ProofStack_AxiomClassVec{};
};
class TaskListClass {
public:
void addListener(const uint64_t InIdxUInt64, AxiomClass& listener) {
std::vector<AxiomClass&> obj{};
obj.push_back(listener);
listeners.emplace(InIdxUInt64,obj);
}
void notifyAttributeChanged(const uint64_t key, const uint64_t new_value, const uint64_t guid) {
auto it = listeners.find(key);
if (it != listeners.end() && it->second.GetGUID_UInt64() != guid) {
it->second.onAttributeChanged(key, new_value);
}
else if (it->second.GetGUID_UInt64() != guid) {
AxiomClass obj(new_value);
addListener(key, obj);
}
}
private:
std::unordered_map<const uint64_t, AxiomClass&> listeners;
uint64_t IdxUInt64 = 0;
};
/*
TaskListClass, in tandem with std::for_each(std::execute::par,*.begin(), *.end()) or std::thread, emulates unordered_map parallel read/write access.
Use the TaskListClass to propagate attribute changes:
cpp
*/
int main() {
TaskListClass TaskListLHS;
// Create Axioms and register them as listeners
std::vector<AxiomClass> Axioms;
for (uint64_t i = 0; i < 1000; ++i) {
Axioms.emplace_back(i);
TaskListLHS.addListener(i, Axioms.back());
}
// Update an attribute and broadcast the change
const uint64_t guid = 10;
const uint64_t key = 42;
const uint64_t new_value = 12345;
TaskListLHS.notifyAttributeChanged(key, new_value, guid);
return 0;
}
/*
In this example, the Axioms no longer need to access the shared unordered_map to read each other's attribute values. Instead, they receive updates through the onAttributeChanged method when an attribute changes. This reduces contention and simplifies synchronization.
However, this approach has some trade-offs:
It increases memory usage since each object stores its attribute value.
It can increase communication overhead, as each update must be broadcast to all listeners.
It may not be suitable for scenarios where you need to query an object's attribute value at any given time, as the broadcast is only sent when an attribute is updated.
You should consider these trade-offs when deciding if the Listener/Broadcast architecture is the right solution for your use case.
*/