Skip to content

Commit 2d0d057

Browse files
author
Kim Barrett
committed
8304016: Add BitMap find_last suite of functions
Reviewed-by: stefank, aboldtch
1 parent 42723dc commit 2d0d057

File tree

3 files changed

+204
-64
lines changed

3 files changed

+204
-64
lines changed

src/hotspot/share/utilities/bitMap.hpp

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -98,10 +98,19 @@ class BitMap {
9898
// - flip designates whether searching for 1s or 0s. Must be one of
9999
// find_{zeros,ones}_flip.
100100
// - aligned_right is true if end is a priori on a bm_word_t boundary.
101+
// - returns end if not found.
101102
template<bm_word_t flip, bool aligned_right>
102103
inline idx_t find_first_bit_impl(idx_t beg, idx_t end) const;
103104

104-
// Values for find_first_bit_impl flip parameter.
105+
// Helper for find_last_{set,clear}_bit variants.
106+
// - flip designates whether searching for 1s or 0s. Must be one of
107+
// find_{zeros,ones}_flip.
108+
// - aligned_left is true if beg is a priori on a bm_word_t boundary.
109+
// - returns end if not found.
110+
template<bm_word_t flip, bool aligned_left>
111+
inline idx_t find_last_bit_impl(idx_t beg, idx_t end) const;
112+
113+
// Values for find_{first,last}_bit_impl flip parameter.
105114
static const bm_word_t find_ones_flip = 0;
106115
static const bm_word_t find_zeros_flip = ~(bm_word_t)0;
107116

@@ -287,9 +296,9 @@ class BitMap {
287296
return iterate(cl, 0, size());
288297
}
289298

290-
// Looking for 1's and 0's at indices equal to or greater than "beg",
291-
// stopping if none has been found before "end", and returning
292-
// "end" (which must be at most "size") in that case.
299+
// Return the index of the first set (or clear) bit in the range [beg, end),
300+
// or end if none found.
301+
// precondition: beg and end form a valid range for the bitmap.
293302
idx_t find_first_set_bit(idx_t beg, idx_t end) const;
294303
idx_t find_first_clear_bit(idx_t beg, idx_t end) const;
295304

@@ -304,6 +313,23 @@ class BitMap {
304313
// aligned to bitsizeof(bm_word_t).
305314
idx_t find_first_set_bit_aligned_right(idx_t beg, idx_t end) const;
306315

316+
// Return the index of the last set (or clear) bit in the range [beg, end),
317+
// or end if none found.
318+
// precondition: beg and end form a valid range for the bitmap.
319+
idx_t find_last_set_bit(idx_t beg, idx_t end) const;
320+
idx_t find_last_clear_bit(idx_t beg, idx_t end) const;
321+
322+
idx_t find_last_set_bit(idx_t beg) const {
323+
return find_last_set_bit(beg, size());
324+
}
325+
idx_t find_last_clear_bit(idx_t beg) const {
326+
return find_last_clear_bit(beg, size());
327+
}
328+
329+
// Like "find_last_set_bit", except requires that "beg" is
330+
// aligned to bitsizeof(bm_word_t).
331+
idx_t find_last_set_bit_aligned_left(idx_t beg, idx_t end) const;
332+
307333
// Returns the number of bits set in the bitmap.
308334
idx_t count_one_bits() const;
309335

src/hotspot/share/utilities/bitMap.inline.hpp

Lines changed: 102 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include "runtime/atomic.hpp"
3131
#include "utilities/align.hpp"
3232
#include "utilities/count_trailing_zeros.hpp"
33+
#include "utilities/powerOfTwo.hpp"
3334

3435
inline void BitMap::set_bit(idx_t bit) {
3536
verify_index(bit);
@@ -165,64 +166,113 @@ inline void BitMap::par_clear_range(idx_t beg, idx_t end, RangeSizeHint hint) {
165166
}
166167
}
167168

169+
// General notes regarding find_{first,last}_bit_impl.
170+
//
171+
// The first (last) word often contains an interesting bit, either due to
172+
// density or because of features of the calling algorithm. So it's important
173+
// to examine that word with a minimum of fuss, minimizing setup time for
174+
// additional words that will be wasted if the that word is indeed
175+
// interesting.
176+
//
177+
// The first (last) bit is similarly often interesting. When it matters
178+
// (density or features of the calling algorithm make it likely that bit is
179+
// set), going straight to counting bits compares poorly to examining that bit
180+
// first; the counting operations can be relatively expensive, plus there is
181+
// the additional range check (unless aligned). But when that bit isn't set,
182+
// the cost of having tested for it is relatively small compared to the rest
183+
// of the search.
184+
//
185+
// The benefit from aligned_right being true is relatively small. It saves an
186+
// operation in the setup of the word search loop. It also eliminates the
187+
// range check on the final result. However, callers often have a comparison
188+
// with end, and inlining may allow the two comparisons to be combined. It is
189+
// important when !aligned_right that return paths either return end or a
190+
// value dominated by a comparison with end. aligned_right is still helpful
191+
// when the caller doesn't have a range check because features of the calling
192+
// algorithm guarantee an interesting bit will be present.
193+
//
194+
// The benefit from aligned_left is even smaller, as there is no savings in
195+
// the setup of the word search loop.
196+
168197
template<BitMap::bm_word_t flip, bool aligned_right>
169198
inline BitMap::idx_t BitMap::find_first_bit_impl(idx_t beg, idx_t end) const {
170199
STATIC_ASSERT(flip == find_ones_flip || flip == find_zeros_flip);
171200
verify_range(beg, end);
172201
assert(!aligned_right || is_aligned(end, BitsPerWord), "end not aligned");
173202

174-
// The first word often contains an interesting bit, either due to
175-
// density or because of features of the calling algorithm. So it's
176-
// important to examine that first word with a minimum of fuss,
177-
// minimizing setup time for later words that will be wasted if the
178-
// first word is indeed interesting.
179-
180-
// The benefit from aligned_right being true is relatively small.
181-
// It saves an operation in the setup for the word search loop.
182-
// It also eliminates the range check on the final result.
183-
// However, callers often have a comparison with end, and
184-
// inlining often allows the two comparisons to be combined; it is
185-
// important when !aligned_right that return paths either return
186-
// end or a value dominated by a comparison with end.
187-
// aligned_right is still helpful when the caller doesn't have a
188-
// range check because features of the calling algorithm guarantee
189-
// an interesting bit will be present.
190-
191203
if (beg < end) {
192204
// Get the word containing beg, and shift out low bits.
193205
idx_t word_index = to_words_align_down(beg);
194206
bm_word_t cword = flipped_word(word_index, flip) >> bit_in_word(beg);
195-
if ((cword & 1) != 0) {
196-
// The first bit is similarly often interesting. When it matters
197-
// (density or features of the calling algorithm make it likely
198-
// the first bit is set), going straight to the next clause compares
199-
// poorly with doing this check first; count_trailing_zeros can be
200-
// relatively expensive, plus there is the additional range check.
201-
// But when the first bit isn't set, the cost of having tested for
202-
// it is relatively small compared to the rest of the search.
207+
if ((cword & 1) != 0) { // Test the beg bit.
203208
return beg;
204-
} else if (cword != 0) {
205-
// Flipped and shifted first word is non-zero.
206-
idx_t result = beg + count_trailing_zeros(cword);
207-
if (aligned_right || (result < end)) return result;
208-
// Result is beyond range bound; return end.
209-
} else {
210-
// Flipped and shifted first word is zero. Word search through
209+
}
210+
// Position of bit0 of cword in the bitmap. Initially for shifted first word.
211+
idx_t cword_pos = beg;
212+
if (cword == 0) { // Test other bits in the first word.
213+
// First word had no interesting bits. Word search through
211214
// aligned up end for a non-zero flipped word.
212215
idx_t word_limit = aligned_right
213216
? to_words_align_down(end) // Minuscule savings when aligned.
214217
: to_words_align_up(end);
215218
while (++word_index < word_limit) {
216219
cword = flipped_word(word_index, flip);
217220
if (cword != 0) {
218-
idx_t result = bit_index(word_index) + count_trailing_zeros(cword);
219-
if (aligned_right || (result < end)) return result;
220-
// Result is beyond range bound; return end.
221-
assert((word_index + 1) == word_limit, "invariant");
221+
// Update for found non-zero word, and join common tail to compute
222+
// result from cword_pos and non-zero cword.
223+
cword_pos = bit_index(word_index);
222224
break;
223225
}
224226
}
225-
// No bits in range; return end.
227+
}
228+
// For all paths reaching here, (cword != 0) is already known, so we
229+
// expect the compiler to not generate any code for it. Either first word
230+
// was non-zero, or found a non-zero word in range, or fully scanned range
231+
// (so cword is zero).
232+
if (cword != 0) {
233+
idx_t result = cword_pos + count_trailing_zeros(cword);
234+
if (aligned_right || (result < end)) return result;
235+
// Result is beyond range bound; return end.
236+
}
237+
}
238+
return end;
239+
}
240+
241+
template<BitMap::bm_word_t flip, bool aligned_left>
242+
inline BitMap::idx_t BitMap::find_last_bit_impl(idx_t beg, idx_t end) const {
243+
STATIC_ASSERT(flip == find_ones_flip || flip == find_zeros_flip);
244+
verify_range(beg, end);
245+
assert(!aligned_left || is_aligned(beg, BitsPerWord), "beg not aligned");
246+
247+
if (beg < end) {
248+
// Get the last partial and flipped word in the range.
249+
idx_t last_bit_index = end - 1;
250+
idx_t word_index = to_words_align_down(last_bit_index);
251+
bm_word_t cword = flipped_word(word_index, flip);
252+
// Mask for extracting and testing bits of last word.
253+
bm_word_t last_bit_mask = bm_word_t(1) << bit_in_word(last_bit_index);
254+
if ((cword & last_bit_mask) != 0) { // Test last bit.
255+
return last_bit_index;
256+
}
257+
// Extract prior bits, clearing those above last_bit_index.
258+
cword &= (last_bit_mask - 1);
259+
if (cword == 0) { // Test other bits in the last word.
260+
// Last word had no interesting bits. Word search through
261+
// aligned down beg for a non-zero flipped word.
262+
idx_t word_limit = to_words_align_down(beg);
263+
while (word_index-- > word_limit) {
264+
cword = flipped_word(word_index, flip);
265+
if (cword != 0) break;
266+
}
267+
}
268+
// For all paths reaching here, (cword != 0) is already known, so we
269+
// expect the compiler to not generate any code for it. Either last word
270+
// was non-zero, or found a non-zero word in range, or fully scanned range
271+
// (so cword is zero).
272+
if (cword != 0) {
273+
idx_t result = bit_index(word_index) + log2i(cword);
274+
if (aligned_left || (result >= beg)) return result;
275+
// Result is below range bound; return end.
226276
}
227277
}
228278
return end;
@@ -243,6 +293,21 @@ BitMap::find_first_set_bit_aligned_right(idx_t beg, idx_t end) const {
243293
return find_first_bit_impl<find_ones_flip, true>(beg, end);
244294
}
245295

296+
inline BitMap::idx_t
297+
BitMap::find_last_set_bit(idx_t beg, idx_t end) const {
298+
return find_last_bit_impl<find_ones_flip, false>(beg, end);
299+
}
300+
301+
inline BitMap::idx_t
302+
BitMap::find_last_clear_bit(idx_t beg, idx_t end) const {
303+
return find_last_bit_impl<find_zeros_flip, false>(beg, end);
304+
}
305+
306+
inline BitMap::idx_t
307+
BitMap::find_last_set_bit_aligned_left(idx_t beg, idx_t end) const {
308+
return find_last_bit_impl<find_ones_flip, true>(beg, end);
309+
}
310+
246311
// IterateInvoker supports conditionally stopping iteration early. The
247312
// invoker is called with the function to apply to each set index, along with
248313
// the current index. If the function returns void then the invoker always

test/hotspot/gtest/utilities/test_bitMap_search.cpp

Lines changed: 72 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -91,21 +91,34 @@ bool TestIteratorFn::do_bit(size_t offset) {
9191
return true;
9292
}
9393

94-
static idx_t compute_expected(idx_t search_start,
95-
idx_t search_end,
96-
idx_t left_bit,
97-
idx_t right_bit) {
98-
idx_t expected = search_end;
99-
if (search_start <= left_bit) {
100-
if (left_bit < search_end) {
101-
expected = left_bit;
102-
}
103-
} else if (search_start <= right_bit) {
104-
if (right_bit < search_end) {
105-
expected = right_bit;
106-
}
94+
static bool is_bit_in_range(idx_t bit, idx_t beg, idx_t end) {
95+
return (beg <= bit) && (bit < end);
96+
}
97+
98+
static idx_t compute_first_expected(idx_t search_start,
99+
idx_t search_end,
100+
idx_t left_bit,
101+
idx_t right_bit) {
102+
if (is_bit_in_range(left_bit, search_start, search_end)) {
103+
return left_bit;
104+
} else if (is_bit_in_range(right_bit, search_start, search_end)) {
105+
return right_bit;
106+
} else {
107+
return search_end;
108+
}
109+
}
110+
111+
static idx_t compute_last_expected(idx_t search_start,
112+
idx_t search_end,
113+
idx_t left_bit,
114+
idx_t right_bit) {
115+
if (is_bit_in_range(right_bit, search_start, search_end)) {
116+
return right_bit;
117+
} else if (is_bit_in_range(left_bit, search_start, search_end)) {
118+
return left_bit;
119+
} else {
120+
return search_end;
107121
}
108-
return expected;
109122
}
110123

111124
static void test_search_ranges(BitMap& test_ones,
@@ -127,6 +140,21 @@ static void test_search_ranges(BitMap& test_ones,
127140
EXPECT_EQ(right, test_zeros.find_first_clear_bit(left + 1));
128141
EXPECT_EQ(BITMAP_SIZE, test_zeros.find_first_clear_bit(right + 1));
129142

143+
// Test find_last_set_bit with full range of map.
144+
EXPECT_EQ(right, test_ones.find_last_set_bit(0));
145+
EXPECT_EQ(left, test_ones.find_last_set_bit(0, right));
146+
EXPECT_EQ(left, test_ones.find_last_set_bit(0, left));
147+
148+
// Test find_last_set_bit_aligned_left with full range of map.
149+
EXPECT_EQ(right, test_ones.find_last_set_bit_aligned_left(0, BITMAP_SIZE));
150+
EXPECT_EQ(left, test_ones.find_last_set_bit_aligned_left(0, right));
151+
EXPECT_EQ(left, test_ones.find_last_set_bit_aligned_left(0, left));
152+
153+
// Test find_last_clear_bit with full range of map.
154+
EXPECT_EQ(right, test_zeros.find_last_clear_bit(0));
155+
EXPECT_EQ(left, test_zeros.find_last_clear_bit(0, right));
156+
EXPECT_EQ(left, test_zeros.find_last_clear_bit(0, left));
157+
130158
// Check that iterate invokes the closure function on left and right values.
131159
TestIteratorFn test_iteration(0, BITMAP_SIZE, left, right);
132160
test_ones.iterate(&test_iteration, 0, BITMAP_SIZE);
@@ -165,29 +193,50 @@ static void test_search_ranges(BitMap& test_ones,
165193
}
166194

167195
bool aligned_right = search_offsets[o_end] == 0;
196+
bool aligned_left = search_offsets[o_start] == 0;
168197
ASSERT_LE(start, end); // test bug if fail
169198
ASSERT_LT(end, BITMAP_SIZE); // test bug if fail
170199

171-
idx_t expected = compute_expected(start, end, left, right);
200+
idx_t first_expected = compute_first_expected(start, end, left, right);
201+
idx_t last_expected = compute_last_expected(start, end, left, right);
172202

173-
EXPECT_EQ(expected, test_ones.find_first_set_bit(start, end));
174-
EXPECT_EQ(expected, test_zeros.find_first_clear_bit(start, end));
203+
EXPECT_EQ(first_expected, test_ones.find_first_set_bit(start, end));
204+
EXPECT_EQ(first_expected, test_zeros.find_first_clear_bit(start, end));
175205
if (aligned_right) {
176206
EXPECT_EQ(
177-
expected,
207+
first_expected,
178208
test_ones.find_first_set_bit_aligned_right(start, end));
179209
}
180210

181-
idx_t start2 = MIN2(expected + 1, end);
182-
idx_t expected2 = compute_expected(start2, end, left, right);
211+
EXPECT_EQ(last_expected, test_ones.find_last_set_bit(start, end));
212+
EXPECT_EQ(last_expected, test_zeros.find_last_clear_bit(start, end));
213+
if (aligned_left) {
214+
EXPECT_EQ(
215+
last_expected,
216+
test_ones.find_last_set_bit_aligned_left(start, end));
217+
}
218+
219+
idx_t start2 = MIN2(first_expected + 1, end);
220+
idx_t first_expected2 = compute_first_expected(start2, end, left, right);
183221

184-
EXPECT_EQ(expected2, test_ones.find_first_set_bit(start2, end));
185-
EXPECT_EQ(expected2, test_zeros.find_first_clear_bit(start2, end));
222+
idx_t end2 = MAX2(start, last_expected);
223+
idx_t last_expected2 = compute_last_expected(start, end2, left, right);
224+
225+
EXPECT_EQ(first_expected2, test_ones.find_first_set_bit(start2, end));
226+
EXPECT_EQ(first_expected2, test_zeros.find_first_clear_bit(start2, end));
186227
if (aligned_right) {
187228
EXPECT_EQ(
188-
expected2,
229+
first_expected2,
189230
test_ones.find_first_set_bit_aligned_right(start2, end));
190231
}
232+
233+
EXPECT_EQ(last_expected2, test_ones.find_last_set_bit(start, end2));
234+
EXPECT_EQ(last_expected2, test_zeros.find_last_clear_bit(start, end2));
235+
if (aligned_left) {
236+
EXPECT_EQ(
237+
last_expected2,
238+
test_ones.find_last_set_bit_aligned_left(start, end2));
239+
}
191240
}
192241
}
193242
}

0 commit comments

Comments
 (0)