8296139: Make GrowableBitMap the base class of all implementations

Reviewed-by: aboldtch, stefank, simonis
This commit is contained in:
Xin Liu 2022-11-09 19:51:54 +00:00
parent cc8bf95046
commit 1b94ae13d3
3 changed files with 138 additions and 196 deletions

View File

@ -31,56 +31,21 @@
#include "utilities/debug.hpp" #include "utilities/debug.hpp"
#include "utilities/population_count.hpp" #include "utilities/population_count.hpp"
STATIC_ASSERT(sizeof(BitMap::bm_word_t) == BytesPerWord); // "Implementation assumption." using bm_word_t = BitMap::bm_word_t;
using idx_t = BitMap::idx_t;
typedef BitMap::bm_word_t bm_word_t; STATIC_ASSERT(sizeof(bm_word_t) == BytesPerWord); // "Implementation assumption."
typedef BitMap::idx_t idx_t;
class ResourceBitMapAllocator : StackObj { template <class BitMapWithAllocator>
public: bm_word_t* GrowableBitMap<BitMapWithAllocator>::reallocate(bm_word_t* old_map, idx_t old_size_in_bits, idx_t new_size_in_bits, bool clear) {
bm_word_t* allocate(idx_t size_in_words) const {
return NEW_RESOURCE_ARRAY(bm_word_t, size_in_words);
}
void free(bm_word_t* map, idx_t size_in_words) const {
// Don't free resource allocated arrays.
}
};
class CHeapBitMapAllocator : StackObj {
MEMFLAGS _flags;
public:
CHeapBitMapAllocator(MEMFLAGS flags) : _flags(flags) {}
bm_word_t* allocate(size_t size_in_words) const {
return ArrayAllocator<bm_word_t>::allocate(size_in_words, _flags);
}
void free(bm_word_t* map, idx_t size_in_words) const {
ArrayAllocator<bm_word_t>::free(map, size_in_words);
}
};
class ArenaBitMapAllocator : StackObj {
Arena* _arena;
public:
ArenaBitMapAllocator(Arena* arena) : _arena(arena) {}
bm_word_t* allocate(idx_t size_in_words) const {
return (bm_word_t*)_arena->Amalloc(size_in_words * BytesPerWord);
}
void free(bm_word_t* map, idx_t size_in_words) const {
// ArenaBitMaps currently don't free memory.
}
};
template <class Allocator>
BitMap::bm_word_t* BitMap::reallocate(const Allocator& allocator, bm_word_t* old_map, idx_t old_size_in_bits, idx_t new_size_in_bits, bool clear) {
size_t old_size_in_words = calc_size_in_words(old_size_in_bits); size_t old_size_in_words = calc_size_in_words(old_size_in_bits);
size_t new_size_in_words = calc_size_in_words(new_size_in_bits); size_t new_size_in_words = calc_size_in_words(new_size_in_bits);
bm_word_t* map = NULL; bm_word_t* map = NULL;
BitMapWithAllocator* derived = static_cast<BitMapWithAllocator*>(this);
if (new_size_in_words > 0) { if (new_size_in_words > 0) {
map = allocator.allocate(new_size_in_words); map = derived->allocate(new_size_in_words);
if (old_map != NULL) { if (old_map != NULL) {
Copy::disjoint_words((HeapWord*)old_map, (HeapWord*) map, Copy::disjoint_words((HeapWord*)old_map, (HeapWord*) map,
@ -99,85 +64,45 @@ BitMap::bm_word_t* BitMap::reallocate(const Allocator& allocator, bm_word_t* old
} }
if (old_map != NULL) { if (old_map != NULL) {
allocator.free(old_map, old_size_in_words); derived->free(old_map, old_size_in_words);
} }
return map; return map;
} }
template <class Allocator> ArenaBitMap::ArenaBitMap(Arena* arena, idx_t size_in_bits, bool clear)
bm_word_t* BitMap::allocate(const Allocator& allocator, idx_t size_in_bits, bool clear) { : GrowableBitMap<ArenaBitMap>(), _arena(arena) {
// Reuse reallocate to ensure that the new memory is cleared. initialize(size_in_bits, clear);
return reallocate(allocator, NULL, 0, size_in_bits, clear);
} }
template <class Allocator> bm_word_t* ArenaBitMap::allocate(idx_t size_in_words) const {
void BitMap::free(const Allocator& allocator, bm_word_t* map, idx_t size_in_bits) { return (bm_word_t*)_arena->Amalloc(size_in_words * BytesPerWord);
bm_word_t* ret = reallocate(allocator, map, size_in_bits, 0);
assert(ret == NULL, "Reallocate shouldn't have allocated");
}
template <class Allocator>
void BitMap::resize(const Allocator& allocator, idx_t new_size_in_bits, bool clear) {
bm_word_t* new_map = reallocate(allocator, map(), size(), new_size_in_bits, clear);
update(new_map, new_size_in_bits);
}
template <class Allocator>
void BitMap::initialize(const Allocator& allocator, idx_t size_in_bits, bool clear) {
assert(map() == NULL, "precondition");
assert(size() == 0, "precondition");
resize(allocator, size_in_bits, clear);
}
template <class Allocator>
void BitMap::reinitialize(const Allocator& allocator, idx_t new_size_in_bits, bool clear) {
// Remove previous bits - no need to clear
resize(allocator, 0, false /* clear */);
initialize(allocator, new_size_in_bits, clear);
} }
ResourceBitMap::ResourceBitMap(idx_t size_in_bits, bool clear) ResourceBitMap::ResourceBitMap(idx_t size_in_bits, bool clear)
: BitMap(allocate(ResourceBitMapAllocator(), size_in_bits, clear), size_in_bits) { : GrowableBitMap<ResourceBitMap>() {
initialize(size_in_bits, clear);
} }
void ResourceBitMap::resize(idx_t new_size_in_bits) { bm_word_t* ResourceBitMap::allocate(idx_t size_in_words) const {
BitMap::resize(ResourceBitMapAllocator(), new_size_in_bits, true /* clear */); return (bm_word_t*)NEW_RESOURCE_ARRAY(bm_word_t, size_in_words);
}
void ResourceBitMap::initialize(idx_t size_in_bits) {
BitMap::initialize(ResourceBitMapAllocator(), size_in_bits, true /* clear */);
}
void ResourceBitMap::reinitialize(idx_t size_in_bits) {
BitMap::reinitialize(ResourceBitMapAllocator(), size_in_bits, true /* clear */);
}
ArenaBitMap::ArenaBitMap(Arena* arena, idx_t size_in_bits)
: BitMap(allocate(ArenaBitMapAllocator(arena), size_in_bits), size_in_bits) {
} }
CHeapBitMap::CHeapBitMap(idx_t size_in_bits, MEMFLAGS flags, bool clear) CHeapBitMap::CHeapBitMap(idx_t size_in_bits, MEMFLAGS flags, bool clear)
: BitMap(allocate(CHeapBitMapAllocator(flags), size_in_bits, clear), size_in_bits), _flags(flags) { : GrowableBitMap<CHeapBitMap>(), _flags(flags) {
initialize(size_in_bits, clear);
} }
CHeapBitMap::~CHeapBitMap() { CHeapBitMap::~CHeapBitMap() {
free(CHeapBitMapAllocator(_flags), map(), size()); free(map(), size());
} }
void CHeapBitMap::resize(idx_t new_size_in_bits, bool clear) { bm_word_t* CHeapBitMap::allocate(idx_t size_in_words) const {
BitMap::resize(CHeapBitMapAllocator(_flags), new_size_in_bits, clear); return ArrayAllocator<bm_word_t>::allocate(size_in_words, _flags);
} }
void CHeapBitMap::initialize(idx_t size_in_bits, bool clear) { void CHeapBitMap::free(bm_word_t* map, idx_t size_in_words) const {
BitMap::initialize(CHeapBitMapAllocator(_flags), size_in_bits, clear); ArrayAllocator<bm_word_t>::free(map, size_in_words);
}
void CHeapBitMap::reinitialize(idx_t size_in_bits, bool clear) {
BitMap::reinitialize(CHeapBitMapAllocator(_flags), size_in_bits, clear);
} }
#ifdef ASSERT #ifdef ASSERT
@ -706,3 +631,7 @@ void BitMap::print_on(outputStream* st) const {
} }
#endif #endif
template class GrowableBitMap<ArenaBitMap>;
template class GrowableBitMap<ResourceBitMap>;
template class GrowableBitMap<CHeapBitMap>;

View File

@ -64,6 +64,7 @@ class BitMap {
bm_word_t* _map; // First word in bitmap bm_word_t* _map; // First word in bitmap
idx_t _size; // Size of bitmap (in bits) idx_t _size; // Size of bitmap (in bits)
protected:
// The maximum allowable size of a bitmap, in words or bits. // The maximum allowable size of a bitmap, in words or bits.
// Limit max_size_in_bits so aligning up to a word boundary never overflows. // Limit max_size_in_bits so aligning up to a word boundary never overflows.
static idx_t max_size_in_words() { return raw_to_words_align_down(~idx_t(0)); } static idx_t max_size_in_words() { return raw_to_words_align_down(~idx_t(0)); }
@ -110,7 +111,6 @@ class BitMap {
static bool is_small_range_of_words(idx_t beg_full_word, idx_t end_full_word); static bool is_small_range_of_words(idx_t beg_full_word, idx_t end_full_word);
protected:
// Return the position of bit within the word that contains it (e.g., if // Return the position of bit within the word that contains it (e.g., if
// bitmap words are 32 bits, return a number 0 <= n <= 31). // bitmap words are 32 bits, return a number 0 <= n <= 31).
static idx_t bit_in_word(idx_t bit) { return bit & (BitsPerWord - 1); } static idx_t bit_in_word(idx_t bit) { return bit & (BitsPerWord - 1); }
@ -161,42 +161,6 @@ class BitMap {
idx_t count_one_bits_within_word(idx_t beg, idx_t end) const; 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; idx_t count_one_bits_in_range_of_words(idx_t beg_full_word, idx_t end_full_word) const;
// Allocation Helpers.
// Allocates and clears the bitmap memory.
template <class Allocator>
static bm_word_t* allocate(const Allocator&, idx_t size_in_bits, bool clear = true);
// Reallocates and clears the new bitmap memory.
template <class Allocator>
static bm_word_t* reallocate(const Allocator&, bm_word_t* map, idx_t old_size_in_bits, idx_t new_size_in_bits, bool clear = true);
// Free the bitmap memory.
template <class Allocator>
static void free(const Allocator&, bm_word_t* map, idx_t size_in_bits);
// Protected functions, that are used by BitMap sub-classes that support them.
// Resize the backing bitmap memory.
//
// Old bits are transferred to the new memory
// and the extended memory is cleared.
template <class Allocator>
void resize(const Allocator& allocator, idx_t new_size_in_bits, bool clear);
// Set up and clear the bitmap memory.
//
// Precondition: The bitmap was default constructed and has
// not yet had memory allocated via resize or (re)initialize.
template <class Allocator>
void initialize(const Allocator& allocator, idx_t size_in_bits, bool clear);
// Set up and clear the bitmap memory.
//
// Can be called on previously initialized bitmaps.
template <class Allocator>
void reinitialize(const Allocator& allocator, idx_t new_size_in_bits, bool clear);
// Set the map and size. // Set the map and size.
void update(bm_word_t* map, idx_t size) { void update(bm_word_t* map, idx_t size) {
_map = map; _map = map;
@ -354,84 +318,108 @@ class BitMap {
#endif #endif
}; };
// A concrete implementation of the "abstract" BitMap class. // CRTP: BitmapWithAllocator exposes the following Allocator interfaces upward to GrowableBitMap.
// //
// The BitMapView is used when the backing storage is managed externally. // bm_word_t* allocate(idx_t size_in_words) const;
class BitMapView : public BitMap { // void free(bm_word_t* map, idx_t size_in_words) const
public: //
BitMapView() : BitMap(NULL, 0) {} template <class BitMapWithAllocator>
BitMapView(bm_word_t* map, idx_t size_in_bits) : BitMap(map, size_in_bits) {} class GrowableBitMap : public BitMap {
}; protected:
bm_word_t* reallocate(bm_word_t* map, idx_t old_size_in_bits, idx_t new_size_in_bits, bool clear);
// A BitMap with storage in a ResourceArea. GrowableBitMap() : GrowableBitMap(nullptr, 0) {}
class ResourceBitMap : public BitMap { GrowableBitMap(bm_word_t* map, idx_t size_in_bits) : BitMap(map, size_in_bits) {}
public: public:
ResourceBitMap() : BitMap(NULL, 0) {} // Set up and clear the bitmap memory.
// Conditionally clears the bitmap memory. //
ResourceBitMap(idx_t size_in_bits, bool clear = true); // Precondition: The bitmap was default constructed and has
// not yet had memory allocated via resize or (re)initialize.
void initialize(idx_t size_in_bits, bool clear = true) {
assert(map() == NULL, "precondition");
assert(size() == 0, "precondition");
resize(size_in_bits, clear);
}
// Set up and clear the bitmap memory.
//
// Can be called on previously initialized bitmaps.
void reinitialize(idx_t new_size_in_bits, bool clear = true) {
// Remove previous bits - no need to clear
resize(0, false /* clear */);
initialize(new_size_in_bits, clear);
}
// Protected functions, that are used by BitMap sub-classes that support them.
// Resize the backing bitmap memory. // Resize the backing bitmap memory.
// //
// Old bits are transferred to the new memory // Old bits are transferred to the new memory
// and the extended memory is cleared. // and the extended memory is cleared.
void resize(idx_t new_size_in_bits); void resize(idx_t new_size_in_bits, bool clear = true) {
bm_word_t* new_map = reallocate(map(), size(), new_size_in_bits, clear);
update(new_map, new_size_in_bits);
}
};
// Set up and clear the bitmap memory. // A concrete implementation of the "abstract" BitMap class.
// //
// Precondition: The bitmap was default constructed and has // The BitMapView is used when the backing storage is managed externally.
// not yet had memory allocated via resize or initialize. class BitMapView : public BitMap {
void initialize(idx_t size_in_bits); public:
BitMapView() : BitMapView(nullptr, 0) {}
// Set up and clear the bitmap memory. BitMapView(bm_word_t* map, idx_t size_in_bits) : BitMap(map, size_in_bits) {}
//
// Can be called on previously initialized bitmaps.
void reinitialize(idx_t size_in_bits);
}; };
// A BitMap with storage in a specific Arena. // A BitMap with storage in a specific Arena.
class ArenaBitMap : public BitMap { class ArenaBitMap : public GrowableBitMap<ArenaBitMap> {
Arena* const _arena;
NONCOPYABLE(ArenaBitMap);
public: public:
// Clears the bitmap memory. // Clears the bitmap memory.
ArenaBitMap(Arena* arena, idx_t size_in_bits); ArenaBitMap(Arena* arena, idx_t size_in_bits, bool clear = true);
private: bm_word_t* allocate(idx_t size_in_words) const;
NONCOPYABLE(ArenaBitMap); void free(bm_word_t* map, idx_t size_in_words) const {
// ArenaBitMaps currently don't free memory.
}
};
// A BitMap with storage in the current threads resource area.
class ResourceBitMap : public GrowableBitMap<ResourceBitMap> {
public:
ResourceBitMap() : ResourceBitMap(0) {}
ResourceBitMap(idx_t size_in_bits, bool clear = true);
bm_word_t* allocate(idx_t size_in_words) const;
void free(bm_word_t* map, idx_t size_in_words) const {
// ArenaBitMaps currently don't free memory.
}
}; };
// A BitMap with storage in the CHeap. // A BitMap with storage in the CHeap.
class CHeapBitMap : public BitMap { class CHeapBitMap : public GrowableBitMap<CHeapBitMap> {
// NMT memory type
const MEMFLAGS _flags;
private:
// Don't allow copy or assignment, to prevent the // Don't allow copy or assignment, to prevent the
// allocated memory from leaking out to other instances. // allocated memory from leaking out to other instances.
NONCOPYABLE(CHeapBitMap); NONCOPYABLE(CHeapBitMap);
// NMT memory type
MEMFLAGS _flags;
public: public:
CHeapBitMap(MEMFLAGS flags = mtInternal) : BitMap(NULL, 0), _flags(flags) {} CHeapBitMap() : CHeapBitMap(mtInternal) {}
explicit CHeapBitMap(MEMFLAGS flags) : GrowableBitMap(0, false), _flags(flags) {}
// Clears the bitmap memory. // Clears the bitmap memory.
CHeapBitMap(idx_t size_in_bits, MEMFLAGS flags = mtInternal, bool clear = true); CHeapBitMap(idx_t size_in_bits, MEMFLAGS flags = mtInternal, bool clear = true);
~CHeapBitMap(); ~CHeapBitMap();
// Resize the backing bitmap memory. bm_word_t* allocate(idx_t size_in_words) const;
// void free(bm_word_t* map, idx_t size_in_words) const;
// Old bits are transferred to the new memory
// and the extended memory is (optionally) cleared.
void resize(idx_t new_size_in_bits, bool clear = true);
// Set up and (optionally) clear the bitmap memory.
//
// Precondition: The bitmap was default constructed and has
// not yet had memory allocated via resize or initialize.
void initialize(idx_t size_in_bits, bool clear = true);
// Set up and (optionally) clear the bitmap memory.
//
// Can be called on previously initialized bitmaps.
void reinitialize(idx_t size_in_bits, bool clear = true);
}; };
// Convenience class wrapping BitMap which provides multiple bits per slot. // Convenience class wrapping BitMap which provides multiple bits per slot.

View File

@ -23,6 +23,7 @@
#include "precompiled.hpp" #include "precompiled.hpp"
#include "logging/logStream.hpp" #include "logging/logStream.hpp"
#include "memory/arena.hpp"
#include "memory/resourceArea.hpp" #include "memory/resourceArea.hpp"
#include "utilities/bitMap.inline.hpp" #include "utilities/bitMap.inline.hpp"
#include "unittest.hpp" #include "unittest.hpp"
@ -83,25 +84,25 @@ class BitMapTest {
EXPECT_TRUE(map.is_same(map2)); EXPECT_TRUE(map.is_same(map2));
} }
template <class ReinitializableBitMapClass>
static void testReinitialize(BitMap::idx_t init_size) { static void testReinitialize(BitMap::idx_t init_size) {
ResourceMark rm; ResourceMark rm;
ResourceBitMap map(init_size); ReinitializableBitMapClass map(init_size);
map.reinitialize(BITMAP_SIZE); map.reinitialize(BITMAP_SIZE);
fillBitMap(map); fillBitMap(map);
ResourceBitMap map2(BITMAP_SIZE); ReinitializableBitMapClass map2(BITMAP_SIZE);
fillBitMap(map2); fillBitMap(map2);
EXPECT_TRUE(map.is_same(map2)) << "With init_size " << init_size; EXPECT_TRUE(map.is_same(map2)) << "With init_size " << init_size;
} }
#ifdef ASSERT #ifdef ASSERT
template <class PrintableBitMapClass>
static void testPrintOn(BitMap::idx_t size) { static void testPrintOn(BitMap::idx_t size) {
ResourceMark rm; ResourceMark rm;
ResourceBitMap map(size); PrintableBitMapClass map(size);
if (size > 0) { if (size > 0) {
map.set_bit(size / 2); map.set_bit(size / 2);
} }
@ -113,9 +114,19 @@ class BitMapTest {
#endif #endif
}; };
// TestArenaBitMap is the shorthand combination of Arena and ArenaBitMap.
// Multiple inheritance guarantees to construct Arena first.
class TestArenaBitMap : private Arena, public ArenaBitMap {
public:
TestArenaBitMap() : TestArenaBitMap(0) {}
TestArenaBitMap(idx_t size_in_bits, bool clear = true) : Arena(mtTest),
ArenaBitMap(static_cast<Arena*>(this), size_in_bits, clear) {}
};
class TestCHeapBitMap : public CHeapBitMap { class TestCHeapBitMap : public CHeapBitMap {
public: public:
TestCHeapBitMap(size_t size = 0) : CHeapBitMap(size, mtTest) {} TestCHeapBitMap(size_t size = 0) : CHeapBitMap(size, mtTest) {}
}; };
TEST_VM(BitMap, resize_grow) { TEST_VM(BitMap, resize_grow) {
@ -123,6 +134,8 @@ TEST_VM(BitMap, resize_grow) {
EXPECT_FALSE(HasFailure()) << "Failed on type ResourceBitMap"; EXPECT_FALSE(HasFailure()) << "Failed on type ResourceBitMap";
BitMapTest::testResizeGrow<TestCHeapBitMap>(); BitMapTest::testResizeGrow<TestCHeapBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type CHeapBitMap"; EXPECT_FALSE(HasFailure()) << "Failed on type CHeapBitMap";
BitMapTest::testResizeGrow<TestArenaBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type ArenaBitMap";
} }
TEST_VM(BitMap, resize_shrink) { TEST_VM(BitMap, resize_shrink) {
@ -130,6 +143,8 @@ TEST_VM(BitMap, resize_shrink) {
EXPECT_FALSE(HasFailure()) << "Failed on type ResourceBitMap"; EXPECT_FALSE(HasFailure()) << "Failed on type ResourceBitMap";
BitMapTest::testResizeShrink<TestCHeapBitMap>(); BitMapTest::testResizeShrink<TestCHeapBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type CHeapBitMap"; EXPECT_FALSE(HasFailure()) << "Failed on type CHeapBitMap";
BitMapTest::testResizeShrink<TestArenaBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type ArenaBitMap";
} }
TEST_VM(BitMap, resize_same) { TEST_VM(BitMap, resize_same) {
@ -137,6 +152,8 @@ TEST_VM(BitMap, resize_same) {
EXPECT_FALSE(HasFailure()) << "Failed on type ResourceBitMap"; EXPECT_FALSE(HasFailure()) << "Failed on type ResourceBitMap";
BitMapTest::testResizeSame<TestCHeapBitMap>(); BitMapTest::testResizeSame<TestCHeapBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type CHeapBitMap"; EXPECT_FALSE(HasFailure()) << "Failed on type CHeapBitMap";
BitMapTest::testResizeSame<TestArenaBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type ArenaBitMap";
} }
// Verify that when growing with clear, all added bits get cleared, // Verify that when growing with clear, all added bits get cleared,
@ -162,20 +179,28 @@ TEST_VM(BitMap, initialize) {
EXPECT_FALSE(HasFailure()) << "Failed on type ResourceBitMap"; EXPECT_FALSE(HasFailure()) << "Failed on type ResourceBitMap";
BitMapTest::testInitialize<TestCHeapBitMap>(); BitMapTest::testInitialize<TestCHeapBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type CHeapBitMap"; EXPECT_FALSE(HasFailure()) << "Failed on type CHeapBitMap";
BitMapTest::testInitialize<TestArenaBitMap>();
EXPECT_FALSE(HasFailure()) << "Failed on type ArenaBitMap";
} }
TEST_VM(BitMap, reinitialize) { TEST_VM(BitMap, reinitialize) {
BitMapTest::testReinitialize(0); constexpr BitMap::idx_t sizes[] = {0, BitMapTest::BITMAP_SIZE >> 3, BitMapTest::BITMAP_SIZE};
BitMapTest::testReinitialize(BitMapTest::BITMAP_SIZE >> 3);
BitMapTest::testReinitialize(BitMapTest::BITMAP_SIZE); for (auto size : sizes) {
BitMapTest::testReinitialize<ResourceBitMap>(size);
BitMapTest::testReinitialize<TestArenaBitMap>(size);
}
} }
#ifdef ASSERT #ifdef ASSERT
TEST_VM(BitMap, print_on) { TEST_VM(BitMap, print_on) {
BitMapTest::testPrintOn(0); constexpr BitMap::idx_t sizes[] = {0, BitMapTest::BITMAP_SIZE >> 3, BitMapTest::BITMAP_SIZE};
BitMapTest::testPrintOn(BitMapTest::BITMAP_SIZE >> 3);
BitMapTest::testPrintOn(BitMapTest::BITMAP_SIZE); for (auto size : sizes) {
BitMapTest::testPrintOn<ResourceBitMap>(size);
BitMapTest::testPrintOn<TestArenaBitMap>(size);
}
} }
#endif #endif