Skip to content

Commit 463f2f1

Browse files
authored
⚡ improved collision logic in RealNumberUniqueTable (#461)
## Description This PR improves the logic for handling collisions in the unique table for real numbers. Since we track tail pointers anyway, insertions in the back can be accomplished very cheaply. This can be used in both lookup scenarios; the border case and the middle case. If no match was found in a border case, it is clear that the new entry should either be added to the end of one or the beginning of the other bucket list. In the standard case, one can first check whether the looked up value is larger (or equal) than the last element in the bucket. If so, the new entry can just be added to the end of the collision chain. In a 33-qubit Grover simulation this saved a couple million collisions. ## Checklist: <!--- This checklist serves as a reminder of a couple of things that ensure your pull request will be merged swiftly. --> - [x] The pull request only contains commits that are related to it. - [x] I have added appropriate tests and documentation. - [x] I have made sure that all CI jobs on GitHub pass. - [x] The pull request introduces no new warnings and follows the project's style guidelines.
1 parent 2903079 commit 463f2f1

File tree

2 files changed

+68
-29
lines changed

2 files changed

+68
-29
lines changed

include/dd/RealNumberUniqueTable.hpp

+11-5
Original file line numberDiff line numberDiff line change
@@ -189,14 +189,20 @@ class RealNumberUniqueTable {
189189
RealNumber* findOrInsert(std::int64_t key, fp val);
190190

191191
/**
192-
* @brief Inserts a value into the bucket indexed by key.
193-
* @details This function inserts a value into the bucket indexed by key.
194-
* It assumes that no element within TOLERANCE is present in the bucket.
192+
* @brief Inserts a value in the front of the bucket indexed by key.
195193
* @param key The index of the bucket to insert the value into.
196194
* @param val The value to insert.
197-
* @returns A pointer to the inserted entry.
195+
* @return A pointer to the inserted entry.
198196
*/
199-
RealNumber* insert(std::int64_t key, fp val);
197+
RealNumber* insertFront(std::int64_t key, fp val);
198+
199+
/**
200+
* @brief Inserts a value in the back of the bucket indexed by key.
201+
* @param key The index of the bucket to insert the value into.
202+
* @param val The value to insert.
203+
* @return A pointer to the inserted entry.
204+
*/
205+
RealNumber* insertBack(std::int64_t key, fp val);
200206

201207
/**
202208
* @brief Lookup a non-negative number in the table.

src/dd/RealNumberUniqueTable.cpp

+57-24
Original file line numberDiff line numberDiff line change
@@ -117,9 +117,13 @@ RealNumber* RealNumberUniqueTable::lookupNonNegative(const fp val) {
117117
return pUpper;
118118
}
119119

120-
// value was not found in the table -> get a new entry and add it to the
121-
// central bucket
122-
return insert(key, val);
120+
// Since no match was found, a new value needs to be added
121+
// Depending on which border of the bucket the value lies, a value either
122+
// needs to be inserted in the front or the back of the bucket.
123+
if (key == lowerKey) {
124+
return insertFront(key, val);
125+
}
126+
return insertBack(key, val);
123127
}
124128

125129
bool RealNumberUniqueTable::possiblyNeedsCollection() const noexcept {
@@ -226,11 +230,36 @@ std::ostream& RealNumberUniqueTable::printBucketDistribution(std::ostream& os) {
226230

227231
RealNumber* RealNumberUniqueTable::findOrInsert(const std::int64_t key,
228232
const fp val) {
229-
const fp valTol = val + RealNumber::eps;
233+
const auto k = static_cast<std::size_t>(key);
234+
auto* curr = table[k];
235+
if (curr == nullptr) {
236+
auto* entry = memoryManager->get();
237+
entry->value = val;
238+
entry->next = curr;
239+
table[k] = entry;
240+
tailTable[k] = entry;
241+
stats.trackInsert();
242+
return entry;
243+
}
230244

231-
auto* curr = table[static_cast<std::size_t>(key)];
232-
RealNumber* prev = nullptr;
245+
auto* back = tailTable[k];
246+
if (back != nullptr && back->value <= val) {
247+
if (RealNumber::approximatelyEquals(val, back->value)) {
248+
++stats.hits;
249+
return back;
250+
}
251+
++stats.collisions;
252+
auto* entry = memoryManager->get();
253+
entry->value = val;
254+
entry->next = nullptr;
255+
back->next = entry;
256+
tailTable[k] = entry;
257+
stats.trackInsert();
258+
return entry;
259+
}
233260

261+
RealNumber* prev = nullptr;
262+
const fp valTol = val + RealNumber::eps;
234263
while (curr != nullptr && curr->value <= valTol) {
235264
if (RealNumber::approximatelyEquals(curr->value, val)) {
236265
// check if val is actually closer to the next element in the list (if
@@ -260,42 +289,46 @@ RealNumber* RealNumberUniqueTable::findOrInsert(const std::int64_t key,
260289
entry->value = val;
261290

262291
if (prev == nullptr) {
263-
// table bucket is empty
264-
table[static_cast<std::size_t>(key)] = entry;
292+
// add to front of bucket
293+
table[k] = entry;
265294
} else {
266295
prev->next = entry;
267296
}
268297
entry->next = curr;
269298
if (curr == nullptr) {
270-
tailTable[static_cast<std::size_t>(key)] = entry;
299+
tailTable[k] = entry;
271300
}
272301
stats.trackInsert();
273302
return entry;
274303
}
275304

276-
RealNumber* RealNumberUniqueTable::insert(const std::int64_t key,
277-
const fp val) {
305+
RealNumber* RealNumberUniqueTable::insertFront(const std::int64_t key,
306+
const fp val) {
278307
auto* entry = memoryManager->get();
279308
entry->value = val;
280309

281310
auto* curr = table[static_cast<std::size_t>(key)];
282-
RealNumber* prev = nullptr;
283-
284-
while (curr != nullptr && curr->value <= val) {
285-
++stats.collisions;
286-
prev = curr;
287-
curr = curr->next;
311+
table[static_cast<std::size_t>(key)] = entry;
312+
entry->next = curr;
313+
if (curr == nullptr) {
314+
tailTable[static_cast<std::size_t>(key)] = entry;
288315
}
316+
stats.trackInsert();
317+
return entry;
318+
}
289319

290-
if (prev == nullptr) {
291-
// table bucket is empty
320+
RealNumber* RealNumberUniqueTable::insertBack(const std::int64_t key,
321+
const fp val) {
322+
auto* entry = memoryManager->get();
323+
entry->value = val;
324+
entry->next = nullptr;
325+
326+
auto* back = tailTable[static_cast<std::size_t>(key)];
327+
tailTable[static_cast<std::size_t>(key)] = entry;
328+
if (back == nullptr) {
292329
table[static_cast<std::size_t>(key)] = entry;
293330
} else {
294-
prev->next = entry;
295-
}
296-
entry->next = curr;
297-
if (curr == nullptr) {
298-
tailTable[static_cast<std::size_t>(key)] = entry;
331+
back->next = entry;
299332
}
300333
stats.trackInsert();
301334
return entry;

0 commit comments

Comments
 (0)