From 690aab37a864bfb53d90f623b048efacdbfb86b7 Mon Sep 17 00:00:00 2001 From: Yechi Ma <2662511702@qq.com> Date: Wed, 11 Dec 2024 18:10:34 +0800 Subject: [PATCH] slotpage --- CMakeLists.txt | 3 +- db/slotpage.h | 236 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 238 insertions(+), 1 deletion(-) create mode 100644 db/slotpage.h diff --git a/CMakeLists.txt b/CMakeLists.txt index cd5ac28..cf5eec7 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -117,7 +117,8 @@ endif(BUILD_SHARED_LIBS) # Must be included before CMAKE_INSTALL_INCLUDEDIR is used. include(GNUInstallDirs) -add_library(leveldb "") +add_library(leveldb "" + db/slotpage.h) target_sources(leveldb PRIVATE "${PROJECT_BINARY_DIR}/${LEVELDB_PORT_CONFIG_DIR}/port_config.h" diff --git a/db/slotpage.h b/db/slotpage.h new file mode 100644 index 0000000..2662a5a --- /dev/null +++ b/db/slotpage.h @@ -0,0 +1,236 @@ +// +// Created by 马也驰 on 2024/12/9. +// + +#ifndef LEVELDB_SLOTPAGE_H +#define LEVELDB_SLOTPAGE_H + +#include +#include +#include +#include +#include +#include +#include + +#define BITMAP_SIZE 8192 +#define BITS_PER_BYTE 8 + +#define POSMASK(off) (0x01 << (BITS_PER_BYTE-(off)-1)) +#define SETBIT(byte, off) (*(byte) |= POSMASK(off)) +#define RESETBIT(byte, off) (*(byte) &= (0xff ^ POSMASK(off))) +#define HASFREESLOT(byte) ((byte) != ~0x0) + +struct slot_content { + uint32_t vlog_num; + uint32_t value_offset; + slot_content(uint32_t vn, uint32_t vo) { + vlog_num = vn; + value_offset = vo; + } +}; + +class SlotCache { +// slot number -> slot content +#define BLOCK_NUM 16 +#define BLOCK_SIZE 4096 +#define SLOT_PER_BLOCK (BLOCK_SIZE/sizeof(slot_content)) +#define SLOT_OFFSET_IN_BLOCK(slot_num) ((slot_num)%SLOT_PER_BLOCK) +public: + SlotCache() { + for (auto i = 0; i < BLOCK_NUM; i++) { + block_cache[i] = static_cast(malloc(BLOCK_SIZE)); + access_time[i] = 0; + info[i] = block_info(); + } + } + + void get_slot(std::string &slotpage_fname, size_t slot_num, struct slot_content *sc) { + auto block_num = slotnum_hash2_blocknum(slot_num); + auto blockcache_num = block_num % BLOCK_NUM; + mtx.lock(); + if (info[blockcache_num].slotpage_fname != slotpage_fname) { // cache miss + write_back_block(blockcache_num); + read_in_block(blockcache_num, slotpage_fname, block_num); + } + read_slot(sc, blockcache_num, SLOT_OFFSET_IN_BLOCK(slot_num)); + access_time[blockcache_num]++; + mtx.unlock(); + } + + void set_slot(std::string &slotpage_fname, size_t slot_num, struct slot_content *sc) { + auto block_num = slotnum_hash2_blocknum(slot_num); + auto blockcache_num = block_num % BLOCK_NUM; + mtx.lock(); + if (info[blockcache_num].slotpage_fname != slotpage_fname) { + write_back_block(blockcache_num); + read_in_block(blockcache_num, slotpage_fname, block_num); + } + set_slot(sc, blockcache_num, SLOT_OFFSET_IN_BLOCK(slot_num)); + access_time[blockcache_num]++; + info[blockcache_num].is_dirty = true; + mtx.unlock(); + } + +private: + struct block_info { + std::string slotpage_fname; + size_t block_num; + bool is_dirty; + block_info() {} + block_info(std::string &slotpage_fname, size_t block_num, bool is_dirty=false) { + this->slotpage_fname = slotpage_fname; + this->block_num = block_num; + this->is_dirty = is_dirty; + } +}; + +private: + inline static size_t slotnum_hash2_blocknum(size_t slot_num) { + return slot_num * sizeof(slot_content) / BLOCK_SIZE; + } + void write_back_block(size_t blockcache_num) { + auto &fname = info[blockcache_num].slotpage_fname; + auto write_pos = info[blockcache_num].block_num * BLOCK_SIZE; + auto slotpage_handler = std::fstream(fname, std::ios::in | std::ios::out); + slotpage_handler.seekp(write_pos); + slotpage_handler.write( + reinterpret_cast(block_cache[blockcache_num]), BLOCK_SIZE); + } + void read_in_block(size_t blockcache_num, std::string &slotpage_fname, size_t block_num) { + auto slotpage_handler = std::fstream(slotpage_fname, std::ios::in | std::ios::out); + slotpage_handler.seekp(block_num*BLOCK_SIZE); + slotpage_handler.read(reinterpret_cast(block_cache[blockcache_num]), BLOCK_SIZE); + access_time[blockcache_num] = 0; + info[blockcache_num] = block_info(slotpage_fname, block_num); + } + inline void read_slot(struct slot_content *sc, size_t blockcache_num, size_t slot_num_offset) { + auto src = &block_cache[blockcache_num][slot_num_offset]; + memcpy(sc, src, sizeof(slot_content)); + } + inline void set_slot(struct slot_content *sc, size_t blockcache_num, size_t slot_num_offset) { + auto src = &block_cache[blockcache_num][slot_num_offset]; + memcpy(src, sc, sizeof(slot_content)); + } + +private: + std::mutex mtx; + struct slot_content *block_cache[BLOCK_NUM]; + size_t access_time[BLOCK_NUM]; + struct block_info info[BLOCK_NUM]; +}; + + +class BitMap { + // in memory bitmap +public: + BitMap() { + char *bitmap = static_cast(malloc(BITMAP_SIZE*sizeof(char))); + bitmaps_.push_back(bitmap); + size = BITMAP_SIZE; + first_empty_slot = 0; + } + + void dealloc_slot(size_t slot_num) { + const size_t byte = slot2byte(slot_num); + const size_t off = slot2offset(slot_num); + char *target_byte = get_bitmap_byte(byte); + assert(*target_byte & POSMASK(off)); + RESETBIT(target_byte, off); +// set_bitmap_byte(byte, target_byte); + first_empty_slot = first_empty_slot < slot_num ? first_empty_slot:slot_num; + } + + size_t alloc_slot() { + size_t target_slot = first_empty_slot; + char *start_byte = get_bitmap_byte(slot2byte(first_empty_slot)); + const size_t off = slot2offset(first_empty_slot); + SETBIT(start_byte, off); + if (HASFREESLOT(*start_byte)) { + auto bit_off = find_first_free_slot_inbyte(*start_byte); + first_empty_slot += bit_off - off; + } else { + size_t i; + for (i = slot2byte(first_empty_slot)+1; i < size; i++) { + char *byte = get_bitmap_byte(i); + if (HASFREESLOT(*byte)) { + auto bit_off = find_first_free_slot_inbyte(*byte); + first_empty_slot = byte2slot(i) + bit_off; + } + } + // scale the bitmap + if (i >= size) { + alloc_new_bitmap(); + char *byte = get_bitmap_byte(i); + SETBIT(byte, 0); + first_empty_slot = byte2slot(i) + 1; + } + } + return target_slot; + } + +private: + static inline size_t slot2byte(size_t slot_num) { return slot_num / BITS_PER_BYTE; } + static inline size_t byte2slot(uint8_t byte_num) { return byte_num * BITS_PER_BYTE; } + static inline size_t slot2offset(size_t slot_num) { return slot_num % BITS_PER_BYTE; } + inline char *get_bitmap_byte(size_t byte_num) { + char *bitmap = bitmaps_.at(byte_num / BITMAP_SIZE); + return &bitmap[byte_num % BITMAP_SIZE]; + } +// inline void set_bitmap_byte(size_t byte_num, char byte) { +// char *bitmap = bitmaps_.at(byte_num / BITMAP_SIZE); +// bitmap[byte_num % BITMAP_SIZE] = byte; +// } + static inline size_t find_first_free_slot_inbyte(uint8_t byte) { + uint32_t rbyte = __builtin_bswap32((uint32_t)byte) >> 24; // 使用 32 位变量 + return BITS_PER_BYTE - __builtin_ctz(~rbyte) - 1; // 使用 GCC 内建函数找到最低位 1 的位置 + } + void alloc_new_bitmap() { + char *bitmap = static_cast(malloc(BITMAP_SIZE*sizeof(char))); + bitmaps_.push_back(bitmap); + size += BITMAP_SIZE; + } + +private: + std::vector bitmaps_; + size_t size; + size_t first_empty_slot; +}; + + +// read and write slot in disk +// first version with no cache +class SlotPage { +public: + SlotPage(std::string &dbname) { + slotpage_fname = slotpage_handler_name(dbname); + bitmap = new BitMap(); + } + +public: + void get_slot(size_t slot_num, struct slot_content *sc) { + slotcache->get_slot(slotpage_fname, slot_num, sc); + } + void set_slot(size_t slot_num, struct slot_content *sc) { + slotcache->set_slot(slotpage_fname, slot_num, sc); + } + size_t alloc_slot() { return bitmap->alloc_slot(); } + void dealloc_slot(size_t slot_num) { bitmap->dealloc_slot(slot_num); } + +private: + static std::string slotpage_handler_name(std::string &dbname) { + return "./" + dbname + "_slotpage"; + } + inline static size_t slotpage_pos(size_t slot_num) { + return slot_num * sizeof(slot_content); + } + +private: + std::string slotpage_fname; + BitMap *bitmap; + static SlotCache *slotcache; +}; + +SlotCache *SlotPage::slotcache = new SlotCache(); + +#endif // LEVELDB_SLOTPAGE_H