changeset 58573:55b8fc5c9052

8241581: Add BitMap::count_one_bits variant for arbitrary lengths Reviewed-by: redestad, kbarrett
author stuefe
date Fri, 27 Mar 2020 07:16:22 +0100
parents 439a44cd1ee8
children 2bf2f8eead7d
files src/hotspot/share/utilities/bitMap.cpp src/hotspot/share/utilities/bitMap.hpp test/hotspot/gtest/utilities/test_bitMap_popcnt.cpp
diffstat 3 files changed, 247 insertions(+), 2 deletions(-) [+]
line wrap: on
line diff
--- a/src/hotspot/share/utilities/bitMap.cpp	Fri Mar 27 11:34:45 2020 +0530
+++ b/src/hotspot/share/utilities/bitMap.cpp	Fri Mar 27 07:16:22 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)));
--- a/src/hotspot/share/utilities/bitMap.hpp	Fri Mar 27 11:34:45 2020 +0530
+++ b/src/hotspot/share/utilities/bitMap.hpp	Fri Mar 27 07:16:22 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);
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/hotspot/gtest/utilities/test_bitMap_popcnt.cpp	Fri Mar 27 07:16:22 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);
+
+}
+