Skip to content

Commit 6ee41c4

Browse files
committed
Inline HABSOps in HABS
1 parent 9efd43e commit 6ee41c4

File tree

2 files changed

+160
-248
lines changed

2 files changed

+160
-248
lines changed

core/src/main/java/org/neo4j/gds/core/utils/paged/HugeAtomicBitSet.java

Lines changed: 160 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -53,36 +53,116 @@ private HugeAtomicBitSet(HugeAtomicLongArray bits, long numBits, int remainder)
5353
* Returns the state of the bit at the given index.
5454
*/
5555
public boolean get(long index) {
56-
return HugeAtomicBitSetOps.get(bits, numBits, index);
56+
assert (index < numBits);
57+
long wordIndex = index / NUM_BITS;
58+
int bitIndex = (int) (index % NUM_BITS);
59+
long bitmask = 1L << bitIndex;
60+
return (bits.get(wordIndex) & bitmask) != 0;
5761
}
5862

5963
/**
6064
* Sets the bit at the given index to true.
6165
*/
6266
public void set(long index) {
63-
HugeAtomicBitSetOps.set(bits, numBits, index);
67+
assert (index < numBits);
68+
69+
long wordIndex = index / NUM_BITS;
70+
int bitIndex = (int) (index % NUM_BITS);
71+
long bitmask = 1L << bitIndex;
72+
73+
long oldWord = bits.get(wordIndex);
74+
while (true) {
75+
long newWord = oldWord | bitmask;
76+
if (newWord == oldWord) {
77+
// nothing to set
78+
return;
79+
}
80+
long currentWord = bits.compareAndExchange(wordIndex, oldWord, newWord);
81+
if (currentWord == oldWord) {
82+
// CAS successful
83+
return;
84+
}
85+
// CAS unsuccessful, try again
86+
oldWord = currentWord;
87+
}
6488
}
6589

6690
/**
6791
* Sets the bits from the startIndex (inclusive) to the endIndex (exclusive).
6892
*/
6993
public void set(long startIndex, long endIndex) {
70-
HugeAtomicBitSetOps.setRange(bits, numBits, startIndex, endIndex);
94+
assert (startIndex <= endIndex);
95+
assert (endIndex <= numBits);
96+
97+
long startWordIndex = startIndex / NUM_BITS;
98+
// since endIndex is exclusive, we need the word before that index
99+
long endWordIndex = (endIndex - 1) / NUM_BITS;
100+
101+
long startBitMask = -1L << startIndex;
102+
long endBitMask = -1L >>> -endIndex;
103+
104+
if (startWordIndex == endWordIndex) {
105+
// set within single word
106+
setWord(bits, startWordIndex, startBitMask & endBitMask);
107+
} else {
108+
// set within range
109+
setWord(bits, startWordIndex, startBitMask);
110+
for (long wordIndex = startWordIndex + 1; wordIndex < endWordIndex; wordIndex++) {
111+
bits.set(wordIndex, -1L);
112+
}
113+
setWord(bits, endWordIndex, endBitMask);
114+
}
71115
}
72116

73117
/**
74118
* Sets a bit and returns the previous value.
75119
* The index should be less than the BitSet size.
76120
*/
77121
public boolean getAndSet(long index) {
78-
return HugeAtomicBitSetOps.getAndSet(bits, numBits, index);
122+
assert (index < numBits);
123+
124+
long wordIndex = index / NUM_BITS;
125+
int bitIndex = (int) (index % NUM_BITS);
126+
long bitmask = 1L << bitIndex;
127+
128+
long oldWord = bits.get(wordIndex);
129+
while (true) {
130+
long newWord = oldWord | bitmask;
131+
if (newWord == oldWord) {
132+
// already set
133+
return true;
134+
}
135+
long currentWord = bits.compareAndExchange(wordIndex, oldWord, newWord);
136+
if (currentWord == oldWord) {
137+
// CAS successful
138+
return false;
139+
}
140+
// CAS unsuccessful, try again
141+
oldWord = currentWord;
142+
}
79143
}
80144

81145
/**
82146
* Toggles the bit at the given index.
83147
*/
84148
public void flip(long index) {
85-
HugeAtomicBitSetOps.flip(bits, numBits, index);
149+
assert (index < numBits);
150+
151+
long wordIndex = index / NUM_BITS;
152+
int bitIndex = (int) (index % NUM_BITS);
153+
long bitmask = 1L << bitIndex;
154+
155+
long oldWord = bits.get(wordIndex);
156+
while (true) {
157+
long newWord = oldWord ^ bitmask;
158+
long currentWord = bits.compareAndExchange(wordIndex, oldWord, newWord);
159+
if (currentWord == oldWord) {
160+
// CAS successful
161+
return;
162+
}
163+
// CAS unsuccessful, try again
164+
oldWord = currentWord;
165+
}
86166
}
87167

88168
/**
@@ -91,7 +171,23 @@ public void flip(long index) {
91171
* This method is not thread-safe.
92172
*/
93173
public void forEachSetBit(LongConsumer consumer) {
94-
HugeAtomicBitSetOps.forEachSetBit(bits, consumer);
174+
var cursor = bits.initCursor(bits.newCursor());
175+
176+
while (cursor.next()) {
177+
long[] block = cursor.array;
178+
int offset = cursor.offset;
179+
int limit = cursor.limit;
180+
long base = cursor.base;
181+
182+
for (int i = offset; i < limit; i++) {
183+
long word = block[i];
184+
while (word != 0) {
185+
long next = Long.numberOfTrailingZeros(word);
186+
consumer.accept(Long.SIZE * (base + i) + next);
187+
word = word ^ Long.lowestOneBit(word);
188+
}
189+
}
190+
}
95191
}
96192

97193
/**
@@ -100,7 +196,13 @@ public void forEachSetBit(LongConsumer consumer) {
100196
* Note: this method is not thread-safe.
101197
*/
102198
public long cardinality() {
103-
return HugeAtomicBitSetOps.cardinality(bits);
199+
long setBitCount = 0;
200+
201+
for (long wordIndex = 0; wordIndex < bits.size(); wordIndex++) {
202+
setBitCount += Long.bitCount(bits.get(wordIndex));
203+
}
204+
205+
return setBitCount;
104206
}
105207

106208
/**
@@ -109,7 +211,12 @@ public long cardinality() {
109211
* Note: this method is not thread-safe.
110212
*/
111213
public boolean isEmpty() {
112-
return HugeAtomicBitSetOps.isEmpty(bits);
214+
for (long wordIndex = 0; wordIndex < bits.size(); wordIndex++) {
215+
if (Long.bitCount(bits.get(wordIndex)) > 0) {
216+
return false;
217+
}
218+
}
219+
return true;
113220
}
114221

115222
/**
@@ -118,7 +225,12 @@ public boolean isEmpty() {
118225
* Note: this method is not thread-safe.
119226
*/
120227
public boolean allSet() {
121-
return HugeAtomicBitSetOps.allSet(bits, remainder);
228+
for (long wordIndex = 0; wordIndex < bits.size() - 1; wordIndex++) {
229+
if (Long.bitCount(bits.get(wordIndex)) < NUM_BITS) {
230+
return false;
231+
}
232+
}
233+
return Long.bitCount(bits.get(bits.size() - 1)) >= (long) remainder;
122234
}
123235

124236
/**
@@ -134,13 +246,50 @@ public long size() {
134246
* Note: this method is not thread-safe.
135247
*/
136248
public void clear() {
137-
HugeAtomicBitSetOps.clear(bits);
249+
bits.setAll(0);
138250
}
139251

140252
/**
141253
* Resets the bit at the given index.
142254
*/
143255
public void clear(long index) {
144-
HugeAtomicBitSetOps.clear(bits, numBits, index);
256+
assert (index < numBits);
257+
258+
long wordIndex = index / NUM_BITS;
259+
int bitIndex = (int) (index % NUM_BITS);
260+
long bitmask = ~(1L << bitIndex);
261+
262+
long oldWord = bits.get(wordIndex);
263+
while (true) {
264+
long newWord = oldWord & bitmask;
265+
if (newWord == oldWord) {
266+
// already cleared
267+
return;
268+
}
269+
long currentWord = bits.compareAndExchange(wordIndex, oldWord, newWord);
270+
if (currentWord == oldWord) {
271+
// CAS successful
272+
return;
273+
}
274+
// CAS unsuccessful, try again
275+
oldWord = currentWord;
276+
}
277+
}
278+
279+
private static void setWord(HugeAtomicLongArray bits, long wordIndex, long bitMask) {
280+
var oldWord = bits.get(wordIndex);
281+
while (true) {
282+
var newWord = oldWord | bitMask;
283+
if (newWord == oldWord) {
284+
// already set
285+
return;
286+
}
287+
var currentWord = bits.compareAndExchange(wordIndex, oldWord, newWord);
288+
if (currentWord == oldWord) {
289+
// CAX successful
290+
return;
291+
}
292+
oldWord = currentWord;
293+
}
145294
}
146295
}

0 commit comments

Comments
 (0)