# HG changeset patch # User stuefe # Date 1585137317 -3600 # Wed Mar 25 12:55:17 2020 +0100 # Node ID 8450f1d9f8da8aa227bec460e072a48096b4ef56 # Parent 9cff5368d25a95e39cc82d02e6db2a839097e303 [mq]: bitmap-arbitrary-range-popcount diff -r 9cff5368d25a -r 8450f1d9f8da src/hotspot/share/utilities/bitMap.cpp --- a/src/hotspot/share/utilities/bitMap.cpp Wed Mar 25 07:58:17 2020 +0100 +++ b/src/hotspot/share/utilities/bitMap.cpp Wed Mar 25 12:55:17 2020 +0100 @@ -647,15 +647,58 @@ 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))); diff -r 9cff5368d25a -r 8450f1d9f8da src/hotspot/share/utilities/bitMap.hpp --- a/src/hotspot/share/utilities/bitMap.hpp Wed Mar 25 07:58:17 2020 +0100 +++ b/src/hotspot/share/utilities/bitMap.hpp Wed Mar 25 12:55:17 2020 +0100 @@ -158,6 +158,9 @@ 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 @@ // 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); diff -r 9cff5368d25a -r 8450f1d9f8da test/hotspot/gtest/utilities/test_bitMap_popcnt.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/hotspot/gtest/utilities/test_bitMap_popcnt.cpp Wed Mar 25 12:55:17 2020 +0100 @@ -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); + +} +