Skip to content

Commit f2e8437

Browse files
author
Can Gokmen
committed
Implemented concurrent ART
1 parent 6541a62 commit f2e8437

2 files changed

Lines changed: 95 additions & 26 deletions

File tree

concurrent/ConcurrentART.h

Lines changed: 87 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,15 @@ class ConcurrentART : public ART {
1919
}
2020

2121
void insertCC(uint8_t key[], uintptr_t value) {
22-
ConcurrentART::insert(this, root, &root, key, 0, value, maxPrefixLength, nullptr, 0);
22+
while (true) { // Retry loop
23+
try {
24+
insert(this, root, &root, key, 0, value, maxPrefixLength, nullptr, 0);
25+
return; // Success - exit retry loop
26+
} catch (const RestartException&) {
27+
// Conflict detected - loop will retry from beginning
28+
continue;
29+
}
30+
}
2331
}
2432

2533
private:
@@ -83,11 +91,12 @@ class ConcurrentART : public ART {
8391
if (!*child) {
8492
// Insert leaf into inner node
8593
ArtNode* newNode = makeLeaf(value);
94+
uint8_t keyByte = key[depth];
95+
8696
switch (node->type) {
8797
case NodeType4: {
8898
// Cast node to Node4 to access its members
8999
Node4* node4 = static_cast<Node4*>(node);
90-
uint8_t keyByte = key[depth];
91100
// Insert leaf into inner node
92101
if (node4->count < 4) {
93102

@@ -125,18 +134,50 @@ class ConcurrentART : public ART {
125134
newNode16->key[i] = flipSign(node4->key[i]);
126135
memcpy(newNode16->child, node4->child, node4->count * sizeof(uintptr_t));
127136

137+
// Shouldn't be deleted, other threads might still be reading it
138+
// delete node;
139+
140+
// INSERT NODE 16 PART
141+
142+
// Flip the sign bit of the key byte for correct ordering in signed
143+
// comparisons
144+
uint8_t keyByteFlipped = flipSign(keyByte);
145+
146+
// SIMD: Compare keyByteFlipped with all keys in the node in parallel
147+
// _mm_set1_epi8 sets all 16 bytes of an SSE register to keyByteFlipped
148+
// _mm_loadu_si128 loads the node's keys into an SSE register
149+
// _mm_cmplt_epi8 does a signed comparison of each byte
150+
__m128i cmp = _mm_cmplt_epi8(
151+
_mm_set1_epi8(keyByteFlipped),
152+
_mm_loadu_si128(reinterpret_cast<__m128i*>(newNode16->key)));
153+
154+
// _mm_movemask_epi8 creates a 16-bit mask from the comparison results
155+
// Only consider the bits for the active keys (node16->count)
156+
uint16_t bitfield =
157+
_mm_movemask_epi8(cmp) & (0xFFFF >> (16 - newNode16->count));
158+
159+
// Find the position of the first set bit (i.e., where keyByteFlipped <
160+
// key[i])
161+
unsigned pos = bitfield ? ctz(bitfield) : newNode16->count;
162+
163+
// Shift keys and children to the right to make space for the new
164+
// key/child. This preserves the sorted order of keys in the node.
165+
memmove(newNode16->key + pos + 1, newNode16->key + pos, newNode16->count - pos);
166+
memmove(newNode16->child + pos + 1, newNode16->child + pos,
167+
(newNode16->count - pos) * sizeof(uintptr_t));
168+
newNode16->key[pos] = keyByteFlipped;
169+
newNode16->child[pos] = newNode;
170+
newNode16->count++;
171+
128172
writeUnlockObsolete(node);
129173
if (parent) { writeUnlock(parent); }
130-
131-
delete node4;
132-
return newNode16->insertNode16(tree, nodeRef, keyByte, newNode);
174+
133175
}
134176
break;
135177
}
136178
case NodeType16: {
137179
// Cast node to Node16 to access its members
138180
Node16* node16 = static_cast<Node16*>(node);
139-
uint8_t keyByte = key[depth];
140181

141182
// Insert leaf into inner node
142183
if (node16->count < 16) {
@@ -185,26 +226,38 @@ class ConcurrentART : public ART {
185226
upgradeToWriteLockOrRestart(node, version, parent);
186227

187228
// Grow to Node48
188-
Node48* newNode = new Node48();
189-
*nodeRef = newNode;
190-
memcpy(newNode->child, node16->child, node16->count * sizeof(uintptr_t));
229+
Node48* newNode48 = new Node48();
230+
*nodeRef = newNode48;
231+
memcpy(newNode48->child, node16->child, node16->count * sizeof(uintptr_t));
191232
for (unsigned i = 0; i < node16->count; i++)
192-
newNode->childIndex[flipSign(node16->key[i])] = i;
193-
copyPrefix(node16, newNode);
194-
newNode->count = node16->count;
233+
newNode48->childIndex[flipSign(node16->key[i])] = i;
234+
copyPrefix(node16, newNode48);
235+
newNode48->count = node16->count;
236+
237+
// Shouldn't be deleted, other threads might still be reading it
238+
// delete node;
239+
240+
// INSERT NODE 48 PART
241+
242+
// Insert element
243+
unsigned pos = newNode48->count;
244+
if (newNode48->child[pos])
245+
for (pos = 0; newNode48->child[pos] != NULL; pos++)
246+
;
247+
// No memmove needed here because Node48 uses a mapping (childIndex) and
248+
// a dense array.
249+
newNode48->child[pos] = newNode;
250+
newNode48->childIndex[keyByte] = pos;
251+
newNode48->count++;
195252

196253
writeUnlockObsolete(node);
197254
if (parent) { writeUnlock(parent); }
198-
199-
delete node16;
200-
return newNode->insertNode48(tree, nodeRef, keyByte, newNode);
201255
}
202256
break;
203257
}
204258
case NodeType48: {
205259
// Cast node to Node48 to access its members
206260
Node48* node48 = static_cast<Node48*>(node);
207-
uint8_t keyByte = key[depth];
208261

209262
// Insert leaf into inner node
210263
if (node48->count < 48) {
@@ -233,26 +286,35 @@ class ConcurrentART : public ART {
233286
}
234287
upgradeToWriteLockOrRestart(node, version, parent);
235288

236-
Node256* newNode = new Node256();
289+
Node256* newNode256 = new Node256();
237290
for (unsigned i = 0; i < 256; i++)
238291
if (node48->childIndex[i] != 48)
239-
newNode->child[i] = node48->child[node48->childIndex[i]];
240-
newNode->count = node48->count;
241-
copyPrefix(node48, newNode);
242-
*nodeRef = newNode;
292+
newNode256->child[i] = node48->child[node48->childIndex[i]];
293+
newNode256->count = node48->count;
294+
copyPrefix(node48, newNode256);
295+
*nodeRef = newNode256;
296+
297+
// tree->printTree();
298+
299+
// Shouldn't be deleted, other threads might still be reading it
300+
// delete node;
301+
302+
// INSERT NODE 256 PART
303+
304+
// Insert leaf into inner node
305+
// No memmove needed here because Node256 uses a direct mapping for all
306+
// possible keys.
307+
newNode256->count++;
308+
newNode256->child[keyByte] = newNode;
243309

244310
writeUnlockObsolete(node);
245311
if (parent) { writeUnlock(parent); }
246-
247-
delete node48;
248-
return newNode->insertNode256(tree, nodeRef, keyByte, newNode);
249312
}
250313
break;
251314
}
252315
case NodeType256: {
253316
// Cast node to Node256 to access its members
254317
Node256* node256 = static_cast<Node256*>(node);
255-
uint8_t keyByte = key[depth];
256318

257319
upgradeToWriteLockOrRestart(node, version);
258320
if (parent) { readUnlockOrRestart(parent, parentVersion, node); }

concurrent/run_cc.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -159,13 +159,20 @@ class Workload {
159159

160160
auto op_start = chrono::high_resolution_clock::now();
161161
tree.insertCC(key, keys[i]);
162-
tree.printTree();
162+
cout << i << endl;
163163
auto op_stop = chrono::high_resolution_clock::now();
164164

165165
auto duration = chrono::duration_cast<chrono::nanoseconds>(op_stop - op_start);
166166
total_stats.insertion_time += duration.count();
167167
total_stats.keys_processed++;
168168
}
169+
cout << "insert ended" << endl;
170+
/*
171+
uint8_t key[4];
172+
ART::loadKey(keys[433373], key);
173+
tree.insertCC(key, keys[433373]);
174+
*/
175+
169176
} else {
170177
// Multi-threaded for remaining keys
171178
vector<thread> threads;

0 commit comments

Comments
 (0)