diff --git a/cc_format/Makefile b/cc_format/Makefile deleted file mode 100644 index 17026cbb..00000000 --- a/cc_format/Makefile +++ /dev/null @@ -1,61 +0,0 @@ -PROG1 = sv_format.exe -SV_FORMAT_SRCS1 := sv_format.cc transaction.cc util.cc result.cc - -REL := ../common/ -include $(REL)Makefile -SV_FORMAT_ALLSRC = $(SV_FORMAT_SRCS1) $(SRCS2) $(wildcard include/*.hh) - -# start of initialization of some parameters. -ADD_ANALYSIS=1 -BACK_OFF=0 -KEY_SIZE=8 -MASSTREE_USE=1 -NO_WAIT_LOCKING_IN_VALIDATION=0 -NO_WAIT_OF_TICTOC=1 -PARTITION_TABLE=0 -PROCEDURE_SORT=0 -SLEEP_READ_PHASE=0 # of tics -VAL_SIZE=4 -WAL=0 -# end of initialization - -CC = g++ -CFLAGS = -c -pipe -g -O3 -std=c++17 -march=native \ - -Wall -Wextra -Wdangling-else -Wchkp -Winvalid-memory-model \ - -D$(shell uname) \ - -D$(shell hostname) \ - -DKEY_SIZE=$(KEY_SIZE) \ - -DVAL_SIZE=$(VAL_SIZE) \ - -DADD_ANALYSIS=$(ADD_ANALYSIS) \ - -DBACK_OFF=$(BACK_OFF) \ - -DMASSTREE_USE=$(MASSTREE_USE) \ - -DNO_WAIT_LOCKING_IN_VALIDATION=$(NO_WAIT_LOCKING_IN_VALIDATION) \ - -DNO_WAIT_OF_TICTOC=$(NO_WAIT_OF_TICTOC) \ - -DPARTITION_TABLE=$(PARTITION_TABLE) \ - -DPROCEDURE_SORT=$(PROCEDURE_SORT) \ - -DSLEEP_READ_PHASE=$(SLEEP_READ_PHASE) \ - -DWAL=$(WAL) \ - -INCLUDE = -I/usr/include \ - -I../third_party/ \ - -LDLIBS = -lpthread -lboost_filesystem -lboost_system -lgflags -lglog - -OBJS1 = $(SV_FORMAT_SRCS1:.cc=.o) - -all: $(PROG1) - -include ../include/MakefileForMasstreeUse -$(PROG1) : $(OBJS1) $(MASSOBJ) - $(CC) -o $@ $^ $(LDFLAGS) $(LDLIBS) $(INCLUDE) - -.cc.o: - $(CC) $(CFLAGS) -c $< -o $@ - -format: - clang-format -i -verbose -style=Google $(SV_FORMAT_ALLSRC) - -clean: - rm -f *~ *.o *.exe *.stackdump - rm -f ../common/*~ ../common/*.o ../common/*.exe ../common/*.stackdump - rm -rf .deps diff --git a/cc_format/README.md b/cc_format/README.md deleted file mode 100644 index 504f5f1a..00000000 --- a/cc_format/README.md +++ /dev/null @@ -1,39 +0,0 @@ -# Single version concurrency control format -- This is the format for adding single-version concurrency control. - -## Where to edit -### Source files -- result.cc - - L12-14 : Declare the variables with appropriate names. -- sv_format.cc
-Set an appropriate file name. - - L48-66 : Edit it appropriately when you enable log persistence. - - L106-133 : Define a single transaction workflow. -- util.cc
- - L105-117 : Set initial value of record member appropriately. - - leaderWork function : Define the job of a leader thread. -- Makefile
- - Edit the file name "sv_format / SV_FORMAT" appropriately. - - L9-37 : Define your preprocessor definition properly to determine the configuration. - - Please remove the libraries and options that cannot be used in the experimental environment. -- transaction.cc
- - Define the tbegin/validationPhase/abort/writePhase/read/write function properly. - Delete unnecessary functions and add necessary functions to the transaction workflow. -### Header files -- include/common.hh
- - Define the global variables and workload configuration variables that are necessary for Concurrency Control. -- include/log.hh
- - Define the members of LogRecord class as appropriate. Please modify accordingly. -- include/result.hh
- - Change the name of the variable appropriately. -- include/silo_op_element.hh
- - Change the file name appropriately. - - Change the members of ReadElement/WriteElement appropriately. And make corrections accordingly. -- include/transaction.hh
- - Modify the members of TransactionStatus class appropriately as necessary. - - TxnExecutor class is information that should be held by the worker thread. Please add any information necessary for concurrency control to the class members. -- include/tuple.hh
- - Declare the metadata to be stored in the record header in the Tuple class. - - attention : To improve the performance, no keys are stored. When a one-dimensional array is used as a DB table, the index position is the key. If you use masstree, the key-value is stored in the leaf node of masstree and the value is a pointer to the record, so there is no need to store the key. -- include/util.hh
- - Edit it appropriately in conjunction with util.cc. diff --git a/cc_format/include/atomic_tool.hh b/cc_format/include/atomic_tool.hh deleted file mode 100644 index 36a62ad2..00000000 --- a/cc_format/include/atomic_tool.hh +++ /dev/null @@ -1,29 +0,0 @@ -#pragma once - -#include "common.hh" - -#include "../../include/inline.hh" - -INLINE uint64_t atomicLoadGE(); - -INLINE void atomicAddGE() { - uint64_t expected, desired; - - expected = atomicLoadGE(); - for (;;) { - desired = expected + 1; - if (__atomic_compare_exchange_n(&(GlobalEpoch.obj_), &expected, desired, - false, __ATOMIC_ACQ_REL, __ATOMIC_ACQUIRE)) - break; - } -} - -INLINE uint64_t atomicLoadGE() { - uint64_t_64byte result = - __atomic_load_n(&(GlobalEpoch.obj_), __ATOMIC_ACQUIRE); - return result.obj_; -} - -INLINE void atomicStoreThLocalEpoch(unsigned int thid, uint64_t newval) { - __atomic_store_n(&(ThLocalEpoch[thid].obj_), newval, __ATOMIC_RELEASE); -} diff --git a/cc_format/include/common.hh b/cc_format/include/common.hh deleted file mode 100644 index bb95fa6f..00000000 --- a/cc_format/include/common.hh +++ /dev/null @@ -1,61 +0,0 @@ -#pragma once -#include -#include -#include -#include - -#include "tuple.hh" - -#include "../../include/cache_line_size.hh" -#include "../../include/int64byte.hh" -#include "../../include/masstree_wrapper.hh" - -#include "gflags/gflags.h" -#include "glog/logging.h" - -#ifdef GLOBAL_VALUE_DEFINE -#define GLOBAL -alignas(CACHE_LINE_SIZE) GLOBAL uint64_t_64byte GlobalEpoch(1); -#if MASSTREE_USE -alignas(CACHE_LINE_SIZE) GLOBAL MasstreeWrapper MT; -#endif -#else -#define GLOBAL extern -alignas(CACHE_LINE_SIZE) GLOBAL uint64_t_64byte GlobalEpoch; -#if MASSTREE_USE -alignas(CACHE_LINE_SIZE) GLOBAL MasstreeWrapper MT; -#endif -#endif - -#ifdef GLOBAL_VALUE_DEFINE -DEFINE_uint64(clocks_per_us, 2100, - "CPU_MHz. Use this info for measuring time."); -DEFINE_uint64(epoch_time, 40, "Epoch interval[msec]."); -DEFINE_uint64(extime, 3, "Execution time[sec]."); -DEFINE_uint64(max_ope, 10, - "Total number of operations per single transaction."); -DEFINE_bool(rmw, false, - "True means read modify write, false means blind write."); -DEFINE_uint64(rratio, 50, "read ratio of single transaction."); -DEFINE_uint64(thread_num, 10, "Total number of worker threads."); -DEFINE_uint64(tuple_num, 1000000, "Total number of records."); -DEFINE_bool(ycsb, true, - "True uses zipf_skew, false uses faster random generator."); -DEFINE_double(zipf_skew, 0, "zipf skew. 0 ~ 0.999..."); -#else -DECLARE_uint64(clocks_per_us); -DECLARE_uint64(epoch_time); -DECLARE_uint64(extime); -DECLARE_uint64(max_ope); -DECLARE_bool(rmw); -DECLARE_uint64(rratio); -DECLARE_uint64(thread_num); -DECLARE_uint64(tuple_num); -DECLARE_bool(ycsb); -DECLARE_double(zipf_skew); -#endif - -alignas(CACHE_LINE_SIZE) GLOBAL uint64_t_64byte *ThLocalEpoch; -alignas(CACHE_LINE_SIZE) GLOBAL uint64_t_64byte *CTIDW; - -alignas(CACHE_LINE_SIZE) GLOBAL Tuple *Table; diff --git a/cc_format/include/log.hh b/cc_format/include/log.hh deleted file mode 100644 index d38e2e42..00000000 --- a/cc_format/include/log.hh +++ /dev/null @@ -1,54 +0,0 @@ -#pragma once - -#include - -#include -#include - -class LogHeader { - public: - int chkSum_ = 0; - unsigned int logRecNum_ = 0; - const std::size_t len_val_ = VAL_SIZE; - - void init() { - chkSum_ = 0; - logRecNum_ = 0; - } - - void convertChkSumIntoComplementOnTwo() { - chkSum_ ^= 0xffffffff; - ++chkSum_; - } -}; - -class LogRecord { - public: - uint64_t tid_; - unsigned int key_; - char val_[VAL_SIZE]; - - LogRecord() : tid_(0), key_(0) {} - - LogRecord(uint64_t tid, unsigned int key, char *val) : tid_(tid), key_(key) { - memcpy(this->val_, val, VAL_SIZE); - } - - int computeChkSum() { - // compute checksum - int chkSum = 0; - int *itr = (int *)this; - for (unsigned int i = 0; i < sizeof(LogRecord) / sizeof(int); ++i) { - chkSum += (*itr); - ++itr; - } - - return chkSum; - } -}; - -class LogPackage { - public: - LogHeader header_; - std::unique_ptr log_records_; -}; diff --git a/cc_format/include/result.hh b/cc_format/include/result.hh deleted file mode 100644 index 3c5a708a..00000000 --- a/cc_format/include/result.hh +++ /dev/null @@ -1,9 +0,0 @@ -#pragma once - -#include - -#include "../../include/result.hh" - -extern std::vector SiloResult; - -extern void initResult(); diff --git a/cc_format/include/silo_op_element.hh b/cc_format/include/silo_op_element.hh deleted file mode 100644 index b8ae6836..00000000 --- a/cc_format/include/silo_op_element.hh +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include "../../include/op_element.hh" - -template -class ReadElement : public OpElement { - public: - using OpElement::OpElement; - - Tidword tidword_; - char val_[VAL_SIZE]; - - ReadElement(uint64_t key, T* rcdptr, char* val, Tidword tidword) - : OpElement::OpElement(key, rcdptr) { - tidword_.obj_ = tidword.obj_; - memcpy(this->val_, val, VAL_SIZE); - } - - bool operator<(const ReadElement& right) const { - return this->key_ < right.key_; - } -}; - -template -class WriteElement : public OpElement { - public: - using OpElement::OpElement; - - WriteElement(uint64_t key, T* rcdptr) - : OpElement::OpElement(key, rcdptr) {} - - bool operator<(const WriteElement& right) const { - return this->key_ < right.key_; - } -}; diff --git a/cc_format/include/transaction.hh b/cc_format/include/transaction.hh deleted file mode 100644 index b66d3cc1..00000000 --- a/cc_format/include/transaction.hh +++ /dev/null @@ -1,102 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "../../include/fileio.hh" -#include "../../include/procedure.hh" -#include "../../include/result.hh" -#include "../../include/string.hh" -#include "common.hh" -#include "log.hh" -#include "silo_op_element.hh" -#include "tuple.hh" - -#define LOGSET_SIZE 1000 - -using namespace std; - -enum class TransactionStatus : uint8_t { - kInFlight, - kCommitted, - kAborted, -}; - -class TxnExecutor { - public: - vector> read_set_; - vector> write_set_; - vector pro_set_; - - vector log_set_; - LogHeader latest_log_header_; - - TransactionStatus status_; - unsigned int thid_; - /* lock_num_ ... - * the number of locks in local write set. - */ - Result* sres_; - - File logfile_; - - char write_val_[VAL_SIZE]; - char return_val_[VAL_SIZE]; - - TxnExecutor(int thid, Result* sres); - - /** - * @brief function about abort. - * Clean-up local read/write set. - * Release locks. - * @return void - */ - void abort(); - - void begin(); - - void displayWriteSet(); - - Tuple* get_tuple(Tuple* table, uint64_t key) { return &table[key]; } - - /** - * @brief Transaction read function. - * @param [in] key The key of key-value - */ - void read(uint64_t key); - - /** - * @brief Search xxx set - * @detail Search element of local set corresponding to given key. - * In this prototype system, the value to be updated for each worker thread - * is fixed for high performance, so it is only necessary to check the key - * match. - * @param Key [in] the key of key-value - * @return Corresponding element of local set - */ - ReadElement* searchReadSet(uint64_t key); - - /** - * @brief Search xxx set - * @detail Search element of local set corresponding to given key. - * In this prototype system, the value to be updated for each worker thread - * is fixed for high performance, so it is only necessary to check the key - * match. - * @param Key [in] the key of key-value - * @return Corresponding element of local set - */ - WriteElement* searchWriteSet(uint64_t key); - - bool validationPhase(); - - void wal(uint64_t ctid); - - /** - * @brief Transaction write function. - * @param [in] key The key of key-value - */ - void write(uint64_t key); - - void writePhase(); -}; diff --git a/cc_format/include/tuple.hh b/cc_format/include/tuple.hh deleted file mode 100644 index b3f37222..00000000 --- a/cc_format/include/tuple.hh +++ /dev/null @@ -1,14 +0,0 @@ -#pragma once - -#include -#include - -#include -#include - -#include "../../include/cache_line_size.hh" - -class Tuple { - public: - char val_[VAL_SIZE]; -}; diff --git a/cc_format/include/util.hh b/cc_format/include/util.hh deleted file mode 100644 index c077bf49..00000000 --- a/cc_format/include/util.hh +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -extern void chkArg(); - -extern bool chkEpochLoaded(); - -extern void displayDB(); - -extern void displayParameter(); - -extern void genLogFile(std::string &logpath, const int thid); - -extern void leaderWork(uint64_t &epoch_timer_start, uint64_t &epoch_timer_stop); - -extern void makeDB(); - -extern void partTableInit([[maybe_unused]] size_t thid, uint64_t start, - uint64_t end); - -extern void ShowOptParameters(); diff --git a/cc_format/result.cc b/cc_format/result.cc deleted file mode 100644 index 58b36677..00000000 --- a/cc_format/result.cc +++ /dev/null @@ -1,14 +0,0 @@ -#include "include/result.hh" -#include "include/common.hh" - -#include "../include/cache_line_size.hh" -#include "../include/result.hh" - -using namespace std; - -/** - * Please declare it with an appropriate name. - */ -alignas(CACHE_LINE_SIZE) std::vector SiloResult; - -void initResult() { SiloResult.resize(FLAGS_thread_num); } diff --git a/cc_format/sv_format.cc b/cc_format/sv_format.cc deleted file mode 100644 index 6ada0d16..00000000 --- a/cc_format/sv_format.cc +++ /dev/null @@ -1,171 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "boost/filesystem.hpp" - -#define GLOBAL_VALUE_DEFINE -#include "include/atomic_tool.hh" -#include "include/common.hh" -#include "include/result.hh" -#include "include/transaction.hh" -#include "include/util.hh" - -#include "../include/atomic_wrapper.hh" -#include "../include/backoff.hh" -#include "../include/cpu.hh" -#include "../include/debug.hh" -#include "../include/fileio.hh" -#include "../include/masstree_wrapper.hh" -#include "../include/random.hh" -#include "../include/result.hh" -#include "../include/tsc.hh" -#include "../include/util.hh" -#include "../include/zipf.hh" - -using namespace std; - -void worker(size_t thid, char& ready, const bool& start, const bool& quit) { - Result& myres = std::ref(SiloResult[thid]); - Xoroshiro128Plus rnd; - rnd.init(); - TxnExecutor trans(thid, (Result*)&myres); - FastZipf zipf(&rnd, FLAGS_zipf_skew, FLAGS_tuple_num); - uint64_t epoch_timer_start, epoch_timer_stop; -#if BACK_OFF - Backoff backoff(FLAGS_clocks_per_us); -#endif - -#if WAL -/** - * Edit it appropriately when you enable log persistence. - */ -#if 0 - const boost::filesystem::path log_dir_path("/tmp/ccbench"); - if (boost::filesystem::exists(log_dir_path)) { - } else { - boost::system::error_code error; - const bool result = boost::filesystem::create_directory(log_dir_path, - error); if (!result || error) { ERR; - } - } - std::string logpath("/tmp/ccbench"); - */ - std::string logpath; - genLogFile(logpath, thid); - trans.logfile_.open(logpath, O_CREAT | O_TRUNC | O_WRONLY, 0644); - trans.logfile_.ftruncate(10 ^ 9); -#endif -#endif - -#ifdef Linux - setThreadAffinity(thid); -#endif - -#if MASSTREE_USE - MasstreeWrapper::thread_init(int(thid)); -#endif - - storeRelease(ready, 1); - while (!loadAcquire(start)) _mm_pause(); - if (thid == 0) epoch_timer_start = rdtscp(); - while (!loadAcquire(quit)) { -#if PARTITION_TABLE - makeProcedure(trans.pro_set_, rnd, zipf, FLAGS_tuple_num, FLAGS_max_ope, - FLAGS_thread_num, FLAGS_rratio, FLAGS_rmw, FLAGS_ycsb, true, - thid, myres); -#else - makeProcedure(trans.pro_set_, rnd, zipf, FLAGS_tuple_num, FLAGS_max_ope, - FLAGS_thread_num, FLAGS_rratio, FLAGS_rmw, FLAGS_ycsb, false, - thid, myres); -#endif - -#if PROCEDURE_SORT - sort(trans.pro_set_.begin(), trans.pro_set_.end()); -#endif - - RETRY: - if (thid == 0) { - leaderWork(epoch_timer_start, epoch_timer_stop); -#if BACK_OFF - leaderBackoffWork(backoff, Result); -#endif - // printf("Thread #%d: on CPU %d\n", thid, sched_getcpu()); - } - - if (loadAcquire(quit)) break; - - trans.begin(); - for (auto itr = trans.pro_set_.begin(); itr != trans.pro_set_.end(); - ++itr) { - if ((*itr).ope_ == Ope::READ) { - trans.read((*itr).key_); - } else if ((*itr).ope_ == Ope::WRITE) { - trans.write((*itr).key_); - } else if ((*itr).ope_ == Ope::READ_MODIFY_WRITE) { - trans.read((*itr).key_); - trans.write((*itr).key_); - } else { - ERR; - } - } - - if (trans.validationPhase()) { - trans.writePhase(); - /** - * local_commit_counts is used at ../include/backoff.hh to calcurate about - * backoff. - */ - storeRelease(myres.local_commit_counts_, - loadAcquire(myres.local_commit_counts_) + 1); - } else { - trans.abort(); - ++myres.local_abort_counts_; - goto RETRY; - } - } - - return; -} - -int main(int argc, char* argv[]) try { - gflags::SetUsageMessage("Silo benchmark."); - gflags::ParseCommandLineFlags(&argc, &argv, true); - chkArg(); - makeDB(); - - alignas(CACHE_LINE_SIZE) bool start = false; - alignas(CACHE_LINE_SIZE) bool quit = false; - initResult(); - std::vector readys(FLAGS_thread_num); - std::vector thv; - for (size_t i = 0; i < FLAGS_thread_num; ++i) - thv.emplace_back(worker, i, std::ref(readys[i]), std::ref(start), - std::ref(quit)); - waitForReady(readys); - storeRelease(start, true); - for (size_t i = 0; i < FLAGS_extime; ++i) { - sleepMs(1000); - } - storeRelease(quit, true); - for (auto& th : thv) th.join(); - - for (unsigned int i = 0; i < FLAGS_thread_num; ++i) { - SiloResult[0].addLocalAllResult(SiloResult[i]); - } - ShowOptParameters(); - SiloResult[0].displayAllResult(FLAGS_clocks_per_us, FLAGS_extime, - FLAGS_thread_num); - - return 0; -} catch (bad_alloc) { - ERR; -} diff --git a/cc_format/transaction.cc b/cc_format/transaction.cc deleted file mode 100644 index dc54533e..00000000 --- a/cc_format/transaction.cc +++ /dev/null @@ -1,96 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -#include "include/atomic_tool.hh" -#include "include/common.hh" -#include "include/log.hh" -#include "include/transaction.hh" - -#include "../include/backoff.hh" -#include "../include/debug.hh" -#include "../include/fileio.hh" -#include "../include/masstree_wrapper.hh" -#include "../include/tsc.hh" -#include "../include/util.hh" - -extern void displayDB(); - -using namespace std; - -TxnExecutor::TxnExecutor(int thid, Result *sres) : thid_(thid), sres_(sres) { - read_set_.reserve(FLAGS_max_ope); - write_set_.reserve(FLAGS_max_ope); - pro_set_.reserve(FLAGS_max_ope); - - genStringRepeatedNumber(write_val_, VAL_SIZE, thid); -} - -void TxnExecutor::begin() { -} - -WriteElement *TxnExecutor::searchWriteSet(uint64_t key) { - for (auto itr = write_set_.begin(); itr != write_set_.end(); ++itr) { - if ((*itr).key_ == key) return &(*itr); - } - - return nullptr; -} - -ReadElement *TxnExecutor::searchReadSet(uint64_t key) { - for (auto itr = read_set_.begin(); itr != read_set_.end(); ++itr) { - if ((*itr).key_ == key) return &(*itr); - } - - return nullptr; -} - -void TxnExecutor::read(uint64_t key) { -} - -void TxnExecutor::write(uint64_t key) { -} - -bool TxnExecutor::validationPhase() { -} - -void TxnExecutor::abort() { -} - -void TxnExecutor::wal(uint64_t ctid) { - for (auto itr = write_set_.begin(); itr != write_set_.end(); ++itr) { - LogRecord log(ctid, (*itr).key_, write_val_); - log_set_.emplace_back(log); - latest_log_header_.chkSum_ += log.computeChkSum(); - ++latest_log_header_.logRecNum_; - } - - if (log_set_.size() > LOGSET_SIZE / 2) { - // prepare write header - latest_log_header_.convertChkSumIntoComplementOnTwo(); - - // write header - logfile_.write((void *)&latest_log_header_, sizeof(LogHeader)); - - // write log record - // for (auto itr = log_set_.begin(); itr != log_set_.end(); ++itr) - // logfile_.write((void *)&(*itr), sizeof(LogRecord)); - logfile_.write((void *)&(log_set_[0]), - sizeof(LogRecord) * latest_log_header_.logRecNum_); - - // logfile_.fdatasync(); - - // clear for next transactions. - latest_log_header_.init(); - log_set_.clear(); - } -} - -void TxnExecutor::writePhase() { -} - diff --git a/cc_format/util.cc b/cc_format/util.cc deleted file mode 100644 index 69cd3f8d..00000000 --- a/cc_format/util.cc +++ /dev/null @@ -1,157 +0,0 @@ -#include -#include // syscall(SYS_gettid), -#include -#include // syscall(SYS_gettid), -#include // syscall(SYS_gettid), - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "include/atomic_tool.hh" -#include "include/common.hh" -#include "include/transaction.hh" -#include "include/tuple.hh" -#include "include/util.hh" - -#include "../include/cache_line_size.hh" -#include "../include/config.hh" -#include "../include/debug.hh" -#include "../include/masstree_wrapper.hh" -#include "../include/procedure.hh" -#include "../include/random.hh" -#include "../include/tsc.hh" -#include "../include/util.hh" -#include "../include/zipf.hh" - -void chkArg() { - displayParameter(); - - if (FLAGS_rratio > 100) { - ERR; - } - - if (FLAGS_zipf_skew >= 1) { - cout << "FLAGS_zipf_skew must be 0 ~ 0.999..." << endl; - ERR; - } - - if (posix_memalign((void **)&ThLocalEpoch, CACHE_LINE_SIZE, - FLAGS_thread_num * sizeof(uint64_t_64byte)) != 0) - ERR; - if (posix_memalign((void **)&CTIDW, CACHE_LINE_SIZE, - FLAGS_thread_num * sizeof(uint64_t_64byte)) != 0) - ERR; - - // init - for (unsigned int i = 0; i < FLAGS_thread_num; ++i) { - ThLocalEpoch[i].obj_ = 0; - CTIDW[i].obj_ = 0; - } -} - -bool chkEpochLoaded() { - uint64_t nowepo = atomicLoadGE(); - //全てのワーカースレッドが最新エポックを読み込んだか確認する. - for (unsigned int i = 1; i < FLAGS_thread_num; ++i) { - if (__atomic_load_n(&(ThLocalEpoch[i].obj_), __ATOMIC_ACQUIRE) != nowepo) - return false; - } - - return true; -} - -void displayDB() { - Tuple *tuple; - for (unsigned int i = 0; i < FLAGS_tuple_num; ++i) { - tuple = &Table[i]; - cout << "------------------------------" << endl; //-は30個 - cout << "key: " << i << endl; - cout << "val: " << tuple->val_ << endl; - cout << "TIDword: " << tuple->tidword_.obj_ << endl; - cout << "bit: " << tuple->tidword_.obj_ << endl; - cout << endl; - } -} - -void displayParameter() { - cout << "#FLAGS_clocks_per_us:\t" << FLAGS_clocks_per_us << endl; - cout << "#FLAGS_epoch_time:\t" << FLAGS_epoch_time << endl; - cout << "#FLAGS_extime:\t\t" << FLAGS_extime << endl; - cout << "#FLAGS_max_ope:\t\t" << FLAGS_max_ope << endl; - cout << "#FLAGS_rmw:\t\t" << FLAGS_rmw << endl; - cout << "#FLAGS_rratio:\t\t" << FLAGS_rratio << endl; - cout << "#FLAGS_thread_num:\t" << FLAGS_thread_num << endl; - cout << "#FLAGS_tuple_num:\t" << FLAGS_tuple_num << endl; - cout << "#FLAGS_ycsb:\t\t" << FLAGS_ycsb << endl; - cout << "#FLAGS_zipf_skew:\t" << FLAGS_zipf_skew << endl; -} - -void genLogFile(std::string &logpath, const int thid) { - genLogFileName(logpath, thid); - createEmptyFile(logpath); -} - -void partTableInit([[maybe_unused]] size_t thid, uint64_t start, uint64_t end) { -#if MASSTREE_USE - MasstreeWrapper::thread_init(thid); -#endif - - for (auto i = start; i <= end; ++i) { - Tuple *tmp; - tmp = &Table[i]; - tmp->tidword_.epoch = 1; - tmp->tidword_.latest = 1; - tmp->tidword_.lock = 0; - tmp->val_[0] = 'a'; - tmp->val_[1] = '\0'; - -#if MASSTREE_USE - MT.insert_value(i, tmp); -#endif - } -} - -void makeDB() { - if (posix_memalign((void **)&Table, PAGE_SIZE, - (FLAGS_tuple_num) * sizeof(Tuple)) != 0) - ERR; -#if dbs11 - if (madvise((void *)Table, (FLAGS_tuple_num) * sizeof(Tuple), - MADV_HUGEPAGE) != 0) - ERR; -#endif - - size_t maxthread = decideParallelBuildNumber(FLAGS_tuple_num); - - std::vector thv; - for (size_t i = 0; i < maxthread; ++i) - thv.emplace_back(partTableInit, i, i * (FLAGS_tuple_num / maxthread), - (i + 1) * (FLAGS_tuple_num / maxthread) - 1); - for (auto &th : thv) th.join(); -} - -void leaderWork(uint64_t &epoch_timer_start, uint64_t &epoch_timer_stop) { - epoch_timer_stop = rdtscp(); - if (chkClkSpan(epoch_timer_start, epoch_timer_stop, - FLAGS_epoch_time * FLAGS_clocks_per_us * 1000) && - chkEpochLoaded()) { - atomicAddGE(); - epoch_timer_start = epoch_timer_stop; - } -} - -void ShowOptParameters() { - cout << "#ShowOptParameters()" - << ": ADD_ANALYSIS " << ADD_ANALYSIS << ": BACK_OFF " << BACK_OFF - << ": KEY_SIZE " << KEY_SIZE << ": MASSTREE_USE " << MASSTREE_USE - << ": NO_WAIT_LOCKING_IN_VALIDATION " << NO_WAIT_LOCKING_IN_VALIDATION - << ": PARTITION_TABLE " << PARTITION_TABLE << ": PROCEDURE_SORT " - << PROCEDURE_SORT << ": SLEEP_READ_PHASE " << SLEEP_READ_PHASE - << ": VAL_SIZE " << VAL_SIZE << ": WAL " << WAL << endl; -} diff --git a/docs/contributing_en.md b/docs/contributing_en.md index 8f0cb9d2..08ec1fb2 100644 --- a/docs/contributing_en.md +++ b/docs/contributing_en.md @@ -139,6 +139,101 @@ Concretely: - From `CLAUDE_en.md` (English): link to `docs/_en.md` - From `README.md` (English, canonical): link to `docs/_en.md` (for English readers) +## Adding a new protocol + +How to add a new concurrency-control (CC) protocol as `cc//`. **Line-number-based instructions are deliberately avoided** — they go stale on every internal refactor. Instead this section is conceptual: "what the real work is", "which file plays which role", and "which mechanism you plug into". For the full architecture overview and the TxExecutor contract details, see [architecture_en.md](architecture_en.md). + +### The real work: implement the CC algorithm in the tx operations + +**Adding a new protocol means implementing the concurrency-control algorithm itself in `TxExecutor`'s transaction operations.** ccbench's mission is to *compare CC protocols on a shared foundation* (the Masstree index, the workload templates, the measurement harness), and the differences between protocols live precisely in the bodies of these operations. Copying an existing protocol's directory only **erects the scaffolding (a starting point)** — it is not the work itself. Copying, renaming, and registering with CMake does not produce a "new protocol" if the bodies are still those of the original protocol. + +The operations to implement, their responsibilities, and how their bodies change with the CC algorithm: + +| Operation | Common responsibility | What changes with the algorithm | +|---|---|---| +| `read` | Fetch the visible data for a key and return it to the caller. Read-own-writes (return from the local write set if present) also belongs here | **What counts as "visible"**. OCC reads the latest version while recording its TID into the read set / 2PL takes a read lock at access time / MVCC picks, from the version chain, the latest version at or before its own timestamp | +| `update` | Reserve an update to an existing record (`WARN_NOT_FOUND` if it does not exist) | **When the update becomes visible**. OCC just stages it into the write set, deferred until commit / 2PL takes a write lock here (upgrading if it holds a read lock) / MVCC creates and links a new version in the pending state | +| `insert` | Create a new record (`WARN_ALREADY_EXISTS` if it exists) | How the new `Tuple` is initialized. OCC creates it with the `absent` bit and sets it at commit / 2PL creates it already write-locked / MVCC gives it a first version. May need a Masstree node version check (silo's `node_map_`) | +| `delete_record` | Reserve a record deletion | OCC sets `absent` and pushes to the GC queue / 2PL takes a write lock and unlinks from the index at commit / MVCC stages a delete-marked new version | +| `scan` | Walk a key range and return each visible record. **Both the `int64_t limit` overload and the one without are required** (the concept demands it) | For each tuple in the range, apply essentially the same decision as `read`, per the CC algorithm | +| `commit` | Finalize the transaction; return success/failure as `bool` | **The heart of the protocol**. OCC validates the read set → locks the write set → write phase / 2PL (assuming it already holds every lock) applies writes and releases locks / MVCC runs a version consistency check → promotes pending versions to committed | +| `abort` | Roll the transaction back and release the resources it acquired | OCC removes inserted rows and clears the read/write sets / 2PL releases every held lock / MVCC discards pending versions | + +`begin` (timestamp allocation, etc.), the tuple/version memory layout, the lock/validation logic, and GC (garbage collection) are **likewise things you redesign to fit the algorithm**. Take the tuple layout alone: silo's `Tuple` carries a `Tidword` (lock bit + epoch + TID), ss2pl's carries a `ReaderWriteLock`, and mvto's `Tuple` carries the head of an `atomic` version chain plus `min_wts_`. Designing "what to write in `read`/`commit`, and what to put in the tuple, for my algorithm" is the center of this work. + +The CC families in one line each: + +- **OCC (optimistic, e.g. [cc/silo/](../cc/silo/))** — takes no locks during access and validates everything at `commit`. `read` records the TID, and `commit` only writes after confirming "the versions I read have not changed". +- **2PL (pessimistic, e.g. [cc/ss2pl/](../cc/ss2pl/))** — takes a lock the moment it accesses (shared for read, exclusive for update). All `commit` does is apply the writes and release the locks. +- **MVCC (multi-version, e.g. [cc/cicada/](../cc/cicada/), [cc/mvto/](../cc/mvto/))** — keeps a version chain per record and decides visibility by timestamp order. `read` selects a version, `update` creates a new version, `commit` runs a version consistency check. GC of old versions is mandatory. + +For the deterministic family, see [cc/d2pl/](../cc/d2pl/). + +### Starting point: copy an existing protocol as scaffolding + +Doing the real work above *together with* creating files from scratch is painful, so **copy the whole directory (`cc//`) of the existing protocol closest to your goal and use it as scaffolding (a starting point)**. What the copy gives you is "a `TxExecutor` skeleton that builds", "the wiring to the workload templates", and "boilerplate like `result.cc` / `util.cc`" — **the CC algorithm does not come with it**. Right after copying, you have nothing but "a working copy of the original protocol"; the work is to actually rewrite the tx operations, tuple layout, lock/validation logic, and GC from the table above to fit the CC algorithm you want to implement. "Copy and rename and done" does not yield a new protocol. + +Pick what to scaffold from by which family your target algorithm is closest to: + +- Optimistic (OCC) family → [cc/silo/](../cc/silo/) — the most straightforward reference implementation, supports all four workloads +- Multi-version (MVCC) family → [cc/cicada/](../cc/cicada/) or [cc/mvto/](../cc/mvto/) +- Pessimistic / lock-based → [cc/ss2pl/](../cc/ss2pl/) +- Deterministic → [cc/d2pl/](../cc/d2pl/) + +There used to be a `cc_format/` "single-version template" directory, but it was Makefile-based and diverged from the main CMake build, and its `README.md` steps were line-number-based and went stale — so it was removed (#83; see #34 for the discussion history). The template's role is replaced by "copy an existing protocol" plus, if needed, AI scaffolding (have it generate a skeleton that satisfies the TxExecutor contract). + +### Directory components + +Rewrite the scaffolded `cc//` to match the new protocol name and the CC algorithm. Each file's role: + +| File | Role | +|---|---| +| `transaction.cc` / `include/transaction.hh` | **The protocol's core**. `include/transaction.hh` defines the `TxExecutor` class (with `static_assert(TxExecutorLike);` right after the class definition — [include/tx_executor_concept.hh](../include/tx_executor_concept.hh)), and `transaction.cc` holds the implementations of `read` / `update` / `insert` / `delete_record` / `scan` / `commit` / `abort`. The "real work" above is mostly rewriting here | +| `include/tuple.hh` / `include/version.hh`, etc. | The `Tuple` / `Version` memory layout. Design here the metadata your algorithm needs — lock bits, timestamps, version chains, etc. | +| `include/*_op_element.hh` | The read/write set element types. Determined by what `commit`'s validation needs to remember | +| `CMakeLists.txt` | Just one call to `ccbench_add_protocol( ...)`. Details below | +| `_.cc` | Per-workload entry point (`ycsb_*`, `tpcc_*`, `bomb_*`, `sbomb_*`). Defines `worker()` and drives the matching workload template ([include/ycsb.hh](../include/ycsb.hh), [include/tpcc.hh](../include/tpcc.hh), [include/bomb.hh](../include/bomb.hh), etc.). You need one per tag listed in the `CMakeLists.txt` `WORKLOADS` | +| `result.cc` | Defines the per-thread result buffer (the `Result` vector) and `initResult()` | +| `util.cc` / `include/util.hh` | DB initialization, record initial-value setup, the leader thread's job (`leaderWork`), etc. | + +### Wiring after the scaffolding: plug into `ccbench_add_protocol()` + +The build is assembled declaratively by the `ccbench_add_protocol()` helper in [cmake/ProtocolHelpers.cmake](../cmake/ProtocolHelpers.cmake). A `cc//CMakeLists.txt` is just a single call to this helper: + +```cmake +ccbench_add_protocol( + SOURCES transaction.cc util.cc result.cc # .cc shared across workloads; don't put entry points here + WORKLOADS ycsb tpcc bomb sbomb # a subset of the supported workload tags + OPTIONS # protocol-specific -D defines (optional) + FOO=${CCBENCH_FOO} +) +``` + +- For each tag `W` in `WORKLOADS`, the helper builds `W_.exe` from `W_.cc` + `SOURCES` and links `ccbench_common` + `ccbench::masstree` + `ccbench::mimalloc`. +- The universal `-D` flags (`KEY_SIZE`, `VAL_SIZE`, `BACK_OFF`, `ADD_ANALYSIS`, `MASSTREE_USE`, etc.) and `-Wall -Wextra -Werror` are added automatically. To add a protocol-specific cache option, add it to [cmake/Options.cmake](../cmake/Options.cmake). +- Finally, add the new directory name to the `foreach(_proto …)` loop in the top-level [CMakeLists.txt](../CMakeLists.txt). This also makes a row appear automatically in [build/PROTOCOL_MATRIX.md](../build/PROTOCOL_MATRIX.md) at configure time. + +### Enforced by the TxExecutor contract + +The tx operations you rewrite in "the real work" above must satisfy the `TxExecutor` API the workload templates expect. This contract is expressed as the `TxExecutorLike` concept in [include/tx_executor_concept.hh](../include/tx_executor_concept.hh), and each protocol **enforces it at compile time** via `static_assert(TxExecutorLike);` at the end of `transaction.hh`. A missing method or a signature mismatch (e.g. forgetting the `int64_t limit` overload of `scan`) fails that protocol's own build with a named diagnostic, so it never becomes a runtime crash. The concept only constrains "the outward shape, the signatures" — it does *not* guarantee that the body of each operation contains a correct CC algorithm; that is the implementer's responsibility. For the meaning of each contract method, see [architecture_en.md](architecture_en.md). + +### Checklist + +CC algorithm implementation (the real work): + +- [ ] Rewrote `read` / `update` / `insert` / `delete_record` / `scan` / `commit` / `abort` to fit the CC algorithm being implemented (no logic left over from the copy source) +- [ ] Redesigned the tuple/version layout, lock/validation logic, and GC to fit the algorithm +- [ ] Have both the `int64_t limit` overload of `scan` and the one without + +Scaffolding / wiring: + +- [ ] Copied `cc//` from the existing protocol closest to the goal and renamed it +- [ ] `cc//CMakeLists.txt` calls `ccbench_add_protocol( ...)` +- [ ] Added `` to the `foreach(_proto …)` loop in the top-level `CMakeLists.txt` +- [ ] `transaction.hh` ends with `static_assert(TxExecutorLike);` +- [ ] `cmake -S . -B build` passes and each binary listed in `WORKLOADS` builds +- [ ] Added a row to the protocol table in [docs/protocols_en.md](protocols_en.md) (canonical is `_ja`, so update it as a pair) + ## Sending a PR (TODO) PR conventions are not yet written up. Add as you notice patterns. diff --git a/docs/contributing_ja.md b/docs/contributing_ja.md index 1dd61f04..028898ba 100644 --- a/docs/contributing_ja.md +++ b/docs/contributing_ja.md @@ -136,6 +136,101 @@ - `CLAUDE_en.md` (英語) → `docs/_en.md` にリンク - `README.md` (英語、原典) → `docs/_en.md` にリンク (英語 reader 向け) +## 新しいプロトコルを追加する + +新しい並行性制御 (CC) プロトコルを `cc//` として足すときの手順。**行番号ベースの指示は意図的に書かない** — 内部リファクタリングのたびに陳腐化するため。代わりに「作業の本体は何か」「どのファイルが何の役割を持つか」「どの仕組みに乗るか」を概念ベースで説明する。アーキテクチャ全体像と TxExecutor 契約の詳細は [architecture_ja.md](architecture_ja.md) を参照。 + +### 作業の本体: tx 操作に CC アルゴリズムを実装する + +**新プロトコルの追加とは、`TxExecutor` のトランザクション操作に並行制御アルゴリズムそのものを実装すること。** ccbench の使命は「共通基盤 (Masstree インデックス、ワークロードテンプレート、計測ハーネス) の上で CC プロトコルを比較する」ことであり、プロトコル間の差分はまさにこれらの操作の中身に宿る。既存プロトコルのディレクトリをコピーするのは**足場 (出発点) を組むだけ**であって、それ自体は作業ではない。コピーして改名して CMake に登録しても、中身が元のプロトコルのままなら「新プロトコル」は存在しない。 + +実装すべき操作と、その責務、CC アルゴリズムによって中身がどう変わるか: + +| 操作 | 共通の責務 | アルゴリズムによって変わるところ | +|---|---|---| +| `read` | キーから可視なデータを取り出して呼び出し側に返す。read-own-writes (自分の write set にあればそれを返す) もここ | **何を「可視」とみなすか**。OCC は最新版を読みつつ TID を read set に控える / 2PL はアクセス時に read ロックを取る / MVCC は自分のタイムスタンプ以前の最新バージョンをバージョンチェーンから選ぶ | +| `update` | 既存レコードへの更新を予約する (レコードが無ければ `WARN_NOT_FOUND`) | **更新をいつ可視にするか**。OCC は write set に積むだけで commit まで遅延 / 2PL はここで write ロック (read ロック保持中なら昇格) / MVCC は pending 状態の新バージョンを生成・連結 | +| `insert` | 新規レコードを作る (既存なら `WARN_ALREADY_EXISTS`) | 新規 `Tuple` の初期化方法。OCC は `absent` ビット付きで作り commit 時に立てる / 2PL は生成時に write ロック済み / MVCC は最初のバージョンを持たせる。Masstree ノードの版数チェックが要るかも (silo の `node_map_`) | +| `delete_record` | レコード削除を予約する | OCC は `absent` を立てて GC キューへ / 2PL は write ロックを取り commit 時にインデックスから外す / MVCC は削除マークの新バージョンを積む | +| `scan` | キー範囲を走査し可視な各レコードを返す。`int64_t limit` 付きと無しの**両方の overload が必須** (concept が要求) | 範囲内の各タプルに対して実質 `read` と同じ判断を、CC アルゴリズムごとに適用する | +| `commit` | トランザクションを確定し、成否を `bool` で返す | **プロトコルの心臓部**。OCC は read set 検証 → write ロック → write phase / 2PL は (ロックは既に全部持っている前提で) write を反映してロック解放 / MVCC は version consistency check → pending バージョンを committed に昇格 | +| `abort` | トランザクションを巻き戻し、確保した資源を解放する | OCC は insert した行を消し read/write set をクリア / 2PL は保持ロックを全解放 / MVCC は pending バージョンを破棄 | + +`begin` (タイムスタンプ確保など) や、tuple/version のメモリレイアウト、ロック・検証ロジック、GC (Garbage Collection) も**同じくアルゴリズムに応じて設計し直す対象**。例えば tuple のレイアウトひとつ取っても: silo は `Tuple` に `Tidword` (ロックビット + epoch + TID) を持ち、ss2pl は `ReaderWriteLock` を持ち、mvto は `Tuple` が `atomic` のバージョンチェーン先頭と `min_wts_` を持つ。「自分のアルゴリズムでは read/commit に何を書くか、tuple に何を持たせるか」を設計するのがこの作業の中心。 + +CC ファミリ間の違いを一言でまとめると: + +- **OCC (楽観, 例: [cc/silo/](../cc/silo/))** — アクセス中はロックを取らず、`commit` 時にまとめて検証する。`read` は TID を控え、`commit` で「読んだ版が変わっていないか」を確かめて初めて書き込む。 +- **2PL (悲観, 例: [cc/ss2pl/](../cc/ss2pl/))** — アクセスした瞬間にロックを取る (read は共有、update は排他)。`commit` 時にやることは write の反映とロック解放だけ。 +- **MVCC (多版, 例: [cc/cicada/](../cc/cicada/), [cc/mvto/](../cc/mvto/))** — レコードごとにバージョンチェーンを持ち、タイムスタンプ順序で可視性を決める。`read` はバージョン選択、`update` は新バージョン生成、`commit` は version consistency check。古いバージョンの GC が必須。 + +決定論的なものは [cc/d2pl/](../cc/d2pl/) を参照。 + +### 出発点: 既存プロトコルをコピーして足場にする + +上記の本体作業をゼロからのファイル作成と一緒にやると大変なので、**目的に一番近い既存プロトコルのディレクトリ (`cc//`) を丸ごとコピーし、足場 (出発点) として使う**。コピーが提供してくれるのは「ビルドが通る `TxExecutor` の雛形」「ワークロードテンプレートとの配線」「`result.cc` / `util.cc` などの定型部分」であって、**CC アルゴリズムは付いてこない**。コピー直後の状態は「元のプロトコルの動くコピー」にすぎず、ここから上表の tx 操作・tuple レイアウト・ロック/検証ロジック・GC を、実装したい CC アルゴリズムに合わせて実際に書き換えていくのが作業。「コピーして改名して終わり」では新プロトコルにならない。 + +どれを足場にするかは、実装したいアルゴリズムが上記どのファミリに近いかで選ぶ: + +- 楽観的 (OCC) 系なら [cc/silo/](../cc/silo/) — 一番素直で、ワークロード 4 種すべてに対応している参照実装 +- 多版 (MVCC) 系なら [cc/cicada/](../cc/cicada/) や [cc/mvto/](../cc/mvto/) +- 悲観的 / ロックベースなら [cc/ss2pl/](../cc/ss2pl/) +- 決定論的なら [cc/d2pl/](../cc/d2pl/) + +かつて `cc_format/` という「単版用テンプレート」ディレクトリがあったが、Makefile ベースで本体 CMake ビルドと乖離し、`README.md` の手順も行番号ベースで陳腐化していたため削除した (#83、議論の経緯は #34)。テンプレートの役割は「既存プロトコルのコピー」と、必要なら AI スキャフォルディング (TxExecutor 契約を満たす雛形を生成させる) で代替する。 + +### ディレクトリの構成要素 + +足場としてコピーした `cc//` を、新プロトコル名と CC アルゴリズムに合わせて書き換える。各ファイルの役割: + +| ファイル | 役割 | +|---|---| +| `transaction.cc` / `include/transaction.hh` | **プロトコルの中核**。`include/transaction.hh` が `TxExecutor` クラスを定義し (クラス定義の直後で `static_assert(TxExecutorLike);` — [include/tx_executor_concept.hh](../include/tx_executor_concept.hh))、`transaction.cc` が `read` / `update` / `insert` / `delete_record` / `scan` / `commit` / `abort` の実装を持つ。上記「作業の本体」で書き換えるのは主にここ | +| `include/tuple.hh` / `include/version.hh` 等 | `Tuple` / `Version` のメモリレイアウト。ロックビット・タイムスタンプ・バージョンチェーンなど、アルゴリズムが必要とするメタデータをここで設計する | +| `include/*_op_element.hh` | read/write set の要素型。`commit` の検証で何を覚えておく必要があるかで決まる | +| `CMakeLists.txt` | `ccbench_add_protocol( ...)` を 1 回呼ぶだけ。詳細は下記 | +| `_.cc` | ワークロードごとのエントリポイント (`ycsb_*`, `tpcc_*`, `bomb_*`, `sbomb_*`)。`worker()` を定義し、対応するワークロードテンプレート ([include/ycsb.hh](../include/ycsb.hh), [include/tpcc.hh](../include/tpcc.hh), [include/bomb.hh](../include/bomb.hh) 等) を駆動する。`CMakeLists.txt` の `WORKLOADS` に挙げたタグ分だけ必要 | +| `result.cc` | スレッドごとの集計結果バッファ (`Result` ベクタ) と `initResult()` の定義 | +| `util.cc` / `include/util.hh` | DB 初期化、レコードの初期値設定、リーダースレッドの仕事 (`leaderWork`) など | + +### 足場を組んだ後の配線: `ccbench_add_protocol()` に乗せる + +ビルドは [cmake/ProtocolHelpers.cmake](../cmake/ProtocolHelpers.cmake) の `ccbench_add_protocol()` ヘルパーが宣言的に組み立てる。`cc//CMakeLists.txt` はこのヘルパーを 1 回呼ぶだけで済む: + +```cmake +ccbench_add_protocol( + SOURCES transaction.cc util.cc result.cc # ワークロード間で共有する .cc。エントリポイントは入れない + WORKLOADS ycsb tpcc bomb sbomb # サポートするワークロードタグの部分集合 + OPTIONS # プロトコル固有の -D defines (任意) + FOO=${CCBENCH_FOO} +) +``` + +- ヘルパーは `WORKLOADS` の各タグ `W` について `W_.exe` を `W_.cc` + `SOURCES` からビルドし、`ccbench_common` + `ccbench::masstree` + `ccbench::mimalloc` をリンクする。 +- 汎用 `-D` フラグ (`KEY_SIZE`, `VAL_SIZE`, `BACK_OFF`, `ADD_ANALYSIS`, `MASSTREE_USE` 等) と `-Wall -Wextra -Werror` は自動で付く。プロトコル固有の cache オプションを増やすときは [cmake/Options.cmake](../cmake/Options.cmake) に追加する。 +- 最後に、トップレベル [CMakeLists.txt](../CMakeLists.txt) の `foreach(_proto …)` ループに新しいディレクトリ名を 1 つ追加する。これで configure 時に [build/PROTOCOL_MATRIX.md](../build/PROTOCOL_MATRIX.md) にも自動で行が増える。 + +### TxExecutor 契約による強制 + +上記「作業の本体」で書き換える tx 操作は、ワークロードテンプレートが期待する `TxExecutor` API を満たさなければならない。この契約は [include/tx_executor_concept.hh](../include/tx_executor_concept.hh) の `TxExecutorLike` concept として表現され、各プロトコルが `transaction.hh` 末尾の `static_assert(TxExecutorLike);` で**コンパイル時に強制**する。メソッドの欠落やシグネチャ不一致 (例: `scan` の `int64_t limit` 付き overload 忘れ) は、そのプロトコル自身のビルドが named diagnostic 付きで失敗するので、実行時クラッシュにはならない。concept はあくまで「シグネチャという外形」を縛るだけで、各操作の中身に正しい CC アルゴリズムが入っているかは保証しない — そこは実装者の責任。契約の各メソッドの意味は [architecture_ja.md](architecture_ja.md) を参照。 + +### チェックリスト + +CC アルゴリズムの実装 (本体): + +- [ ] `read` / `update` / `insert` / `delete_record` / `scan` / `commit` / `abort` を、実装する CC アルゴリズムに合わせて書き換えた (コピー元のロジックが残っていない) +- [ ] tuple/version レイアウト・ロック/検証ロジック・GC をアルゴリズムに合わせて設計し直した +- [ ] `scan` の `int64_t limit` 付き・無し両 overload がある + +足場・配線: + +- [ ] `cc//` を目的に近い既存プロトコルからコピーして名前を書き換えた +- [ ] `cc//CMakeLists.txt` が `ccbench_add_protocol( ...)` を呼んでいる +- [ ] トップレベル `CMakeLists.txt` の `foreach(_proto …)` に `` を追加した +- [ ] `transaction.hh` 末尾に `static_assert(TxExecutorLike);` がある +- [ ] `cmake -S . -B build` が通り、`WORKLOADS` に挙げた各バイナリがビルドできる +- [ ] [docs/protocols_ja.md](protocols_ja.md) のプロトコル表に行を足した (原典なので `_en` もセットで) + ## PR を出すとき (TODO) PR の出し方の規約はまだ書いていない。気付いた点があれば追記。