Skip to content
Permalink
Browse files

8241581: Add BitMap::count_one_bits variant for arbitrary lengths

Reviewed-by: redestad, kbarrett
  • Loading branch information
tstuefe committed Mar 27, 2020
1 parent 2bc4308 commit cc8715d8449a8908ec4685a3cbffbae2d6aca3a2
@@ -647,15 +647,58 @@ bool BitMap::iterate(BitMapClosure* blk, idx_t leftOffset, idx_t rightOffset) {
return true;
}

BitMap::idx_t BitMap::count_one_bits() const {
BitMap::idx_t BitMap::count_one_bits_in_range_of_words(idx_t beg_full_word, idx_t end_full_word) const {
idx_t sum = 0;
for (idx_t i = 0; i < size_in_words(); i++) {
for (idx_t i = beg_full_word; i < end_full_word; i++) {
bm_word_t w = map()[i];
sum += population_count(w);
}
return sum;
}

BitMap::idx_t BitMap::count_one_bits_within_word(idx_t beg, idx_t end) const {
if (beg != end) {
assert(end > beg, "must be");
bm_word_t mask = ~inverted_bit_mask_for_range(beg, end);
bm_word_t w = *word_addr(beg);
w &= mask;
return population_count(w);
}
return 0;
}

BitMap::idx_t BitMap::count_one_bits() const {
return count_one_bits_in_range_of_words(0, size_in_words());
}

// Returns the number of bits set within [beg, end).
BitMap::idx_t BitMap::count_one_bits(idx_t beg, idx_t end) const {

verify_range(beg, end);

idx_t beg_full_word = to_words_align_up(beg);
idx_t end_full_word = to_words_align_down(end);

idx_t sum = 0;

if (beg_full_word < end_full_word) {
// The range includes at least one full word.
sum += count_one_bits_within_word(beg, bit_index(beg_full_word));
sum += count_one_bits_in_range_of_words(beg_full_word, end_full_word);
sum += count_one_bits_within_word(bit_index(end_full_word), end);
} else {
// The range spans at most 2 partial words.
idx_t boundary = MIN2(bit_index(beg_full_word), end);
sum += count_one_bits_within_word(beg, boundary);
sum += count_one_bits_within_word(boundary, end);
}

assert(sum <= (beg - end), "must be");

return sum;

}

void BitMap::print_on_error(outputStream* st, const char* prefix) const {
st->print_cr("%s[" PTR_FORMAT ", " PTR_FORMAT ")",
prefix, p2i(map()), p2i((char*)map() + (size() >> LogBitsPerByte)));
@@ -158,6 +158,9 @@ class BitMap {

static void clear_range_of_words(bm_word_t* map, idx_t beg, idx_t end);

idx_t count_one_bits_within_word(idx_t beg, idx_t end) const;
idx_t count_one_bits_in_range_of_words(idx_t beg_full_word, idx_t end_full_word) const;

// Verification.

// Verify size_in_bits does not exceed max_size_in_bits().
@@ -310,6 +313,9 @@ class BitMap {
// Returns the number of bits set in the bitmap.
idx_t count_one_bits() const;

// Returns the number of bits set within [beg, end).
idx_t count_one_bits(idx_t beg, idx_t end) const;

// Set operations.
void set_union(const BitMap& bits);
void set_difference(const BitMap& bits);
@@ -0,0 +1,196 @@
/*
* Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2020, SAP and/or its affiliates.
*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

#include "precompiled.hpp"
#include "memory/allocation.hpp"
#include "runtime/os.hpp"
#include "utilities/bitMap.inline.hpp"
#include "utilities/ostream.hpp"
#include "unittest.hpp"

// A simple stupid bitmap to mimic BitMap operations.
class SimpleFakeBitmap {

const int _size;
char* _buffer;

public:

SimpleFakeBitmap(int size) : _size(size), _buffer(NEW_C_HEAP_ARRAY(char, size, mtInternal)) {
clear();
}

~SimpleFakeBitmap() {
FREE_C_HEAP_ARRAY(char, _buffer);
}

// Set or clear the specified bit.
void set_range(int beg, int end) { ::memset(_buffer + beg, 'X', end - beg); }
void clear_range(int beg, int end) { ::memset(_buffer + beg, ' ', end - beg); }
void clear() { clear_range(0, _size); }

bool at(int idx) const { return _buffer[idx] == 'X'; }

unsigned popcnt(int beg, int end) const {
int sum = 0;
for (int i = beg; i < end; i ++) {
if (at(i)) {
sum ++;
}
}
return sum;
}

unsigned popcnt() const { return popcnt(0, _size); }

};

#define ASSERT_POPCNT_ALL(bm, expected) \
ASSERT_EQ(bm.count_one_bits(), (BitMap::idx_t)(expected));

#define ASSERT_POPCNT_RANGE(bm, beg, end, expected) \
ASSERT_EQ(bm.count_one_bits(beg, end), (BitMap::idx_t)(expected));

#define ASSERT_POPCNT_ALL_CMP(bm, fake_bm) \
ASSERT_EQ(bm.count_one_bits(), fake_bm.popcnt());

#define ASSERT_POPCNT_RANGE_CMP(bm, beg, end, fake_bm) \
ASSERT_EQ(bm.count_one_bits(beg, end), fake_bm.popcnt(beg, end));

// Just a small stack obj to print error details if we fail.
struct ErrorDetailsPrinter {
BitMap& _bm;
const int _beg;
const int _end;
int _beg_qu;
int _end_qu;
bool _armed;

ErrorDetailsPrinter(BitMap& bm, int beg, int end)
: _bm(bm), _beg(beg), _end(end), _beg_qu(-1), _end_qu(-1), _armed(true) {}
~ErrorDetailsPrinter() {
if (_armed) {
tty->print_cr("beg %d end %d beg_qu %d, end_qu %d\n", _beg, _end, _beg_qu, _end_qu);
_bm.print_on(tty);
}
}
};

static void set_or_clear_random_range(BitMap& bm, SimpleFakeBitmap& fbm, int beg, int end) {
int range = end - beg;
if (range > 0) {
int from = os::random() % range;
int to = os::random() % range;
if (from > to) {
int s = from;
from = to;
to = s;
}
from += beg;
to += beg;
if ((os::random() % 10) > 5) {
bm.set_range(from, to);
fbm.set_range(from, to);
} else {
bm.clear_range(from, to);
fbm.clear_range(from, to);
}
}
}

static void test_bitmap_popcnt(int bitsize) {
CHeapBitMap bm(bitsize);
SimpleFakeBitmap fbm(bitsize);

ASSERT_POPCNT_ALL(bm, 0);
ASSERT_POPCNT_RANGE(bm, 0, 0, 0);
ASSERT_POPCNT_RANGE(bm, 0, bitsize, 0);

const int stepsize = bitsize > 64 ? 5 : 1;

for (int beg = 0; beg < bitsize; beg += stepsize) {
for (int end = beg; end < bitsize; end += stepsize) {
ErrorDetailsPrinter pe(bm, beg, end);

bm.clear();
bm.set_range(beg, end);

fbm.clear();
fbm.set_range(beg, end);

ASSERT_POPCNT_ALL_CMP(bm, fbm);

for (int beg_query_range = 0; beg_query_range < bitsize; beg_query_range += stepsize) {
for (int end_query_range = beg_query_range; end_query_range < bitsize; end_query_range += stepsize) {
pe._beg_qu = beg_query_range;
pe._end_qu = end_query_range;
ASSERT_POPCNT_RANGE_CMP(bm, beg_query_range, end_query_range, fbm);

// Clear some random ranges and retest
for (int n = 0; n < 3; n ++) {
set_or_clear_random_range(bm, fbm, beg, end);
ASSERT_POPCNT_RANGE_CMP(bm, beg_query_range, end_query_range, fbm);
}

}
}

pe._armed = false; // all is well don't print
}
}

}

TEST_VM(BitMap, popcnt_1) { test_bitmap_popcnt(1); }
TEST_VM(BitMap, popcnt_8) { test_bitmap_popcnt(8); }
TEST_VM(BitMap, popcnt_15) { test_bitmap_popcnt(15); }
TEST_VM(BitMap, popcnt_19) { test_bitmap_popcnt(17); }
TEST_VM(BitMap, popcnt_63) { test_bitmap_popcnt(63); }
TEST_VM(BitMap, popcnt_300) { test_bitmap_popcnt(300); }

TEST_VM(BitMap, popcnt_large) {

CHeapBitMap bm(64 * K);

ASSERT_POPCNT_ALL(bm, 0);
ASSERT_POPCNT_RANGE(bm, 0, 64 * K, 0);
ASSERT_POPCNT_RANGE(bm, 47, 199, 0);

bm.set_bit(100);

ASSERT_POPCNT_ALL(bm, 1);
ASSERT_POPCNT_RANGE(bm, 0, 64 * K, 1);
ASSERT_POPCNT_RANGE(bm, 47, 199, 1);
ASSERT_POPCNT_RANGE(bm, 199, 299, 0 );

bm.set_range(0, 64 * K);

ASSERT_POPCNT_ALL(bm, 64 * K);
ASSERT_POPCNT_RANGE(bm, 0, 64 * K, 64 * K);
ASSERT_POPCNT_RANGE(bm, 47, 199, 152);
ASSERT_POPCNT_RANGE(bm, 199, 299, 100);

}

0 comments on commit cc8715d

Please sign in to comment.