#include "include/ceph_assert.h"
#include "bluestore_types.h"
#include "common/debug.h"
+#include <atomic>
#define dout_context cct
#define dout_subsys ceph_subsys_bluestore
ext.length = (soffset - ext.offset);
return ext;
}
+
+//----------------------------------------------------------------------------
+uint64_t SimpleBitmap::set_atomic(uint64_t offset, uint64_t length)
+{
+ if (offset >= m_num_bits) {
+ return 0;
+ }
+ if (offset + length >= m_num_bits) {
+ length = m_num_bits - offset;
+ }
+ if (length == 0) {
+ return 0;
+ }
+
+ auto apply_mask = [&](uint64_t word, uint64_t mask) -> uint64_t {
+ // Bits are completely unrelated to each other
+ // Plus, we do not care who sets the which bits if 2 chains of sets overlap.
+ // So, going for relaxed.
+ uint64_t old_val = std::atomic_ref<uint64_t>(m_arr[word]).fetch_or(mask, std::memory_order::relaxed);
+ // We set b if mask[b] = 1 and old_val[b] = 0.
+ uint64_t we_set_those = ~old_val & mask;
+ return std::popcount(we_set_those);
+ };
+ auto [word_start, bit_start] = split(offset);
+ auto [word_end, bit_end] = split(offset + length - 1);
+
+ uint64_t mask;
+ if (word_start == word_end) {
+ mask = (FULL_MASK << bit_start) &
+ (FULL_MASK >> (MAX_BIT_NO - bit_end));
+ return apply_mask(word_start, mask);
+ }
+ uint64_t bit_set_count = 0;
+ uint64_t word = word_start;
+ mask = FULL_MASK << bit_start;
+ bit_set_count += apply_mask(word, mask);
+ word++;
+ while (word < word_end) {
+ mask = FULL_MASK;
+ bit_set_count += apply_mask(word, mask);
+ word++;
+ }
+ mask = FULL_MASK >> (MAX_BIT_NO - bit_end);
+ bit_set_count += apply_mask(word, mask);
+ return bit_set_count;
+}
+
+//----------------------------------------------------------------------------
+uint64_t SimpleBitmap::clr_atomic(uint64_t offset, uint64_t length)
+{
+ if (offset >= m_num_bits) {
+ return 0;
+ }
+ if (offset + length >= m_num_bits) {
+ length = m_num_bits - offset;
+ }
+ if (length == 0) {
+ return 0;
+ }
+
+ auto apply_mask = [&](uint64_t word, uint64_t mask) -> uint64_t {
+ // Bits are completely unrelated to each other
+ // Plus, we do not care who clears the which bits if 2 chains of sets overlap.
+ // So, going for relaxed.
+ uint64_t old_val = std::atomic_ref<uint64_t>(m_arr[word]).fetch_and(~mask, std::memory_order::relaxed);
+ // We cleared b if mask[b] = 1 and old_val[b] = 1.
+ uint64_t we_cleared_those = old_val & mask;
+ return std::popcount(we_cleared_those);
+ };
+ auto [word_start, bit_start] = split(offset);
+ auto [word_end, bit_end] = split(offset + length - 1);
+
+ uint64_t mask;
+ if (word_start == word_end) {
+ mask = (FULL_MASK << bit_start) &
+ (FULL_MASK >> (MAX_BIT_NO - bit_end));
+ return apply_mask(word_start, mask);
+ }
+ uint64_t bit_set_count = 0;
+ uint64_t word = word_start;
+ mask = FULL_MASK << bit_start;
+ bit_set_count += apply_mask(word, mask);
+ word++;
+ while (word < word_end) {
+ mask = FULL_MASK;
+ bit_set_count += apply_mask(word, mask);
+ word++;
+ }
+ mask = FULL_MASK >> (MAX_BIT_NO - bit_end);
+ bit_set_count += apply_mask(word, mask);
+ return bit_set_count;
+}
// returns a copy of the next clear extent starting at @offset
extent_t get_next_clr_extent(uint64_t offset);
+ // Sets a bit range (@length~@offset).
+ // This variant is safe for multithread operations.
+ // Can be intermixed with clr_atomic, but not with set or clr.
+ // Returns: count of bits set
+ uint64_t set_atomic(uint64_t offset, uint64_t length);
+
+ // Clears a bit range (@length~@offset).
+ // This variant is safe for multithread operations.
+ // Can be intermixed with set_atomic, but not with set or clr.
+ // Returns: count of bits set
+ uint64_t clr_atomic(uint64_t offset, uint64_t length);
+
//----------------------------------------------------------------------------
inline uint64_t get_size() {
return m_num_bits;
constexpr static uint64_t BITS_IN_WORD = (BYTES_IN_WORD * 8);
constexpr static uint64_t BITS_IN_WORD_MASK = (BITS_IN_WORD - 1);
constexpr static uint64_t BITS_IN_WORD_SHIFT = 6;
+ constexpr static uint64_t MAX_BIT_NO = BITS_IN_WORD - 1;
constexpr static uint64_t FULL_MASK = (~((uint64_t)0));
CephContext *cct;
#include "perfglue/heap_profiler.h"
#include "os/bluestore/Writer.h"
#include "common/pretty_binary.h"
-
#include <bitset>
#include <sstream>
+#include <boost/random/mersenne_twister.hpp>
+#include <boost/random/uniform_int.hpp>
+
+typedef boost::mt11213b generator_type;
#define _STR(x) #x
#define STRINGIFY(x) _STR(x)
}
}
+TEST(SimpleBitmap, multithread) {
+ generator_type rng(1234567);
+ // 2^10 = 1K * 4KB = 4MB
+ // 2^20 = 1M * 4KB = 4GB
+ // 2^30 = 1G * 4KB = 4TB
+ for (uint64_t scale = 10; scale < 30; scale++) {
+ uint64_t bit_count = boost::uniform_int<uint64_t>(1 << scale, 2 << scale)(rng);
+ std::cout << "bit_count=" << bit_count << std::endl;
+ SimpleBitmap sbmap(g_ceph_context, bit_count);
+
+ auto randomer = [&](int thread_nr, int64_t* overall_set) {
+ generator_type local_rng(thread_nr);
+ boost::uniform_int<> size_gen(1, 100);
+ boost::uniform_int<> set_clear_gen(0,1);
+ int64_t overall_set_counter = 0;
+ for (int i = 0; i < 100000; i++) {
+ uint64_t size = size_gen(local_rng);
+ uint64_t location = boost::uniform_int<uint64_t>(0, bit_count - size)(local_rng);
+ int d = set_clear_gen(local_rng);
+ if (d) {
+ overall_set_counter += sbmap.set_atomic(location, size);
+ } else {
+ overall_set_counter -= sbmap.clr_atomic(location, size);
+ }
+ }
+ *overall_set = overall_set_counter;
+ };
+ static constexpr uint8_t thread_count = 8;
+ thread thr[thread_count];
+ int64_t overall_set[thread_count] = {0};
+ for (int t = 0; t < thread_count; t++) {
+ thr[t] = thread(randomer, t, &overall_set[t]);
+ }
+ int64_t bits_set_in_threads = 0;
+ for (int t = 0; t < thread_count; t++) {
+ thr[t].join();
+ bits_set_in_threads += overall_set[t];
+ }
+
+ uint64_t bits_cleared = sbmap.clr_atomic(0, bit_count);
+ EXPECT_EQ(bits_cleared, bits_set_in_threads);
+ }
+}
+
TEST(shared_blob_2hash_tracker_t, basic_test) {
shared_blob_2hash_tracker_t t1(1024 * 1024, 4096);