From 0804e9325c450b6b2947a1b5395e6283f9334130 Mon Sep 17 00:00:00 2001 From: Pawel Karczewski Date: Thu, 17 Dec 2020 10:53:39 +0100 Subject: [PATCH 1/5] Copy clang-format from pmemkv --- .clang-format | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 .clang-format diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..a4c4324 --- /dev/null +++ b/.clang-format @@ -0,0 +1,34 @@ +AccessModifierOffset: -8 +AlignOperands: false +AllowShortBlocksOnASingleLine: false +AllowShortFunctionsOnASingleLine: false +AllowShortIfStatementsOnASingleLine: false +AlwaysBreakAfterDefinitionReturnType: false +AlwaysBreakTemplateDeclarations: true +BasedOnStyle: LLVM +BraceWrapping: + AfterClass: false + AfterControlStatement: false + AfterEnum: false + AfterFunction: true + AfterNamespace: true + AfterObjCDeclaration: false + AfterStruct: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false +BreakBeforeBraces: Custom +BreakStringLiterals: false +ColumnLimit: 90 +ConstructorInitializerAllOnOneLineOrOnePerLine: false +ContinuationIndentWidth: 8 +FixNamespaceComments: false +IndentCaseLabels: true +IndentWidth: 8 +PointerAlignment: Right +SpaceBeforeParens: ControlStatements +SpacesBeforeTrailingComments: 1 +SpacesInContainerLiterals: false +SpacesInCStyleCastParentheses: false +UseTab: Always From 1d9a706fa0c471b52e60df61defd814d3782a2ba Mon Sep 17 00:00:00 2001 From: Pawel Karczewski Date: Fri, 18 Dec 2020 14:14:30 +0100 Subject: [PATCH 2/5] Increase ColumnLimit to 110 characters --- .clang-format | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.clang-format b/.clang-format index a4c4324..f03f9cc 100644 --- a/.clang-format +++ b/.clang-format @@ -20,7 +20,7 @@ BraceWrapping: IndentBraces: false BreakBeforeBraces: Custom BreakStringLiterals: false -ColumnLimit: 90 +ColumnLimit: 110 ConstructorInitializerAllOnOneLineOrOnePerLine: false ContinuationIndentWidth: 8 FixNamespaceComments: false From 52bf7a8f00a84c83b249bff2d2c6bec67f6237ac Mon Sep 17 00:00:00 2001 From: Pawel Karczewski Date: Thu, 17 Dec 2020 11:23:51 +0100 Subject: [PATCH 3/5] make: Add cpp-format target --- Makefile | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/Makefile b/Makefile index 34a5746..775b50e 100644 --- a/Makefile +++ b/Makefile @@ -5,6 +5,9 @@ bench: CFLAGS = $(shell pkg-config --cflags libpmemkv) -DOS_LINUX -fno-builtin-memcmp -march=native -DNDEBUG -O2 -std=c++11 bench: LDFLAGS = -ldl -lpthread $(shell pkg-config --libs libpmemkv) +CPP_FILES = $(shell find . -iname "*.h" -o -iname "*.cc" -o -iname "*.cpp" -o -iname "*.hpp") + +.PHONY: cppformat $(CPP_FILES) reset: rm -rf /dev/shm/pmemkv /tmp/pmemkv @@ -17,3 +20,8 @@ bench: reset run_bench: bench PMEM_IS_PMEM_FORCE=1 ./pmemkv_bench --db=/dev/shm/pmemkv --db_size_in_gb=1 --histogram=1 + +cppformat: $(CPP_FILES) + +$(CPP_FILES): + clang-format-10 -i $@ From b6642350ba8921fe04a0eb56190c538cf60e7743 Mon Sep 17 00:00:00 2001 From: Pawel Karczewski Date: Fri, 18 Dec 2020 14:23:01 +0100 Subject: [PATCH 4/5] Format c++ code with clang-format It's formatted with rules copied from pmemkv but with clang-format-10.0.1, so code style may be little bit different than in pmemkv project. If you use `git blame` and see this commit, most probably you want to use `--ignore-revs-file` option, or set git config option ''' git config blame.ignoreRevsFile .git-blame-ignore-revs ''' It's impossible to create .git-blame-ignore-revs file in this commit, as such file have to contain sha of this commit FIXES: #24 --- bench/db_bench.cc | 1663 ++++++++++++++-------------- bench/include/leveldb/env.h | 616 ++++++----- bench/include/leveldb/slice.h | 187 ++-- bench/include/leveldb/status.h | 214 ++-- bench/port/atomic_pointer.h | 270 +++-- bench/port/port_posix.cc | 75 +- bench/port/port_posix.h | 177 +-- bench/port/thread_annotations.h | 5 +- bench/util/csv.h | 16 +- bench/util/env.cc | 144 +-- bench/util/env_posix.cc | 1370 ++++++++++++----------- bench/util/env_posix_test_helper.h | 26 +- bench/util/histogram.cc | 215 ++-- bench/util/histogram.h | 63 +- bench/util/logging.cc | 109 +- bench/util/logging.h | 24 +- bench/util/mutexlock.h | 38 +- bench/util/posix_logger.h | 147 +-- bench/util/random.h | 102 +- bench/util/status.cc | 130 +-- bench/util/testutil.cc | 76 +- bench/util/testutil.h | 76 +- 22 files changed, 3058 insertions(+), 2685 deletions(-) diff --git a/bench/db_bench.cc b/bench/db_bench.cc index 5d6d8e5..811d063 100644 --- a/bench/db_bench.cc +++ b/bench/db_bench.cc @@ -9,62 +9,60 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2017-2020, Intel Corporation -#include -#include +#include #include #include +#include +#include +#include #include -#include -#include #include -#include -#include +#include +#include -#include "leveldb/env.h" -#include "testutil.h" -#include "port/port_posix.h" -#include "histogram.h" #include "csv.h" +#include "histogram.h" +#include "leveldb/env.h" +#include "libpmemkv.hpp" #include "mutexlock.h" +#include "port/port_posix.h" #include "random.h" -#include "libpmemkv.hpp" +#include "testutil.h" static const std::string USAGE = - "pmemkv_bench\n" - "--engine= (storage engine name, default: cmap)\n" - "--db= (path to persistent pool, default: /dev/shm/pmemkv)\n" - " (note: file on DAX filesystem, DAX device, or poolset file)\n" - "--db_size_in_gb= (size of persistent pool to create in GB, default: 0)\n" - " (note: always use 0 with existing poolset or device DAX configs)\n" - " (note: when pool path is non-existing, value should be > 0)\n" - "--histogram=<0|1> (show histograms when reporting latencies)\n" - "--num= (number of keys to place in database, default: 1000000)\n" - "--reads= (number of read operations, default: 1000000)\n" - "--threads= (number of concurrent threads, default: 1)\n" - "--key_size= (size of keys in bytes, default: 16)\n" - "--value_size= (size of values in bytes, default: 100)\n" - "--readwritepercent= (Ratio of reads to reads/writes (expressed " - "as percentage) for the ReadRandomWriteRandom workload. The default value " - "90 means 90% operations out of all reads and writes operations are reads. " - "In other words, 9 gets for every 1 put.) type: int32 default: 90\n" - "--disjoint=<0|1> (specifies whether each thread works on disjoint set of keys. " - "0 means that all threads read/write to the db using any key between 0 and `num`, so that " - "number of ops is `threads` * `num`. 1 means that each thread performs reads/writes using " - "only [`thread_id` * `num` / `threads`, (`thread_id` + 1) * `num` / `threads`) subset of keys, " - "so that total number of ops is `num`. The default value is 0.)\n" - "--benchmarks=, (comma-separated list of benchmarks to run)\n" - " fillseq (load N values in sequential key order)\n" - " fillrandom (load N values in random key order)\n" - " overwrite (replace N values in random key order)\n" - " readseq (read N values in sequential key order)\n" - " readrandom (read N values in random key order)\n" - " readmissing (read N missing values in random key order)\n" - " deleteseq (delete N values in sequential key order)\n" - " deleterandom (delete N values in random key order)\n" - " readwhilewriting (1 writer, N threads doing random reads)\n" - " readrandomwriterandom (N threads doing random-read, random-write)\n"; - - + "pmemkv_bench\n" + "--engine= (storage engine name, default: cmap)\n" + "--db= (path to persistent pool, default: /dev/shm/pmemkv)\n" + " (note: file on DAX filesystem, DAX device, or poolset file)\n" + "--db_size_in_gb= (size of persistent pool to create in GB, default: 0)\n" + " (note: always use 0 with existing poolset or device DAX configs)\n" + " (note: when pool path is non-existing, value should be > 0)\n" + "--histogram=<0|1> (show histograms when reporting latencies)\n" + "--num= (number of keys to place in database, default: 1000000)\n" + "--reads= (number of read operations, default: 1000000)\n" + "--threads= (number of concurrent threads, default: 1)\n" + "--key_size= (size of keys in bytes, default: 16)\n" + "--value_size= (size of values in bytes, default: 100)\n" + "--readwritepercent= (Ratio of reads to reads/writes (expressed " + "as percentage) for the ReadRandomWriteRandom workload. The default value " + "90 means 90% operations out of all reads and writes operations are reads. " + "In other words, 9 gets for every 1 put.) type: int32 default: 90\n" + "--disjoint=<0|1> (specifies whether each thread works on disjoint set of keys. " + "0 means that all threads read/write to the db using any key between 0 and `num`, so that " + "number of ops is `threads` * `num`. 1 means that each thread performs reads/writes using " + "only [`thread_id` * `num` / `threads`, (`thread_id` + 1) * `num` / `threads`) subset of keys, " + "so that total number of ops is `num`. The default value is 0.)\n" + "--benchmarks=, (comma-separated list of benchmarks to run)\n" + " fillseq (load N values in sequential key order)\n" + " fillrandom (load N values in random key order)\n" + " overwrite (replace N values in random key order)\n" + " readseq (read N values in sequential key order)\n" + " readrandom (read N values in random key order)\n" + " readmissing (read N missing values in random key order)\n" + " deleteseq (delete N values in sequential key order)\n" + " deleterandom (delete N values in random key order)\n" + " readwhilewriting (1 writer, N threads doing random reads)\n" + " readrandomwriterandom (N threads doing random-read, random-write)\n"; // Number of key/values to place in database static int FLAGS_num = 1000000; @@ -105,548 +103,575 @@ leveldb::Env *g_env = NULL; #if defined(__linux) -static Slice TrimSpace(Slice s) { - size_t start = 0; - while (start < s.size() && isspace(s[start])) { - start++; - } - size_t limit = s.size(); - while (limit > start && isspace(s[limit - 1])) { - limit--; - } - return Slice(s.data() + start, limit - start); +static Slice TrimSpace(Slice s) +{ + size_t start = 0; + while (start < s.size() && isspace(s[start])) { + start++; + } + size_t limit = s.size(); + while (limit > start && isspace(s[limit - 1])) { + limit--; + } + return Slice(s.data() + start, limit - start); } #endif - // Helper for quickly generating random data. class RandomGenerator { - private: - std::string data_; - unsigned int pos_; - - public: - RandomGenerator() { - // We use a limited amount of data over and over again and ensure - // that it is larger than the compression window (32KB), and also - // large enough to serve all typical value sizes we want to write. - Random rnd(301); - std::string piece; - while (data_.size() < (unsigned)std::max(1048576, FLAGS_value_size)) { - // Add a short fragment that is as compressible as specified - // by FLAGS_compression_ratio. - test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); - data_.append(piece); - } - pos_ = 0; - } - - Slice Generate(unsigned int len) { - assert(len <= data_.size()); - if (pos_ + len > data_.size()) { - pos_ = 0; - } - pos_ += len; - return Slice(data_.data() + pos_ - len, len); - } - - Slice GenerateWithTTL(unsigned int len) { - assert(len <= data_.size()); - if (pos_ + len > data_.size()) { - pos_ = 0; - } - pos_ += len; - return Slice(data_.data() + pos_ - len, len); - } +private: + std::string data_; + unsigned int pos_; + +public: + RandomGenerator() + { + // We use a limited amount of data over and over again and ensure + // that it is larger than the compression window (32KB), and also + // large enough to serve all typical value sizes we want to write. + Random rnd(301); + std::string piece; + while (data_.size() < (unsigned)std::max(1048576, FLAGS_value_size)) { + // Add a short fragment that is as compressible as specified + // by FLAGS_compression_ratio. + test::CompressibleString(&rnd, FLAGS_compression_ratio, 100, &piece); + data_.append(piece); + } + pos_ = 0; + } + + Slice Generate(unsigned int len) + { + assert(len <= data_.size()); + if (pos_ + len > data_.size()) { + pos_ = 0; + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } + + Slice GenerateWithTTL(unsigned int len) + { + assert(len <= data_.size()); + if (pos_ + len > data_.size()) { + pos_ = 0; + } + pos_ += len; + return Slice(data_.data() + pos_ - len, len); + } }; -static void AppendWithSpace(std::string *str, Slice msg) { - if (msg.empty()) return; - if (!str->empty()) { - str->push_back(' '); - } - str->append(msg.data(), msg.size()); +static void AppendWithSpace(std::string *str, Slice msg) +{ + if (msg.empty()) + return; + if (!str->empty()) { + str->push_back(' '); + } + str->append(msg.data(), msg.size()); } enum OperationType : unsigned char { - kRead = 0, - kWrite, - kDelete, - kSeek, - kMerge, - kUpdate, + kRead = 0, + kWrite, + kDelete, + kSeek, + kMerge, + kUpdate, }; - -class BenchmarkLogger : public CSV -{ +class BenchmarkLogger : public CSV { private: - struct hist - { + struct hist { std::string name; std::string histogram; }; std::vector histograms; + public: using CSV::insert; - BenchmarkLogger() : CSV("Benchmark") {}; + BenchmarkLogger() : CSV("Benchmark"){}; void insert(std::string name, Histogram histogram) { histograms.push_back({name, histogram.ToString()}); std::vector percentiles = {50, 75, 90, 99.9, 99.99}; - for (double &percentile : percentiles) - { + for (double &percentile : percentiles) { insert(name, "Percentilie P" + std::to_string(percentile), - histogram.Percentile(percentile)); + histogram.Percentile(percentile)); } insert(name, "Median", histogram.Median()); - } void print_histogram() { - std::cout << "------------------------------------------------" << std::endl; - for ( auto &histogram: histograms ) - { - std::cout << histogram.name << std::endl << histogram.histogram << std::endl; + std::cout << "------------------------------------------------" << std::endl; + for (auto &histogram : histograms) { + std::cout << histogram.name << std::endl << histogram.histogram << std::endl; } } }; class Stats { private: - double start_; - double finish_; - double seconds_; - int done_; - int next_report_; - int64_t bytes_; - double last_op_finish_; - Histogram hist_; - std::string message_; - bool exclude_from_merge_; + double start_; + double finish_; + double seconds_; + int done_; + int next_report_; + int64_t bytes_; + double last_op_finish_; + Histogram hist_; + std::string message_; + bool exclude_from_merge_; public: - Stats() { Start(); } - - void Start() { - next_report_ = 100; - last_op_finish_ = start_; - hist_.Clear(); - done_ = 0; - bytes_ = 0; - seconds_ = 0; - start_ = g_env->NowMicros(); - finish_ = start_; - message_.clear(); - // When set, stats from this thread won't be merged with others. - exclude_from_merge_ = false; - } - - void Merge(const Stats &other) { - if (other.exclude_from_merge_) - return; - - hist_.Merge(other.hist_); - done_ += other.done_; - bytes_ += other.bytes_; - seconds_ += other.seconds_; - if (other.start_ < start_) start_ = other.start_; - if (other.finish_ > finish_) finish_ = other.finish_; - - // Just keep the messages from one thread - if (message_.empty()) message_ = other.message_; - } - - void Stop() { - finish_ = g_env->NowMicros(); - seconds_ = (finish_ - start_) * 1e-6; - } - - void AddMessage(Slice msg) { - AppendWithSpace(&message_, msg); - } - - void SetExcludeFromMerge() { exclude_from_merge_ = true; } - - void FinishedSingleOp() { - if (FLAGS_histogram) { - double now = g_env->NowMicros(); - double micros = now - last_op_finish_; - hist_.Add(micros); - if (micros > 20000) { - fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); - fflush(stderr); - } - last_op_finish_ = now; - } - - done_++; - if (done_ >= next_report_) { - if (next_report_ < 1000) next_report_ += 100; - else if (next_report_ < 5000) next_report_ += 500; - else if (next_report_ < 10000) next_report_ += 1000; - else if (next_report_ < 50000) next_report_ += 5000; - else if (next_report_ < 100000) next_report_ += 10000; - else if (next_report_ < 500000) next_report_ += 50000; - else next_report_ += 100000; - fprintf(stderr, "... finished %d ops%30s\r", done_, ""); - fflush(stderr); - } - } - - void AddBytes(int64_t n) { - bytes_ += n; - } - - float get_micros_per_op() - { - // Pretend at least one op was done in case we are running a benchmark - // that does not call FinishedSingleOp(). - if (done_ < 1) done_ = 1; - return seconds_ * 1e6 / done_; - } - - float get_ops_per_sec() - { - // Pretend at least one op was done in case we are running a benchmark - // that does not call FinishedSingleOp(). - if (done_ < 1) done_ = 1; - double elapsed = (finish_ - start_) * 1e-6; - - return done_ / elapsed; - } - - float get_throughput() - { - // Rate and ops/sec is computed on actual elapsed time, not the sum of per-thread - // elapsed times. - double elapsed = (finish_ - start_) * 1e-6; - return (bytes_ / 1048576.0) / elapsed; - } - - std::string get_extra_data() - { - return message_; - } - - Histogram& get_histogram() - { - return hist_; - } + Stats() + { + Start(); + } + + void Start() + { + next_report_ = 100; + last_op_finish_ = start_; + hist_.Clear(); + done_ = 0; + bytes_ = 0; + seconds_ = 0; + start_ = g_env->NowMicros(); + finish_ = start_; + message_.clear(); + // When set, stats from this thread won't be merged with others. + exclude_from_merge_ = false; + } + + void Merge(const Stats &other) + { + if (other.exclude_from_merge_) + return; + + hist_.Merge(other.hist_); + done_ += other.done_; + bytes_ += other.bytes_; + seconds_ += other.seconds_; + if (other.start_ < start_) + start_ = other.start_; + if (other.finish_ > finish_) + finish_ = other.finish_; + + // Just keep the messages from one thread + if (message_.empty()) + message_ = other.message_; + } + + void Stop() + { + finish_ = g_env->NowMicros(); + seconds_ = (finish_ - start_) * 1e-6; + } + + void AddMessage(Slice msg) + { + AppendWithSpace(&message_, msg); + } + + void SetExcludeFromMerge() + { + exclude_from_merge_ = true; + } + + void FinishedSingleOp() + { + if (FLAGS_histogram) { + double now = g_env->NowMicros(); + double micros = now - last_op_finish_; + hist_.Add(micros); + if (micros > 20000) { + fprintf(stderr, "long op: %.1f micros%30s\r", micros, ""); + fflush(stderr); + } + last_op_finish_ = now; + } + + done_++; + if (done_ >= next_report_) { + if (next_report_ < 1000) + next_report_ += 100; + else if (next_report_ < 5000) + next_report_ += 500; + else if (next_report_ < 10000) + next_report_ += 1000; + else if (next_report_ < 50000) + next_report_ += 5000; + else if (next_report_ < 100000) + next_report_ += 10000; + else if (next_report_ < 500000) + next_report_ += 50000; + else + next_report_ += 100000; + fprintf(stderr, "... finished %d ops%30s\r", done_, ""); + fflush(stderr); + } + } + + void AddBytes(int64_t n) + { + bytes_ += n; + } + + float get_micros_per_op() + { + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) + done_ = 1; + return seconds_ * 1e6 / done_; + } + + float get_ops_per_sec() + { + // Pretend at least one op was done in case we are running a benchmark + // that does not call FinishedSingleOp(). + if (done_ < 1) + done_ = 1; + double elapsed = (finish_ - start_) * 1e-6; + + return done_ / elapsed; + } + + float get_throughput() + { + // Rate and ops/sec is computed on actual elapsed time, not the sum of per-thread + // elapsed times. + double elapsed = (finish_ - start_) * 1e-6; + return (bytes_ / 1048576.0) / elapsed; + } + + std::string get_extra_data() + { + return message_; + } + + Histogram &get_histogram() + { + return hist_; + } }; // State shared by all concurrent executions of the same benchmark. struct SharedState { - port::Mutex mu; - port::CondVar cv; - int total; + port::Mutex mu; + port::CondVar cv; + int total; - // Each thread goes through the following states: - // (1) initializing - // (2) waiting for others to be initialized - // (3) running - // (4) done + // Each thread goes through the following states: + // (1) initializing + // (2) waiting for others to be initialized + // (3) running + // (4) done - int num_initialized; - int num_done; - bool start; + int num_initialized; + int num_done; + bool start; - SharedState() : cv(&mu) {} + SharedState() : cv(&mu) + { + } }; // Per-thread state for concurrent executions of the same benchmark. struct ThreadState { - int tid; // 0..n-1 when running in n threads - Random rand; // Has different seeds for different threads - Stats stats; - SharedState *shared; - - ThreadState(int index) - : tid(index), - rand(1000 + index) { - } + int tid; // 0..n-1 when running in n threads + Random rand; // Has different seeds for different threads + Stats stats; + SharedState *shared; + + ThreadState(int index) : tid(index), rand(1000 + index) + { + } }; class Duration { - typedef std::chrono::high_resolution_clock::time_point time_point; + typedef std::chrono::high_resolution_clock::time_point time_point; + public: - Duration(uint64_t max_seconds, int64_t max_ops, int64_t ops_per_stage = 0) { - max_seconds_ = max_seconds; - max_ops_= max_ops; - ops_per_stage_ = (ops_per_stage > 0) ? ops_per_stage : max_ops; - ops_ = 0; - start_at_ = std::chrono::high_resolution_clock::now(); - } - - int64_t GetStage() { return std::min(ops_, max_ops_ - 1) / ops_per_stage_; } - - bool Done(int64_t increment) { - if (increment <= 0) increment = 1; // avoid Done(0) and infinite loops - ops_ += increment; - - if (max_seconds_) { - // Recheck every appx 1000 ops (exact iff increment is factor of 1000) - auto granularity = FLAGS_ops_between_duration_checks; - if ((ops_ / granularity) != ((ops_ - increment) / granularity)) { - time_point now = std::chrono::high_resolution_clock::now(); - return std::chrono::duration_cast(now - start_at_).count() >= max_seconds_; - } else { - return false; - } - } else { - return ops_ > max_ops_; - } - } + Duration(uint64_t max_seconds, int64_t max_ops, int64_t ops_per_stage = 0) + { + max_seconds_ = max_seconds; + max_ops_ = max_ops; + ops_per_stage_ = (ops_per_stage > 0) ? ops_per_stage : max_ops; + ops_ = 0; + start_at_ = std::chrono::high_resolution_clock::now(); + } + + int64_t GetStage() + { + return std::min(ops_, max_ops_ - 1) / ops_per_stage_; + } + + bool Done(int64_t increment) + { + if (increment <= 0) + increment = 1; // avoid Done(0) and infinite loops + ops_ += increment; + + if (max_seconds_) { + // Recheck every appx 1000 ops (exact iff increment is factor of 1000) + auto granularity = FLAGS_ops_between_duration_checks; + if ((ops_ / granularity) != ((ops_ - increment) / granularity)) { + time_point now = std::chrono::high_resolution_clock::now(); + return std::chrono::duration_cast(now - start_at_) + .count() >= max_seconds_; + } else { + return false; + } + } else { + return ops_ > max_ops_; + } + } private: - uint64_t max_seconds_; - int64_t max_ops_; - int64_t ops_per_stage_; - int64_t ops_; - time_point start_at_; + uint64_t max_seconds_; + int64_t max_ops_; + int64_t ops_per_stage_; + int64_t ops_; + time_point start_at_; }; class Benchmark { private: - pmem::kv::db *kv_; - int num_; - int value_size_; - int key_size_; - int reads_; - int64_t readwrites_; - BenchmarkLogger &logger; - Slice name; - int n; - const char *engine; - - void (Benchmark::*method)(ThreadState *) = NULL; - - void PrintHeader() { - PrintEnvironment(); - logger.insert(name.ToString(), "Path", FLAGS_db); - logger.insert(name.ToString(), "Engine", engine); - logger.insert(name.ToString(), "Keys [bytes each]", FLAGS_key_size); - logger.insert(name.ToString(), "Values [bytes each]", FLAGS_value_size); - logger.insert(name.ToString(), "Entries", num_); - logger.insert(name.ToString(), "RawSize [MB (estimated)]", - ((static_cast(FLAGS_key_size + FLAGS_value_size) * num_) - / 1048576.0)); - PrintWarnings(); - } - - void PrintWarnings() { + pmem::kv::db *kv_; + int num_; + int value_size_; + int key_size_; + int reads_; + int64_t readwrites_; + BenchmarkLogger &logger; + Slice name; + int n; + const char *engine; + + void (Benchmark::*method)(ThreadState *) = NULL; + + void PrintHeader() + { + PrintEnvironment(); + logger.insert(name.ToString(), "Path", FLAGS_db); + logger.insert(name.ToString(), "Engine", engine); + logger.insert(name.ToString(), "Keys [bytes each]", FLAGS_key_size); + logger.insert(name.ToString(), "Values [bytes each]", FLAGS_value_size); + logger.insert(name.ToString(), "Entries", num_); + logger.insert(name.ToString(), "RawSize [MB (estimated)]", + ((static_cast(FLAGS_key_size + FLAGS_value_size) * num_) / 1048576.0)); + PrintWarnings(); + } + + void PrintWarnings() + { #if defined(__GNUC__) && !defined(__OPTIMIZE__) - fprintf(stdout, - "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n" - ); + fprintf(stdout, "WARNING: Optimization is disabled: benchmarks unnecessarily slow\n"); #endif #ifndef NDEBUG - fprintf(stdout, - "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); + fprintf(stdout, "WARNING: Assertions are enabled; benchmarks unnecessarily slow\n"); #endif - } + } - void PrintEnvironment() { + void PrintEnvironment() + { #if defined(__linux) - time_t now = time(NULL); - auto formatted_time = std::string(ctime(&now)); - logger.insert(name.ToString(), "Date:", formatted_time.erase(formatted_time.find_last_not_of("\n"))); - - FILE *cpuinfo = fopen("/proc/cpuinfo", "r"); - if (cpuinfo != NULL) { - char line[1000]; - int num_cpus = 0; - std::string cpu_type; - std::string cache_size; - while (fgets(line, sizeof(line), cpuinfo) != NULL) { - const char *sep = strchr(line, ':'); - if (sep == NULL) { - continue; - } - Slice key = TrimSpace(Slice(line, sep - 1 - line)); - Slice val = TrimSpace(Slice(sep + 1)); - if (key == "model name") { - ++num_cpus; - cpu_type = val.ToString(); - } else if (key == "cache size") { - cache_size = val.ToString(); - } - } - fclose(cpuinfo); - logger.insert(name.ToString(), "CPU", std::to_string(num_cpus)); - logger.insert(name.ToString(), "CPU model", cpu_type); - logger.insert(name.ToString(), "CPUCache", cache_size); - } + time_t now = time(NULL); + auto formatted_time = std::string(ctime(&now)); + logger.insert(name.ToString(), + "Date:", formatted_time.erase(formatted_time.find_last_not_of("\n"))); + + FILE *cpuinfo = fopen("/proc/cpuinfo", "r"); + if (cpuinfo != NULL) { + char line[1000]; + int num_cpus = 0; + std::string cpu_type; + std::string cache_size; + while (fgets(line, sizeof(line), cpuinfo) != NULL) { + const char *sep = strchr(line, ':'); + if (sep == NULL) { + continue; + } + Slice key = TrimSpace(Slice(line, sep - 1 - line)); + Slice val = TrimSpace(Slice(sep + 1)); + if (key == "model name") { + ++num_cpus; + cpu_type = val.ToString(); + } else if (key == "cache size") { + cache_size = val.ToString(); + } + } + fclose(cpuinfo); + logger.insert(name.ToString(), "CPU", std::to_string(num_cpus)); + logger.insert(name.ToString(), "CPU model", cpu_type); + logger.insert(name.ToString(), "CPUCache", cache_size); + } #endif - } + } public: - Benchmark(Slice name, pmem::kv::db *kv, int num_threads, const char *engine, BenchmarkLogger &logger) - : - kv_(kv), - num_(FLAGS_num), - value_size_(FLAGS_value_size), - key_size_(FLAGS_key_size), - reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), - readwrites_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), - logger(logger), - n(num_threads), - name(name), - engine(engine) - { - fprintf(stderr, "running %s \n", name.ToString().c_str()); - bool fresh_db = false; - - if (name == Slice("fillseq")) { - fresh_db = true; - method = &Benchmark::WriteSeq; - } else if (name == Slice("fillrandom")) { - fresh_db = true; - method = &Benchmark::WriteRandom; - } else if (name == Slice("overwrite")) { - method = &Benchmark::WriteRandom; - } else if (name == Slice("readseq")) { - method = &Benchmark::ReadSeq; - } else if (name == Slice("readrandom")) { - method = &Benchmark::ReadRandom; - } else if (name == Slice("readmissing")) { - method = &Benchmark::ReadMissing; - } else if (name == Slice("deleteseq")) { - method = &Benchmark::DeleteSeq; - } else if (name == Slice("deleterandom")) { - method = &Benchmark::DeleteRandom; - } else if (name == Slice("readwhilewriting")) { - ++num_threads; - method = &Benchmark::ReadWhileWriting; - } else if (name == Slice("readrandomwriterandom")) { - method = &Benchmark::ReadRandomWriteRandom; - } else { - throw std::runtime_error("unknown benchmark: " + name.ToString()); - } - PrintHeader(); - - Open(fresh_db, name.ToString()); - } - - ~Benchmark() - { - kv_->close(); - } - - Slice AllocateKey(std::unique_ptr& key_guard) { - const char* tmp = new char[key_size_]; - key_guard.reset(tmp); - return Slice(key_guard.get(), key_size_); - } - - void GenerateKeyFromInt(uint64_t v, int64_t num_keys, Slice* key, bool missing = false) { - char *start = const_cast(key->data()); - char *pos = start; - int bytes_to_fill = std::min(key_size_ - static_cast(pos - start), 8); - if(missing){ - int64_t v1=-v; - memcpy(pos, static_cast(&v1), bytes_to_fill); - } - else - memcpy(pos, static_cast(&v), bytes_to_fill); - pos += bytes_to_fill; - if (key_size_ > pos - start) { - memset(pos, '0', key_size_ - (pos - start)); - } - } - - void Run() { - SharedState shared; - shared.total = n; - shared.num_initialized = 0; - shared.num_done = 0; - shared.start = false; - - ThreadArg *arg = new ThreadArg[n]; - for (int i = 0; i < n; i++) { - arg[i].bm = this; - arg[i].method = method; - arg[i].shared = &shared; - arg[i].thread = new ThreadState(i); - arg[i].thread->shared = &shared; - g_env->StartThread(ThreadBody, &arg[i]); - } - - shared.mu.Lock(); - while (shared.num_initialized < n) { - shared.cv.Wait(); - } - - shared.start = true; - shared.cv.SignalAll(); - while (shared.num_done < n) { - shared.cv.Wait(); - } - shared.mu.Unlock(); - - for (int i = 1; i < n; i++) { - arg[0].thread->stats.Merge(arg[i].thread->stats); - } - auto thread_stats = arg[0].thread->stats; - logger.insert(name.ToString(), "micros/op" ,thread_stats.get_micros_per_op()); - logger.insert(name.ToString(), "ops/sec" , thread_stats.get_ops_per_sec()); - logger.insert(name.ToString(), "throughput [MB/s]" , thread_stats.get_throughput()); - logger.insert(name.ToString(), "extra_data", thread_stats.get_extra_data()); - if (FLAGS_histogram) - { - logger.insert(name.ToString(), thread_stats.get_histogram()); - } - for (int i = 0; i < n; i++) { - delete arg[i].thread; - } - delete[] arg; - } + Benchmark(Slice name, pmem::kv::db *kv, int num_threads, const char *engine, BenchmarkLogger &logger) + : kv_(kv), num_(FLAGS_num), value_size_(FLAGS_value_size), key_size_(FLAGS_key_size), + reads_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), + readwrites_(FLAGS_reads < 0 ? FLAGS_num : FLAGS_reads), logger(logger), n(num_threads), + name(name), engine(engine) + { + fprintf(stderr, "running %s \n", name.ToString().c_str()); + bool fresh_db = false; + + if (name == Slice("fillseq")) { + fresh_db = true; + method = &Benchmark::WriteSeq; + } else if (name == Slice("fillrandom")) { + fresh_db = true; + method = &Benchmark::WriteRandom; + } else if (name == Slice("overwrite")) { + method = &Benchmark::WriteRandom; + } else if (name == Slice("readseq")) { + method = &Benchmark::ReadSeq; + } else if (name == Slice("readrandom")) { + method = &Benchmark::ReadRandom; + } else if (name == Slice("readmissing")) { + method = &Benchmark::ReadMissing; + } else if (name == Slice("deleteseq")) { + method = &Benchmark::DeleteSeq; + } else if (name == Slice("deleterandom")) { + method = &Benchmark::DeleteRandom; + } else if (name == Slice("readwhilewriting")) { + ++num_threads; + method = &Benchmark::ReadWhileWriting; + } else if (name == Slice("readrandomwriterandom")) { + method = &Benchmark::ReadRandomWriteRandom; + } else { + throw std::runtime_error("unknown benchmark: " + name.ToString()); + } + PrintHeader(); + + Open(fresh_db, name.ToString()); + } + + ~Benchmark() + { + kv_->close(); + } + + Slice AllocateKey(std::unique_ptr &key_guard) + { + const char *tmp = new char[key_size_]; + key_guard.reset(tmp); + return Slice(key_guard.get(), key_size_); + } + + void GenerateKeyFromInt(uint64_t v, int64_t num_keys, Slice *key, bool missing = false) + { + char *start = const_cast(key->data()); + char *pos = start; + int bytes_to_fill = std::min(key_size_ - static_cast(pos - start), 8); + if (missing) { + int64_t v1 = -v; + memcpy(pos, static_cast(&v1), bytes_to_fill); + } else + memcpy(pos, static_cast(&v), bytes_to_fill); + pos += bytes_to_fill; + if (key_size_ > pos - start) { + memset(pos, '0', key_size_ - (pos - start)); + } + } + + void Run() + { + SharedState shared; + shared.total = n; + shared.num_initialized = 0; + shared.num_done = 0; + shared.start = false; + + ThreadArg *arg = new ThreadArg[n]; + for (int i = 0; i < n; i++) { + arg[i].bm = this; + arg[i].method = method; + arg[i].shared = &shared; + arg[i].thread = new ThreadState(i); + arg[i].thread->shared = &shared; + g_env->StartThread(ThreadBody, &arg[i]); + } + + shared.mu.Lock(); + while (shared.num_initialized < n) { + shared.cv.Wait(); + } + shared.start = true; + shared.cv.SignalAll(); + while (shared.num_done < n) { + shared.cv.Wait(); + } + shared.mu.Unlock(); + + for (int i = 1; i < n; i++) { + arg[0].thread->stats.Merge(arg[i].thread->stats); + } + auto thread_stats = arg[0].thread->stats; + logger.insert(name.ToString(), "micros/op", thread_stats.get_micros_per_op()); + logger.insert(name.ToString(), "ops/sec", thread_stats.get_ops_per_sec()); + logger.insert(name.ToString(), "throughput [MB/s]", thread_stats.get_throughput()); + logger.insert(name.ToString(), "extra_data", thread_stats.get_extra_data()); + if (FLAGS_histogram) { + logger.insert(name.ToString(), thread_stats.get_histogram()); + } + for (int i = 0; i < n; i++) { + delete arg[i].thread; + } + delete[] arg; + } private: - struct ThreadArg { - Benchmark *bm; - SharedState *shared; - ThreadState *thread; - - void (Benchmark::*method)(ThreadState *); - }; - - static void ThreadBody(void *v) { - ThreadArg *arg = reinterpret_cast(v); - SharedState *shared = arg->shared; - ThreadState *thread = arg->thread; - { - MutexLock l(&shared->mu); - shared->num_initialized++; - if (shared->num_initialized >= shared->total) { - shared->cv.SignalAll(); - } - while (!shared->start) { - shared->cv.Wait(); - } - } - - thread->stats.Start(); - (arg->bm->*(arg->method))(thread); - thread->stats.Stop(); - - { - MutexLock l(&shared->mu); - shared->num_done++; - if (shared->num_done >= shared->total) { - shared->cv.SignalAll(); - } - } - } - - - void Open(bool fresh_db, std::string name) { + struct ThreadArg { + Benchmark *bm; + SharedState *shared; + ThreadState *thread; + + void (Benchmark::*method)(ThreadState *); + }; + + static void ThreadBody(void *v) + { + ThreadArg *arg = reinterpret_cast(v); + SharedState *shared = arg->shared; + ThreadState *thread = arg->thread; + { + MutexLock l(&shared->mu); + shared->num_initialized++; + if (shared->num_initialized >= shared->total) { + shared->cv.SignalAll(); + } + while (!shared->start) { + shared->cv.Wait(); + } + } + + thread->stats.Start(); + (arg->bm->*(arg->method))(thread); + thread->stats.Stop(); + + { + MutexLock l(&shared->mu); + shared->num_done++; + if (shared->num_done >= shared->total) { + shared->cv.SignalAll(); + } + } + } + + void Open(bool fresh_db, std::string name) + { assert(kv_ == NULL); auto start = g_env->NowMicros(); auto size = 1024ULL * 1024ULL * 1024ULL * FLAGS_db_size_in_gb; @@ -660,30 +685,29 @@ class Benchmark { if (fresh_db) { cfg_s = cfg.put_uint64("force_create", 1); if (cfg_s != pmem::kv::status::OK) - throw std::runtime_error( - "putting 'force_create' to config failed"); + throw std::runtime_error("putting 'force_create' to config failed"); cfg_s = cfg.put_uint64("size", size); if (cfg_s != pmem::kv::status::OK) - throw std::runtime_error( - "putting 'size' to config failed"); + throw std::runtime_error("putting 'size' to config failed"); if (kv_ != NULL) { delete kv_; - kv_ = NULL; - } + kv_ = NULL; + } if (FLAGS_db_size_in_gb > 0) { auto start = g_env->NowMicros(); std::remove(FLAGS_db); - logger.insert(name, "Remove [millis millis/op]", ((g_env->NowMicros() - start) * 1e-3)); - } + logger.insert(name, "Remove [millis millis/op]", + ((g_env->NowMicros() - start) * 1e-3)); + } } kv_ = new pmem::kv::db; auto s = kv_->open(engine, std::move(cfg)); - if (s != pmem::kv::status::OK) { + if (s != pmem::kv::status::OK) { fprintf(stderr, "Cannot start engine (%s) for path (%s) with %i GB capacity\n%s\n\nUSAGE: %s", engine, FLAGS_db, FLAGS_db_size_in_gb, pmem::kv::errormsg().c_str(), @@ -691,297 +715,310 @@ class Benchmark { exit(-42); } logger.insert(name, "Open [millis/op]", ((g_env->NowMicros() - start) * 1e-3)); - } - - void DoWrite(ThreadState *thread, bool seq) { - if (num_ != FLAGS_num) { - char msg[100]; - snprintf(msg, sizeof(msg), "(%d ops)", num_); - thread->stats.AddMessage(msg); - } - std::unique_ptr key_guard; - Slice key = AllocateKey(key_guard); - - auto num = FLAGS_disjoint ? num_ / FLAGS_threads : num_; - auto start = FLAGS_disjoint ? thread->tid * num_ : 0; - auto end = FLAGS_disjoint ? (thread->tid + 1) * num_ : num_; - - pmem::kv::status s; - int64_t bytes = 0; - for (int i = start; i < end; i++) { - const int k = seq ? i : (thread->rand.Next() % num) + start; - GenerateKeyFromInt(k, FLAGS_num, &key); - std::string value = std::string(); - value.append(value_size_, 'X'); - s = kv_->put(key.ToString(), value); - bytes += value_size_ + key.size(); - thread->stats.FinishedSingleOp(); - if (s != pmem::kv::status::OK) { - fprintf(stdout, "Out of space at key %i\n", i); - exit(1); - } - } - thread->stats.AddBytes(bytes); - } - - void WriteSeq(ThreadState *thread) { - DoWrite(thread, true); - } - - void WriteRandom(ThreadState *thread) { - DoWrite(thread, false); - } - - void DoRead(ThreadState *thread, bool seq, bool missing) { - pmem::kv::status s; - int64_t bytes = 0; - int found = 0; - std::unique_ptr key_guard; - Slice key = AllocateKey(key_guard); - - auto num = FLAGS_disjoint ? reads_ / FLAGS_threads : reads_; - auto start = FLAGS_disjoint ? thread->tid * num : 0; - auto end = FLAGS_disjoint ? (thread->tid + 1) * num : reads_; - - for (int i = start; i < end; i++) { - const int k = seq ? i : (thread->rand.Next() % num) + start; - GenerateKeyFromInt(k, FLAGS_num, &key, missing); - std::string value; - if (kv_->get(key.ToString(), &value) == pmem::kv::status::OK) found++; - thread->stats.FinishedSingleOp(); - bytes += value.length() + key.size(); - } - thread->stats.AddBytes(bytes); - char msg[100]; - snprintf(msg, sizeof(msg), "(%d of %d found by one thread)", found, reads_); - thread->stats.AddMessage(msg); - } - - void ReadSeq(ThreadState *thread) { - DoRead(thread, true, false); - } - - void ReadRandom(ThreadState *thread) { - DoRead(thread, false, false); - } - - void ReadMissing(ThreadState *thread) { - DoRead(thread, false, true); - } - - void DoDelete(ThreadState *thread, bool seq) { - std::unique_ptr key_guard; - Slice key = AllocateKey(key_guard); - for (int i = 0; i < num_; i++) { - const int k = seq ? i : (thread->rand.Next() % FLAGS_num); - GenerateKeyFromInt(k, FLAGS_num, &key); - kv_->remove(key.ToString()); - thread->stats.FinishedSingleOp(); - } - } - - void DeleteSeq(ThreadState *thread) { - DoDelete(thread, true); - } - - void DeleteRandom(ThreadState *thread) { - DoDelete(thread, false); - } - - void BGWriter(ThreadState* thread, enum OperationType write_merge) { - // Special thread that keeps writing until other threads are done. - RandomGenerator gen; - int64_t bytes = 0; - - // Don't merge stats from this thread with the readers. - thread->stats.SetExcludeFromMerge(); - - std::unique_ptr key_guard; - Slice key = AllocateKey(key_guard); - uint32_t written = 0; - bool hint_printed = false; - - while (true) { - { - MutexLock l(&thread->shared->mu); - - if (thread->shared->num_done + 1 >= thread->shared->num_initialized) { - // Finish the write immediately - break; - } - } - - GenerateKeyFromInt(thread->rand.Next() % FLAGS_num, FLAGS_num, &key); - pmem::kv::status s; - - if (write_merge == kWrite) { - s = kv_->put(key.ToString(), gen.Generate(value_size_).ToString()); - } else { - fprintf(stderr, "Merge operation not supported\n"); - exit(1); - } - written++; - - if (s != pmem::kv::status::OK) { - fprintf(stderr, "Put error\n"); - exit(1); - } - bytes += key.size() + value_size_; - } - thread->stats.AddBytes(bytes); - } - - void ReadWhileWriting(ThreadState* thread) { - if (thread->tid > 0) { - ReadRandom(thread); - } else { - BGWriter(thread, kWrite); - } - } - - void ReadRandomWriteRandom(ThreadState* thread) { - RandomGenerator gen; - std::string value; - int64_t found = 0; - int get_weight = 0; - int put_weight = 0; - int64_t reads_done = 0; - int64_t writes_done = 0; - int64_t bytes = 0; - Duration duration(FLAGS_duration, readwrites_); - - std::unique_ptr key_guard; - Slice key = AllocateKey(key_guard); - - // the number of iterations is the larger of read_ or write_ - while (!duration.Done(1)) { - GenerateKeyFromInt(thread->rand.Next() % FLAGS_num, FLAGS_num, &key); - if (get_weight == 0 && put_weight == 0) { - // one batch completed, reinitialize for next batch - get_weight = FLAGS_readwritepercent; - put_weight = 100 - get_weight; - } - if (get_weight > 0) { - value.clear(); - pmem::kv::status s = kv_->get(key.ToString(), &value); - if (s == pmem::kv::status::OK) { - found++; - } else if (s != pmem::kv::status::NOT_FOUND) { - fprintf(stderr, "get error\n"); - } - - bytes += value.length() + key.size(); - get_weight--; - reads_done++; - thread->stats.FinishedSingleOp(); - } else if (put_weight > 0) { - // then do all the corresponding number of puts - // for all the gets we have done earlier - pmem::kv::status s = kv_->put(key.ToString(), gen.Generate(value_size_).ToString()); - if (s != pmem::kv::status::OK) { - fprintf(stderr, "put error\n"); - exit(1); - } - bytes += key.size() + value_size_; - put_weight--; - writes_done++; - thread->stats.FinishedSingleOp(); - } - } - thread->stats.AddBytes(bytes); - char msg[100]; - snprintf(msg, sizeof(msg), "(reads:%" PRIu64 " writes:%" PRIu64 \ - " total:%" PRIu64 " found:%" PRIu64 ")", - reads_done, writes_done, readwrites_, found); - thread->stats.AddMessage(msg); - } + } + + void DoWrite(ThreadState *thread, bool seq) + { + if (num_ != FLAGS_num) { + char msg[100]; + snprintf(msg, sizeof(msg), "(%d ops)", num_); + thread->stats.AddMessage(msg); + } + std::unique_ptr key_guard; + Slice key = AllocateKey(key_guard); + + auto num = FLAGS_disjoint ? num_ / FLAGS_threads : num_; + auto start = FLAGS_disjoint ? thread->tid * num_ : 0; + auto end = FLAGS_disjoint ? (thread->tid + 1) * num_ : num_; + + pmem::kv::status s; + int64_t bytes = 0; + for (int i = start; i < end; i++) { + const int k = seq ? i : (thread->rand.Next() % num) + start; + GenerateKeyFromInt(k, FLAGS_num, &key); + std::string value = std::string(); + value.append(value_size_, 'X'); + s = kv_->put(key.ToString(), value); + bytes += value_size_ + key.size(); + thread->stats.FinishedSingleOp(); + if (s != pmem::kv::status::OK) { + fprintf(stdout, "Out of space at key %i\n", i); + exit(1); + } + } + thread->stats.AddBytes(bytes); + } + + void WriteSeq(ThreadState *thread) + { + DoWrite(thread, true); + } + + void WriteRandom(ThreadState *thread) + { + DoWrite(thread, false); + } + + void DoRead(ThreadState *thread, bool seq, bool missing) + { + pmem::kv::status s; + int64_t bytes = 0; + int found = 0; + std::unique_ptr key_guard; + Slice key = AllocateKey(key_guard); + + auto num = FLAGS_disjoint ? reads_ / FLAGS_threads : reads_; + auto start = FLAGS_disjoint ? thread->tid * num : 0; + auto end = FLAGS_disjoint ? (thread->tid + 1) * num : reads_; + + for (int i = start; i < end; i++) { + const int k = seq ? i : (thread->rand.Next() % num) + start; + GenerateKeyFromInt(k, FLAGS_num, &key, missing); + std::string value; + if (kv_->get(key.ToString(), &value) == pmem::kv::status::OK) + found++; + thread->stats.FinishedSingleOp(); + bytes += value.length() + key.size(); + } + thread->stats.AddBytes(bytes); + char msg[100]; + snprintf(msg, sizeof(msg), "(%d of %d found by one thread)", found, reads_); + thread->stats.AddMessage(msg); + } + + void ReadSeq(ThreadState *thread) + { + DoRead(thread, true, false); + } + + void ReadRandom(ThreadState *thread) + { + DoRead(thread, false, false); + } + + void ReadMissing(ThreadState *thread) + { + DoRead(thread, false, true); + } + + void DoDelete(ThreadState *thread, bool seq) + { + std::unique_ptr key_guard; + Slice key = AllocateKey(key_guard); + for (int i = 0; i < num_; i++) { + const int k = seq ? i : (thread->rand.Next() % FLAGS_num); + GenerateKeyFromInt(k, FLAGS_num, &key); + kv_->remove(key.ToString()); + thread->stats.FinishedSingleOp(); + } + } + + void DeleteSeq(ThreadState *thread) + { + DoDelete(thread, true); + } + + void DeleteRandom(ThreadState *thread) + { + DoDelete(thread, false); + } + + void BGWriter(ThreadState *thread, enum OperationType write_merge) + { + // Special thread that keeps writing until other threads are done. + RandomGenerator gen; + int64_t bytes = 0; + + // Don't merge stats from this thread with the readers. + thread->stats.SetExcludeFromMerge(); + + std::unique_ptr key_guard; + Slice key = AllocateKey(key_guard); + uint32_t written = 0; + bool hint_printed = false; + + while (true) { + { + MutexLock l(&thread->shared->mu); + + if (thread->shared->num_done + 1 >= thread->shared->num_initialized) { + // Finish the write immediately + break; + } + } + + GenerateKeyFromInt(thread->rand.Next() % FLAGS_num, FLAGS_num, &key); + pmem::kv::status s; + + if (write_merge == kWrite) { + s = kv_->put(key.ToString(), gen.Generate(value_size_).ToString()); + } else { + fprintf(stderr, "Merge operation not supported\n"); + exit(1); + } + written++; + + if (s != pmem::kv::status::OK) { + fprintf(stderr, "Put error\n"); + exit(1); + } + bytes += key.size() + value_size_; + } + thread->stats.AddBytes(bytes); + } + + void ReadWhileWriting(ThreadState *thread) + { + if (thread->tid > 0) { + ReadRandom(thread); + } else { + BGWriter(thread, kWrite); + } + } + + void ReadRandomWriteRandom(ThreadState *thread) + { + RandomGenerator gen; + std::string value; + int64_t found = 0; + int get_weight = 0; + int put_weight = 0; + int64_t reads_done = 0; + int64_t writes_done = 0; + int64_t bytes = 0; + Duration duration(FLAGS_duration, readwrites_); + + std::unique_ptr key_guard; + Slice key = AllocateKey(key_guard); + + // the number of iterations is the larger of read_ or write_ + while (!duration.Done(1)) { + GenerateKeyFromInt(thread->rand.Next() % FLAGS_num, FLAGS_num, &key); + if (get_weight == 0 && put_weight == 0) { + // one batch completed, reinitialize for next batch + get_weight = FLAGS_readwritepercent; + put_weight = 100 - get_weight; + } + if (get_weight > 0) { + value.clear(); + pmem::kv::status s = kv_->get(key.ToString(), &value); + if (s == pmem::kv::status::OK) { + found++; + } else if (s != pmem::kv::status::NOT_FOUND) { + fprintf(stderr, "get error\n"); + } + + bytes += value.length() + key.size(); + get_weight--; + reads_done++; + thread->stats.FinishedSingleOp(); + } else if (put_weight > 0) { + // then do all the corresponding number of puts + // for all the gets we have done earlier + pmem::kv::status s = + kv_->put(key.ToString(), gen.Generate(value_size_).ToString()); + if (s != pmem::kv::status::OK) { + fprintf(stderr, "put error\n"); + exit(1); + } + bytes += key.size() + value_size_; + put_weight--; + writes_done++; + thread->stats.FinishedSingleOp(); + } + } + thread->stats.AddBytes(bytes); + char msg[100]; + snprintf(msg, sizeof(msg), + "(reads:%" PRIu64 " writes:%" PRIu64 " total:%" PRIu64 " found:%" PRIu64 ")", + reads_done, writes_done, readwrites_, found); + thread->stats.AddMessage(msg); + } }; -int main(int argc, char **argv) { - // Default list of comma-separated operations to run - static const char *FLAGS_benchmarks = - "fillseq,fillrandom,overwrite,readseq,readrandom,readmissing,deleteseq,deleterandom,readwhilewriting,readrandomwriterandom"; - // Default engine name - static const char *FLAGS_engine = "cmap"; - - // Print usage statement if necessary - if (argc != 1) { - if ((strcmp(argv[1], "?") == 0) || (strcmp(argv[1], "-?") == 0) - || (strcmp(argv[1], "h") == 0) || (strcmp(argv[1], "-h") == 0) - || (strcmp(argv[1], "-help") == 0) || (strcmp(argv[1], "--help") == 0)) { - fprintf(stderr, "%s", USAGE.c_str()); - exit(1); - } - } - // Parse command-line arguments - for (int i = 1; i < argc; i++) { - int n; - char junk; - if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { - FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); - } else if (strncmp(argv[i], "--engine=", 9) == 0) { - FLAGS_engine = argv[i] + 9; - } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && (n == 0 || n == 1)) { - FLAGS_histogram = n; - } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { - FLAGS_num = n; - } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { - FLAGS_reads = n; - } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) { - FLAGS_threads = n; - } else if (sscanf(argv[i], "--key_size=%d%c", &n, &junk) == 1) { - FLAGS_key_size = n; - } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { - FLAGS_value_size = n; - } else if (sscanf(argv[i], "--readwritepercent=%d%c", &n, &junk) == 1) { - FLAGS_readwritepercent = n; - } else if (strncmp(argv[i], "--db=", 5) == 0) { - FLAGS_db = argv[i] + 5; - } else if (sscanf(argv[i], "--db_size_in_gb=%d%c", &n, &junk) == 1) { - FLAGS_db_size_in_gb = n; - } else if (sscanf(argv[i], "--disjoint=%d%c", &n, &junk) == 1 && (n == 0 || n == 1)) { - FLAGS_disjoint = n; - } else { - fprintf(stderr, "Invalid flag '%s'\n", argv[i]); - exit(1); - } - } - - // Run benchmark against default environment - g_env = leveldb::Env::Default(); - - BenchmarkLogger logger = BenchmarkLogger(); - int return_value = 0; - pmem::kv::db *kv = NULL; - const char *benchmarks = FLAGS_benchmarks; - while (benchmarks !=NULL) - { - const char *sep = strchr(benchmarks, ','); - Slice name; - if (sep == NULL) { - name = benchmarks; - benchmarks = NULL; - } else { - name = Slice(benchmarks, sep - benchmarks); - benchmarks = sep + 1; - } - try{ - auto benchmark = Benchmark(name, kv, FLAGS_threads, FLAGS_engine, logger); - benchmark.Run(); - } catch (std::exception &e) - { - std::cerr << e.what() << std::endl; - return_value = 1; - break; - } - } - if (kv !=NULL) { - delete kv; - } - logger.print(); - if (FLAGS_histogram) - { - logger.print_histogram(); - } - return return_value; +int main(int argc, char **argv) +{ + // Default list of comma-separated operations to run + static const char *FLAGS_benchmarks = + "fillseq,fillrandom,overwrite,readseq,readrandom,readmissing,deleteseq,deleterandom,readwhilewriting,readrandomwriterandom"; + // Default engine name + static const char *FLAGS_engine = "cmap"; + + // Print usage statement if necessary + if (argc != 1) { + if ((strcmp(argv[1], "?") == 0) || (strcmp(argv[1], "-?") == 0) || + (strcmp(argv[1], "h") == 0) || (strcmp(argv[1], "-h") == 0) || + (strcmp(argv[1], "-help") == 0) || (strcmp(argv[1], "--help") == 0)) { + fprintf(stderr, "%s", USAGE.c_str()); + exit(1); + } + } + // Parse command-line arguments + for (int i = 1; i < argc; i++) { + int n; + char junk; + if (leveldb::Slice(argv[i]).starts_with("--benchmarks=")) { + FLAGS_benchmarks = argv[i] + strlen("--benchmarks="); + } else if (strncmp(argv[i], "--engine=", 9) == 0) { + FLAGS_engine = argv[i] + 9; + } else if (sscanf(argv[i], "--histogram=%d%c", &n, &junk) == 1 && (n == 0 || n == 1)) { + FLAGS_histogram = n; + } else if (sscanf(argv[i], "--num=%d%c", &n, &junk) == 1) { + FLAGS_num = n; + } else if (sscanf(argv[i], "--reads=%d%c", &n, &junk) == 1) { + FLAGS_reads = n; + } else if (sscanf(argv[i], "--threads=%d%c", &n, &junk) == 1) { + FLAGS_threads = n; + } else if (sscanf(argv[i], "--key_size=%d%c", &n, &junk) == 1) { + FLAGS_key_size = n; + } else if (sscanf(argv[i], "--value_size=%d%c", &n, &junk) == 1) { + FLAGS_value_size = n; + } else if (sscanf(argv[i], "--readwritepercent=%d%c", &n, &junk) == 1) { + FLAGS_readwritepercent = n; + } else if (strncmp(argv[i], "--db=", 5) == 0) { + FLAGS_db = argv[i] + 5; + } else if (sscanf(argv[i], "--db_size_in_gb=%d%c", &n, &junk) == 1) { + FLAGS_db_size_in_gb = n; + } else if (sscanf(argv[i], "--disjoint=%d%c", &n, &junk) == 1 && (n == 0 || n == 1)) { + FLAGS_disjoint = n; + } else { + fprintf(stderr, "Invalid flag '%s'\n", argv[i]); + exit(1); + } + } + + // Run benchmark against default environment + g_env = leveldb::Env::Default(); + + BenchmarkLogger logger = BenchmarkLogger(); + int return_value = 0; + pmem::kv::db *kv = NULL; + const char *benchmarks = FLAGS_benchmarks; + while (benchmarks != NULL) { + const char *sep = strchr(benchmarks, ','); + Slice name; + if (sep == NULL) { + name = benchmarks; + benchmarks = NULL; + } else { + name = Slice(benchmarks, sep - benchmarks); + benchmarks = sep + 1; + } + try { + auto benchmark = Benchmark(name, kv, FLAGS_threads, FLAGS_engine, logger); + benchmark.Run(); + } catch (std::exception &e) { + std::cerr << e.what() << std::endl; + return_value = 1; + break; + } + } + if (kv != NULL) { + delete kv; + } + logger.print(); + if (FLAGS_histogram) { + logger.print_histogram(); + } + return return_value; } diff --git a/bench/include/leveldb/env.h b/bench/include/leveldb/env.h index 2639000..aba7be7 100644 --- a/bench/include/leveldb/env.h +++ b/bench/include/leveldb/env.h @@ -1,7 +1,10 @@ // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. -// + +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020, Intel Corporation + // An Env is an interface used by the leveldb implementation to access // operating system functionality like the filesystem etc. Callers // may wish to provide a custom Env object when opening a database to @@ -13,13 +16,14 @@ #ifndef STORAGE_LEVELDB_INCLUDE_ENV_H_ #define STORAGE_LEVELDB_INCLUDE_ENV_H_ +#include "leveldb/status.h" #include #include #include #include -#include "leveldb/status.h" -namespace leveldb { +namespace leveldb +{ class FileLock; class Logger; @@ -29,325 +33,361 @@ class Slice; class WritableFile; class Env { - public: - Env() { } - virtual ~Env(); - - // Return a default environment suitable for the current operating - // system. Sophisticated users may wish to provide their own Env - // implementation instead of relying on this default environment. - // - // The result of Default() belongs to leveldb and must never be deleted. - static Env* Default(); - - // Create a brand new sequentially-readable file with the specified name. - // On success, stores a pointer to the new file in *result and returns OK. - // On failure stores NULL in *result and returns non-OK. If the file does - // not exist, returns a non-OK status. Implementations should return a - // NotFound status when the file does not exist. - // - // The returned file will only be accessed by one thread at a time. - virtual Status NewSequentialFile(const std::string& fname, - SequentialFile** result) = 0; - - // Create a brand new random access read-only file with the - // specified name. On success, stores a pointer to the new file in - // *result and returns OK. On failure stores NULL in *result and - // returns non-OK. If the file does not exist, returns a non-OK - // status. Implementations should return a NotFound status when the file does - // not exist. - // - // The returned file may be concurrently accessed by multiple threads. - virtual Status NewRandomAccessFile(const std::string& fname, - RandomAccessFile** result) = 0; - - // Create an object that writes to a new file with the specified - // name. Deletes any existing file with the same name and creates a - // new file. On success, stores a pointer to the new file in - // *result and returns OK. On failure stores NULL in *result and - // returns non-OK. - // - // The returned file will only be accessed by one thread at a time. - virtual Status NewWritableFile(const std::string& fname, - WritableFile** result) = 0; - - // Create an object that either appends to an existing file, or - // writes to a new file (if the file does not exist to begin with). - // On success, stores a pointer to the new file in *result and - // returns OK. On failure stores NULL in *result and returns - // non-OK. - // - // The returned file will only be accessed by one thread at a time. - // - // May return an IsNotSupportedError error if this Env does - // not allow appending to an existing file. Users of Env (including - // the leveldb implementation) must be prepared to deal with - // an Env that does not support appending. - virtual Status NewAppendableFile(const std::string& fname, - WritableFile** result); - - // Returns true iff the named file exists. - virtual bool FileExists(const std::string& fname) = 0; - - // Store in *result the names of the children of the specified directory. - // The names are relative to "dir". - // Original contents of *results are dropped. - virtual Status GetChildren(const std::string& dir, - std::vector* result) = 0; - - // Delete the named file. - virtual Status DeleteFile(const std::string& fname) = 0; - - // Create the specified directory. - virtual Status CreateDir(const std::string& dirname) = 0; - - // Delete the specified directory. - virtual Status DeleteDir(const std::string& dirname) = 0; - - // Store the size of fname in *file_size. - virtual Status GetFileSize(const std::string& fname, uint64_t* file_size) = 0; - - // Rename file src to target. - virtual Status RenameFile(const std::string& src, - const std::string& target) = 0; - - // Lock the specified file. Used to prevent concurrent access to - // the same db by multiple processes. On failure, stores NULL in - // *lock and returns non-OK. - // - // On success, stores a pointer to the object that represents the - // acquired lock in *lock and returns OK. The caller should call - // UnlockFile(*lock) to release the lock. If the process exits, - // the lock will be automatically released. - // - // If somebody else already holds the lock, finishes immediately - // with a failure. I.e., this call does not wait for existing locks - // to go away. - // - // May create the named file if it does not already exist. - virtual Status LockFile(const std::string& fname, FileLock** lock) = 0; - - // Release the lock acquired by a previous successful call to LockFile. - // REQUIRES: lock was returned by a successful LockFile() call - // REQUIRES: lock has not already been unlocked. - virtual Status UnlockFile(FileLock* lock) = 0; - - // Arrange to run "(*function)(arg)" once in a background thread. - // - // "function" may run in an unspecified thread. Multiple functions - // added to the same Env may run concurrently in different threads. - // I.e., the caller may not assume that background work items are - // serialized. - virtual void Schedule( - void (*function)(void* arg), - void* arg) = 0; - - // Start a new thread, invoking "function(arg)" within the new thread. - // When "function(arg)" returns, the thread will be destroyed. - virtual void StartThread(void (*function)(void* arg), void* arg) = 0; - - // *path is set to a temporary directory that can be used for testing. It may - // or many not have just been created. The directory may or may not differ - // between runs of the same process, but subsequent calls will return the - // same directory. - virtual Status GetTestDirectory(std::string* path) = 0; - - // Create and return a log file for storing informational messages. - virtual Status NewLogger(const std::string& fname, Logger** result) = 0; - - // Returns the number of micro-seconds since some fixed point in time. Only - // useful for computing deltas of time. - virtual uint64_t NowMicros() = 0; - - // Sleep/delay the thread for the prescribed number of micro-seconds. - virtual void SleepForMicroseconds(int micros) = 0; - - private: - // No copying allowed - Env(const Env&); - void operator=(const Env&); +public: + Env() + { + } + virtual ~Env(); + + // Return a default environment suitable for the current operating + // system. Sophisticated users may wish to provide their own Env + // implementation instead of relying on this default environment. + // + // The result of Default() belongs to leveldb and must never be deleted. + static Env *Default(); + + // Create a brand new sequentially-readable file with the specified name. + // On success, stores a pointer to the new file in *result and returns OK. + // On failure stores NULL in *result and returns non-OK. If the file does + // not exist, returns a non-OK status. Implementations should return a + // NotFound status when the file does not exist. + // + // The returned file will only be accessed by one thread at a time. + virtual Status NewSequentialFile(const std::string &fname, SequentialFile **result) = 0; + + // Create a brand new random access read-only file with the + // specified name. On success, stores a pointer to the new file in + // *result and returns OK. On failure stores NULL in *result and + // returns non-OK. If the file does not exist, returns a non-OK + // status. Implementations should return a NotFound status when the file does + // not exist. + // + // The returned file may be concurrently accessed by multiple threads. + virtual Status NewRandomAccessFile(const std::string &fname, RandomAccessFile **result) = 0; + + // Create an object that writes to a new file with the specified + // name. Deletes any existing file with the same name and creates a + // new file. On success, stores a pointer to the new file in + // *result and returns OK. On failure stores NULL in *result and + // returns non-OK. + // + // The returned file will only be accessed by one thread at a time. + virtual Status NewWritableFile(const std::string &fname, WritableFile **result) = 0; + + // Create an object that either appends to an existing file, or + // writes to a new file (if the file does not exist to begin with). + // On success, stores a pointer to the new file in *result and + // returns OK. On failure stores NULL in *result and returns + // non-OK. + // + // The returned file will only be accessed by one thread at a time. + // + // May return an IsNotSupportedError error if this Env does + // not allow appending to an existing file. Users of Env (including + // the leveldb implementation) must be prepared to deal with + // an Env that does not support appending. + virtual Status NewAppendableFile(const std::string &fname, WritableFile **result); + + // Returns true iff the named file exists. + virtual bool FileExists(const std::string &fname) = 0; + + // Store in *result the names of the children of the specified directory. + // The names are relative to "dir". + // Original contents of *results are dropped. + virtual Status GetChildren(const std::string &dir, std::vector *result) = 0; + + // Delete the named file. + virtual Status DeleteFile(const std::string &fname) = 0; + + // Create the specified directory. + virtual Status CreateDir(const std::string &dirname) = 0; + + // Delete the specified directory. + virtual Status DeleteDir(const std::string &dirname) = 0; + + // Store the size of fname in *file_size. + virtual Status GetFileSize(const std::string &fname, uint64_t *file_size) = 0; + + // Rename file src to target. + virtual Status RenameFile(const std::string &src, const std::string &target) = 0; + + // Lock the specified file. Used to prevent concurrent access to + // the same db by multiple processes. On failure, stores NULL in + // *lock and returns non-OK. + // + // On success, stores a pointer to the object that represents the + // acquired lock in *lock and returns OK. The caller should call + // UnlockFile(*lock) to release the lock. If the process exits, + // the lock will be automatically released. + // + // If somebody else already holds the lock, finishes immediately + // with a failure. I.e., this call does not wait for existing locks + // to go away. + // + // May create the named file if it does not already exist. + virtual Status LockFile(const std::string &fname, FileLock **lock) = 0; + + // Release the lock acquired by a previous successful call to LockFile. + // REQUIRES: lock was returned by a successful LockFile() call + // REQUIRES: lock has not already been unlocked. + virtual Status UnlockFile(FileLock *lock) = 0; + + // Arrange to run "(*function)(arg)" once in a background thread. + // + // "function" may run in an unspecified thread. Multiple functions + // added to the same Env may run concurrently in different threads. + // I.e., the caller may not assume that background work items are + // serialized. + virtual void Schedule(void (*function)(void *arg), void *arg) = 0; + + // Start a new thread, invoking "function(arg)" within the new thread. + // When "function(arg)" returns, the thread will be destroyed. + virtual void StartThread(void (*function)(void *arg), void *arg) = 0; + + // *path is set to a temporary directory that can be used for testing. It may + // or many not have just been created. The directory may or may not differ + // between runs of the same process, but subsequent calls will return the + // same directory. + virtual Status GetTestDirectory(std::string *path) = 0; + + // Create and return a log file for storing informational messages. + virtual Status NewLogger(const std::string &fname, Logger **result) = 0; + + // Returns the number of micro-seconds since some fixed point in time. Only + // useful for computing deltas of time. + virtual uint64_t NowMicros() = 0; + + // Sleep/delay the thread for the prescribed number of micro-seconds. + virtual void SleepForMicroseconds(int micros) = 0; + +private: + // No copying allowed + Env(const Env &); + void operator=(const Env &); }; // A file abstraction for reading sequentially through a file class SequentialFile { - public: - SequentialFile() { } - virtual ~SequentialFile(); - - // Read up to "n" bytes from the file. "scratch[0..n-1]" may be - // written by this routine. Sets "*result" to the data that was - // read (including if fewer than "n" bytes were successfully read). - // May set "*result" to point at data in "scratch[0..n-1]", so - // "scratch[0..n-1]" must be live when "*result" is used. - // If an error was encountered, returns a non-OK status. - // - // REQUIRES: External synchronization - virtual Status Read(size_t n, Slice* result, char* scratch) = 0; - - // Skip "n" bytes from the file. This is guaranteed to be no - // slower that reading the same data, but may be faster. - // - // If end of file is reached, skipping will stop at the end of the - // file, and Skip will return OK. - // - // REQUIRES: External synchronization - virtual Status Skip(uint64_t n) = 0; - - private: - // No copying allowed - SequentialFile(const SequentialFile&); - void operator=(const SequentialFile&); +public: + SequentialFile() + { + } + virtual ~SequentialFile(); + + // Read up to "n" bytes from the file. "scratch[0..n-1]" may be + // written by this routine. Sets "*result" to the data that was + // read (including if fewer than "n" bytes were successfully read). + // May set "*result" to point at data in "scratch[0..n-1]", so + // "scratch[0..n-1]" must be live when "*result" is used. + // If an error was encountered, returns a non-OK status. + // + // REQUIRES: External synchronization + virtual Status Read(size_t n, Slice *result, char *scratch) = 0; + + // Skip "n" bytes from the file. This is guaranteed to be no + // slower that reading the same data, but may be faster. + // + // If end of file is reached, skipping will stop at the end of the + // file, and Skip will return OK. + // + // REQUIRES: External synchronization + virtual Status Skip(uint64_t n) = 0; + +private: + // No copying allowed + SequentialFile(const SequentialFile &); + void operator=(const SequentialFile &); }; // A file abstraction for randomly reading the contents of a file. class RandomAccessFile { - public: - RandomAccessFile() { } - virtual ~RandomAccessFile(); - - // Read up to "n" bytes from the file starting at "offset". - // "scratch[0..n-1]" may be written by this routine. Sets "*result" - // to the data that was read (including if fewer than "n" bytes were - // successfully read). May set "*result" to point at data in - // "scratch[0..n-1]", so "scratch[0..n-1]" must be live when - // "*result" is used. If an error was encountered, returns a non-OK - // status. - // - // Safe for concurrent use by multiple threads. - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const = 0; - - private: - // No copying allowed - RandomAccessFile(const RandomAccessFile&); - void operator=(const RandomAccessFile&); +public: + RandomAccessFile() + { + } + virtual ~RandomAccessFile(); + + // Read up to "n" bytes from the file starting at "offset". + // "scratch[0..n-1]" may be written by this routine. Sets "*result" + // to the data that was read (including if fewer than "n" bytes were + // successfully read). May set "*result" to point at data in + // "scratch[0..n-1]", so "scratch[0..n-1]" must be live when + // "*result" is used. If an error was encountered, returns a non-OK + // status. + // + // Safe for concurrent use by multiple threads. + virtual Status Read(uint64_t offset, size_t n, Slice *result, char *scratch) const = 0; + +private: + // No copying allowed + RandomAccessFile(const RandomAccessFile &); + void operator=(const RandomAccessFile &); }; // A file abstraction for sequential writing. The implementation // must provide buffering since callers may append small fragments // at a time to the file. class WritableFile { - public: - WritableFile() { } - virtual ~WritableFile(); - - virtual Status Append(const Slice& data) = 0; - virtual Status Close() = 0; - virtual Status Flush() = 0; - virtual Status Sync() = 0; - - private: - // No copying allowed - WritableFile(const WritableFile&); - void operator=(const WritableFile&); +public: + WritableFile() + { + } + virtual ~WritableFile(); + + virtual Status Append(const Slice &data) = 0; + virtual Status Close() = 0; + virtual Status Flush() = 0; + virtual Status Sync() = 0; + +private: + // No copying allowed + WritableFile(const WritableFile &); + void operator=(const WritableFile &); }; // An interface for writing log messages. class Logger { - public: - Logger() { } - virtual ~Logger(); - - // Write an entry to the log file with the specified format. - virtual void Logv(const char* format, va_list ap) = 0; - - private: - // No copying allowed - Logger(const Logger&); - void operator=(const Logger&); +public: + Logger() + { + } + virtual ~Logger(); + + // Write an entry to the log file with the specified format. + virtual void Logv(const char *format, va_list ap) = 0; + +private: + // No copying allowed + Logger(const Logger &); + void operator=(const Logger &); }; - // Identifies a locked file. class FileLock { - public: - FileLock() { } - virtual ~FileLock(); - private: - // No copying allowed - FileLock(const FileLock&); - void operator=(const FileLock&); +public: + FileLock() + { + } + virtual ~FileLock(); + +private: + // No copying allowed + FileLock(const FileLock &); + void operator=(const FileLock &); }; // Log the specified data to *info_log if info_log is non-NULL. -extern void Log(Logger* info_log, const char* format, ...) -# if defined(__GNUC__) || defined(__clang__) - __attribute__((__format__ (__printf__, 2, 3))) -# endif - ; +extern void Log(Logger *info_log, const char *format, ...) +#if defined(__GNUC__) || defined(__clang__) + __attribute__((__format__(__printf__, 2, 3))) +#endif + ; // A utility routine: write "data" to the named file. -Status WriteStringToFile(Env* env, const Slice& data, - const std::string& fname); +Status WriteStringToFile(Env *env, const Slice &data, const std::string &fname); // A utility routine: read contents of named file into *data -Status ReadFileToString(Env* env, const std::string& fname, - std::string* data); +Status ReadFileToString(Env *env, const std::string &fname, std::string *data); // An implementation of Env that forwards all calls to another Env. // May be useful to clients who wish to override just part of the // functionality of another Env. class EnvWrapper : public Env { - public: - // Initialize an EnvWrapper that delegates all calls to *t - explicit EnvWrapper(Env* t) : target_(t) { } - virtual ~EnvWrapper(); - - // Return the target to which this Env forwards all calls - Env* target() const { return target_; } - - // The following text is boilerplate that forwards all methods to target() - Status NewSequentialFile(const std::string& f, SequentialFile** r) { - return target_->NewSequentialFile(f, r); - } - Status NewRandomAccessFile(const std::string& f, RandomAccessFile** r) { - return target_->NewRandomAccessFile(f, r); - } - Status NewWritableFile(const std::string& f, WritableFile** r) { - return target_->NewWritableFile(f, r); - } - Status NewAppendableFile(const std::string& f, WritableFile** r) { - return target_->NewAppendableFile(f, r); - } - bool FileExists(const std::string& f) { return target_->FileExists(f); } - Status GetChildren(const std::string& dir, std::vector* r) { - return target_->GetChildren(dir, r); - } - Status DeleteFile(const std::string& f) { return target_->DeleteFile(f); } - Status CreateDir(const std::string& d) { return target_->CreateDir(d); } - Status DeleteDir(const std::string& d) { return target_->DeleteDir(d); } - Status GetFileSize(const std::string& f, uint64_t* s) { - return target_->GetFileSize(f, s); - } - Status RenameFile(const std::string& s, const std::string& t) { - return target_->RenameFile(s, t); - } - Status LockFile(const std::string& f, FileLock** l) { - return target_->LockFile(f, l); - } - Status UnlockFile(FileLock* l) { return target_->UnlockFile(l); } - void Schedule(void (*f)(void*), void* a) { - return target_->Schedule(f, a); - } - void StartThread(void (*f)(void*), void* a) { - return target_->StartThread(f, a); - } - virtual Status GetTestDirectory(std::string* path) { - return target_->GetTestDirectory(path); - } - virtual Status NewLogger(const std::string& fname, Logger** result) { - return target_->NewLogger(fname, result); - } - uint64_t NowMicros() { - return target_->NowMicros(); - } - void SleepForMicroseconds(int micros) { - target_->SleepForMicroseconds(micros); - } - private: - Env* target_; +public: + // Initialize an EnvWrapper that delegates all calls to *t + explicit EnvWrapper(Env *t) : target_(t) + { + } + virtual ~EnvWrapper(); + + // Return the target to which this Env forwards all calls + Env *target() const + { + return target_; + } + + // The following text is boilerplate that forwards all methods to target() + Status NewSequentialFile(const std::string &f, SequentialFile **r) + { + return target_->NewSequentialFile(f, r); + } + Status NewRandomAccessFile(const std::string &f, RandomAccessFile **r) + { + return target_->NewRandomAccessFile(f, r); + } + Status NewWritableFile(const std::string &f, WritableFile **r) + { + return target_->NewWritableFile(f, r); + } + Status NewAppendableFile(const std::string &f, WritableFile **r) + { + return target_->NewAppendableFile(f, r); + } + bool FileExists(const std::string &f) + { + return target_->FileExists(f); + } + Status GetChildren(const std::string &dir, std::vector *r) + { + return target_->GetChildren(dir, r); + } + Status DeleteFile(const std::string &f) + { + return target_->DeleteFile(f); + } + Status CreateDir(const std::string &d) + { + return target_->CreateDir(d); + } + Status DeleteDir(const std::string &d) + { + return target_->DeleteDir(d); + } + Status GetFileSize(const std::string &f, uint64_t *s) + { + return target_->GetFileSize(f, s); + } + Status RenameFile(const std::string &s, const std::string &t) + { + return target_->RenameFile(s, t); + } + Status LockFile(const std::string &f, FileLock **l) + { + return target_->LockFile(f, l); + } + Status UnlockFile(FileLock *l) + { + return target_->UnlockFile(l); + } + void Schedule(void (*f)(void *), void *a) + { + return target_->Schedule(f, a); + } + void StartThread(void (*f)(void *), void *a) + { + return target_->StartThread(f, a); + } + virtual Status GetTestDirectory(std::string *path) + { + return target_->GetTestDirectory(path); + } + virtual Status NewLogger(const std::string &fname, Logger **result) + { + return target_->NewLogger(fname, result); + } + uint64_t NowMicros() + { + return target_->NowMicros(); + } + void SleepForMicroseconds(int micros) + { + target_->SleepForMicroseconds(micros); + } + +private: + Env *target_; }; -} // namespace leveldb +} // namespace leveldb -#endif // STORAGE_LEVELDB_INCLUDE_ENV_H_ +#endif // STORAGE_LEVELDB_INCLUDE_ENV_H_ diff --git a/bench/include/leveldb/slice.h b/bench/include/leveldb/slice.h index 1c59f40..dca0617 100644 --- a/bench/include/leveldb/slice.h +++ b/bench/include/leveldb/slice.h @@ -1,7 +1,10 @@ // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. -// + +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020, Intel Corporation + // Slice is a simple structure containing a pointer into some external // storage and a size. The user of a Slice must ensure that the slice // is not used after the corresponding external storage has been @@ -20,90 +23,120 @@ #include #include -namespace leveldb { +namespace leveldb +{ class Slice { - public: - // Create an empty slice. - Slice() : data_(""), size_(0) { } - - // Create a slice that refers to d[0,n-1]. - Slice(const char* d, size_t n) : data_(d), size_(n) { } - - // Create a slice that refers to the contents of "s" - Slice(const std::string& s) : data_(s.data()), size_(s.size()) { } - - // Create a slice that refers to s[0,strlen(s)-1] - Slice(const char* s) : data_(s), size_(strlen(s)) { } - - // Return a pointer to the beginning of the referenced data - const char* data() const { return data_; } - - // Return the length (in bytes) of the referenced data - size_t size() const { return size_; } - - // Return true iff the length of the referenced data is zero - bool empty() const { return size_ == 0; } - - // Return the ith byte in the referenced data. - // REQUIRES: n < size() - char operator[](size_t n) const { - assert(n < size()); - return data_[n]; - } - - // Change this slice to refer to an empty array - void clear() { data_ = ""; size_ = 0; } - - // Drop the first "n" bytes from this slice. - void remove_prefix(size_t n) { - assert(n <= size()); - data_ += n; - size_ -= n; - } - - // Return a string that contains the copy of the referenced data. - std::string ToString() const { return std::string(data_, size_); } - - // Three-way comparison. Returns value: - // < 0 iff "*this" < "b", - // == 0 iff "*this" == "b", - // > 0 iff "*this" > "b" - int compare(const Slice& b) const; - - // Return true iff "x" is a prefix of "*this" - bool starts_with(const Slice& x) const { - return ((size_ >= x.size_) && - (memcmp(data_, x.data_, x.size_) == 0)); - } - - private: - const char* data_; - size_t size_; - - // Intentionally copyable +public: + // Create an empty slice. + Slice() : data_(""), size_(0) + { + } + + // Create a slice that refers to d[0,n-1]. + Slice(const char *d, size_t n) : data_(d), size_(n) + { + } + + // Create a slice that refers to the contents of "s" + Slice(const std::string &s) : data_(s.data()), size_(s.size()) + { + } + + // Create a slice that refers to s[0,strlen(s)-1] + Slice(const char *s) : data_(s), size_(strlen(s)) + { + } + + // Return a pointer to the beginning of the referenced data + const char *data() const + { + return data_; + } + + // Return the length (in bytes) of the referenced data + size_t size() const + { + return size_; + } + + // Return true iff the length of the referenced data is zero + bool empty() const + { + return size_ == 0; + } + + // Return the ith byte in the referenced data. + // REQUIRES: n < size() + char operator[](size_t n) const + { + assert(n < size()); + return data_[n]; + } + + // Change this slice to refer to an empty array + void clear() + { + data_ = ""; + size_ = 0; + } + + // Drop the first "n" bytes from this slice. + void remove_prefix(size_t n) + { + assert(n <= size()); + data_ += n; + size_ -= n; + } + + // Return a string that contains the copy of the referenced data. + std::string ToString() const + { + return std::string(data_, size_); + } + + // Three-way comparison. Returns value: + // < 0 iff "*this" < "b", + // == 0 iff "*this" == "b", + // > 0 iff "*this" > "b" + int compare(const Slice &b) const; + + // Return true iff "x" is a prefix of "*this" + bool starts_with(const Slice &x) const + { + return ((size_ >= x.size_) && (memcmp(data_, x.data_, x.size_) == 0)); + } + +private: + const char *data_; + size_t size_; + + // Intentionally copyable }; -inline bool operator==(const Slice& x, const Slice& y) { - return ((x.size() == y.size()) && - (memcmp(x.data(), y.data(), x.size()) == 0)); +inline bool operator==(const Slice &x, const Slice &y) +{ + return ((x.size() == y.size()) && (memcmp(x.data(), y.data(), x.size()) == 0)); } -inline bool operator!=(const Slice& x, const Slice& y) { - return !(x == y); +inline bool operator!=(const Slice &x, const Slice &y) +{ + return !(x == y); } -inline int Slice::compare(const Slice& b) const { - const size_t min_len = (size_ < b.size_) ? size_ : b.size_; - int r = memcmp(data_, b.data_, min_len); - if (r == 0) { - if (size_ < b.size_) r = -1; - else if (size_ > b.size_) r = +1; - } - return r; +inline int Slice::compare(const Slice &b) const +{ + const size_t min_len = (size_ < b.size_) ? size_ : b.size_; + int r = memcmp(data_, b.data_, min_len); + if (r == 0) { + if (size_ < b.size_) + r = -1; + else if (size_ > b.size_) + r = +1; + } + return r; } -} // namespace leveldb - +} // namespace leveldb -#endif // STORAGE_LEVELDB_INCLUDE_SLICE_H_ +#endif // STORAGE_LEVELDB_INCLUDE_SLICE_H_ diff --git a/bench/include/leveldb/status.h b/bench/include/leveldb/status.h index 9a9b46f..40f4693 100644 --- a/bench/include/leveldb/status.h +++ b/bench/include/leveldb/status.h @@ -1,7 +1,10 @@ // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. -// + +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020, Intel Corporation + // A Status encapsulates the result of an operation. It may indicate success, // or it may indicate an error with an associated error message. // @@ -13,100 +16,135 @@ #ifndef STORAGE_LEVELDB_INCLUDE_STATUS_H_ #define STORAGE_LEVELDB_INCLUDE_STATUS_H_ -#include #include "leveldb/slice.h" +#include -namespace leveldb { +namespace leveldb +{ class Status { - public: - // Create a success status. - Status() : state_(NULL) { } - ~Status() { delete[] state_; } - - // Copy the specified status. - Status(const Status& s); - void operator=(const Status& s); - - // Return a success status. - static Status OK() { return Status(); } - - // Return error status of an appropriate type. - static Status NotFound(const Slice& msg, const Slice& msg2 = Slice()) { - return Status(kNotFound, msg, msg2); - } - static Status Corruption(const Slice& msg, const Slice& msg2 = Slice()) { - return Status(kCorruption, msg, msg2); - } - static Status NotSupported(const Slice& msg, const Slice& msg2 = Slice()) { - return Status(kNotSupported, msg, msg2); - } - static Status InvalidArgument(const Slice& msg, const Slice& msg2 = Slice()) { - return Status(kInvalidArgument, msg, msg2); - } - static Status IOError(const Slice& msg, const Slice& msg2 = Slice()) { - return Status(kIOError, msg, msg2); - } - - // Returns true iff the status indicates success. - bool ok() const { return (state_ == NULL); } - - // Returns true iff the status indicates a NotFound error. - bool IsNotFound() const { return code() == kNotFound; } - - // Returns true iff the status indicates a Corruption error. - bool IsCorruption() const { return code() == kCorruption; } - - // Returns true iff the status indicates an IOError. - bool IsIOError() const { return code() == kIOError; } - - // Returns true iff the status indicates a NotSupportedError. - bool IsNotSupportedError() const { return code() == kNotSupported; } - - // Returns true iff the status indicates an InvalidArgument. - bool IsInvalidArgument() const { return code() == kInvalidArgument; } - - // Return a string representation of this status suitable for printing. - // Returns the string "OK" for success. - std::string ToString() const; - - private: - // OK status has a NULL state_. Otherwise, state_ is a new[] array - // of the following form: - // state_[0..3] == length of message - // state_[4] == code - // state_[5..] == message - const char* state_; - - enum Code { - kOk = 0, - kNotFound = 1, - kCorruption = 2, - kNotSupported = 3, - kInvalidArgument = 4, - kIOError = 5 - }; - - Code code() const { - return (state_ == NULL) ? kOk : static_cast(state_[4]); - } - - Status(Code code, const Slice& msg, const Slice& msg2); - static const char* CopyState(const char* s); +public: + // Create a success status. + Status() : state_(NULL) + { + } + ~Status() + { + delete[] state_; + } + + // Copy the specified status. + Status(const Status &s); + void operator=(const Status &s); + + // Return a success status. + static Status OK() + { + return Status(); + } + + // Return error status of an appropriate type. + static Status NotFound(const Slice &msg, const Slice &msg2 = Slice()) + { + return Status(kNotFound, msg, msg2); + } + static Status Corruption(const Slice &msg, const Slice &msg2 = Slice()) + { + return Status(kCorruption, msg, msg2); + } + static Status NotSupported(const Slice &msg, const Slice &msg2 = Slice()) + { + return Status(kNotSupported, msg, msg2); + } + static Status InvalidArgument(const Slice &msg, const Slice &msg2 = Slice()) + { + return Status(kInvalidArgument, msg, msg2); + } + static Status IOError(const Slice &msg, const Slice &msg2 = Slice()) + { + return Status(kIOError, msg, msg2); + } + + // Returns true iff the status indicates success. + bool ok() const + { + return (state_ == NULL); + } + + // Returns true iff the status indicates a NotFound error. + bool IsNotFound() const + { + return code() == kNotFound; + } + + // Returns true iff the status indicates a Corruption error. + bool IsCorruption() const + { + return code() == kCorruption; + } + + // Returns true iff the status indicates an IOError. + bool IsIOError() const + { + return code() == kIOError; + } + + // Returns true iff the status indicates a NotSupportedError. + bool IsNotSupportedError() const + { + return code() == kNotSupported; + } + + // Returns true iff the status indicates an InvalidArgument. + bool IsInvalidArgument() const + { + return code() == kInvalidArgument; + } + + // Return a string representation of this status suitable for printing. + // Returns the string "OK" for success. + std::string ToString() const; + +private: + // OK status has a NULL state_. Otherwise, state_ is a new[] array + // of the following form: + // state_[0..3] == length of message + // state_[4] == code + // state_[5..] == message + const char *state_; + + enum Code { + kOk = 0, + kNotFound = 1, + kCorruption = 2, + kNotSupported = 3, + kInvalidArgument = 4, + kIOError = 5 + }; + + Code code() const + { + return (state_ == NULL) ? kOk : static_cast(state_[4]); + } + + Status(Code code, const Slice &msg, const Slice &msg2); + static const char *CopyState(const char *s); }; -inline Status::Status(const Status& s) { - state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); +inline Status::Status(const Status &s) +{ + state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); } -inline void Status::operator=(const Status& s) { - // The following condition catches both aliasing (when this == &s), - // and the common case where both s and *this are ok. - if (state_ != s.state_) { - delete[] state_; - state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); - } +inline void Status::operator=(const Status &s) +{ + // The following condition catches both aliasing (when this == &s), + // and the common case where both s and *this are ok. + if (state_ != s.state_) { + delete[] state_; + state_ = (s.state_ == NULL) ? NULL : CopyState(s.state_); + } } -} // namespace leveldb +} // namespace leveldb -#endif // STORAGE_LEVELDB_INCLUDE_STATUS_H_ +#endif // STORAGE_LEVELDB_INCLUDE_STATUS_H_ diff --git a/bench/port/atomic_pointer.h b/bench/port/atomic_pointer.h index a5dd9e8..1d20550 100644 --- a/bench/port/atomic_pointer.h +++ b/bench/port/atomic_pointer.h @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020, Intel Corporation + // AtomicPointer provides storage for a lock-free pointer. // Platform-dependent implementation of AtomicPointer: // - If the platform provides a cheap barrier, we use it with raw pointers @@ -43,8 +46,10 @@ #define ARCH_CPU_MIPS_FAMILY 1 #endif -namespace leveldb { -namespace port { +namespace leveldb +{ +namespace port +{ // Define MemoryBarrier() if available // Windows on x86 @@ -55,26 +60,29 @@ namespace port { // Mac OS #elif defined(__APPLE__) -inline void MemoryBarrier() { - OSMemoryBarrier(); +inline void MemoryBarrier() +{ + OSMemoryBarrier(); } #define LEVELDB_HAVE_MEMORY_BARRIER // Gcc on x86 #elif defined(ARCH_CPU_X86_FAMILY) && defined(__GNUC__) -inline void MemoryBarrier() { - // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on - // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. - __asm__ __volatile__("" : : : "memory"); +inline void MemoryBarrier() +{ + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + __asm__ __volatile__("" : : : "memory"); } #define LEVELDB_HAVE_MEMORY_BARRIER // Sun Studio #elif defined(ARCH_CPU_X86_FAMILY) && defined(__SUNPRO_CC) -inline void MemoryBarrier() { - // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on - // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. - asm volatile("" : : : "memory"); +inline void MemoryBarrier() +{ + // See http://gcc.gnu.org/ml/gcc/2003-04/msg01180.html for a discussion on + // this idiom. Also see http://en.wikipedia.org/wiki/Memory_ordering. + asm volatile("" : : : "memory"); } #define LEVELDB_HAVE_MEMORY_BARRIER @@ -91,31 +99,35 @@ typedef void (*LinuxKernelMemoryBarrierFunc)(void); // shows that the extra function call cost is completely negligible on // multi-core devices. // -inline void MemoryBarrier() { - (*(LinuxKernelMemoryBarrierFunc)0xffff0fa0)(); +inline void MemoryBarrier() +{ + (*(LinuxKernelMemoryBarrierFunc)0xffff0fa0)(); } #define LEVELDB_HAVE_MEMORY_BARRIER // ARM64 #elif defined(ARCH_CPU_ARM64_FAMILY) -inline void MemoryBarrier() { - asm volatile("dmb sy" : : : "memory"); +inline void MemoryBarrier() +{ + asm volatile("dmb sy" : : : "memory"); } #define LEVELDB_HAVE_MEMORY_BARRIER // PPC #elif defined(ARCH_CPU_PPC_FAMILY) && defined(__GNUC__) -inline void MemoryBarrier() { - // TODO for some powerpc expert: is there a cheaper suitable variant? - // Perhaps by having separate barriers for acquire and release ops. - asm volatile("sync" : : : "memory"); +inline void MemoryBarrier() +{ + // TODO for some powerpc expert: is there a cheaper suitable variant? + // Perhaps by having separate barriers for acquire and release ops. + asm volatile("sync" : : : "memory"); } #define LEVELDB_HAVE_MEMORY_BARRIER // MIPS #elif defined(ARCH_CPU_MIPS_FAMILY) && defined(__GNUC__) -inline void MemoryBarrier() { - __asm__ __volatile__("sync" : : : "memory"); +inline void MemoryBarrier() +{ + __asm__ __volatile__("sync" : : : "memory"); } #define LEVELDB_HAVE_MEMORY_BARRIER @@ -124,104 +136,146 @@ inline void MemoryBarrier() { // AtomicPointer built using platform-specific MemoryBarrier() #if defined(LEVELDB_HAVE_MEMORY_BARRIER) class AtomicPointer { - private: - void* rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* p) : rep_(p) {} - inline void* NoBarrier_Load() const { return rep_; } - inline void NoBarrier_Store(void* v) { rep_ = v; } - inline void* Acquire_Load() const { - void* result = rep_; - MemoryBarrier(); - return result; - } - inline void Release_Store(void* v) { - MemoryBarrier(); - rep_ = v; - } +private: + void *rep_; + +public: + AtomicPointer() + { + } + explicit AtomicPointer(void *p) : rep_(p) + { + } + inline void *NoBarrier_Load() const + { + return rep_; + } + inline void NoBarrier_Store(void *v) + { + rep_ = v; + } + inline void *Acquire_Load() const + { + void *result = rep_; + MemoryBarrier(); + return result; + } + inline void Release_Store(void *v) + { + MemoryBarrier(); + rep_ = v; + } }; // AtomicPointer based on #elif defined(LEVELDB_ATOMIC_PRESENT) class AtomicPointer { - private: - std::atomic rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* v) : rep_(v) { } - inline void* Acquire_Load() const { - return rep_.load(std::memory_order_acquire); - } - inline void Release_Store(void* v) { - rep_.store(v, std::memory_order_release); - } - inline void* NoBarrier_Load() const { - return rep_.load(std::memory_order_relaxed); - } - inline void NoBarrier_Store(void* v) { - rep_.store(v, std::memory_order_relaxed); - } +private: + std::atomic rep_; + +public: + AtomicPointer() + { + } + explicit AtomicPointer(void *v) : rep_(v) + { + } + inline void *Acquire_Load() const + { + return rep_.load(std::memory_order_acquire); + } + inline void Release_Store(void *v) + { + rep_.store(v, std::memory_order_release); + } + inline void *NoBarrier_Load() const + { + return rep_.load(std::memory_order_relaxed); + } + inline void NoBarrier_Store(void *v) + { + rep_.store(v, std::memory_order_relaxed); + } }; // Atomic pointer based on sparc memory barriers #elif defined(__sparcv9) && defined(__GNUC__) class AtomicPointer { - private: - void* rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* v) : rep_(v) { } - inline void* Acquire_Load() const { - void* val; - __asm__ __volatile__ ( - "ldx [%[rep_]], %[val] \n\t" - "membar #LoadLoad|#LoadStore \n\t" - : [val] "=r" (val) - : [rep_] "r" (&rep_) - : "memory"); - return val; - } - inline void Release_Store(void* v) { - __asm__ __volatile__ ( - "membar #LoadStore|#StoreStore \n\t" - "stx %[v], [%[rep_]] \n\t" - : - : [rep_] "r" (&rep_), [v] "r" (v) - : "memory"); - } - inline void* NoBarrier_Load() const { return rep_; } - inline void NoBarrier_Store(void* v) { rep_ = v; } +private: + void *rep_; + +public: + AtomicPointer() + { + } + explicit AtomicPointer(void *v) : rep_(v) + { + } + inline void *Acquire_Load() const + { + void *val; + __asm__ __volatile__("ldx [%[rep_]], %[val] \n\t" + "membar #LoadLoad|#LoadStore \n\t" + : [val] "=r"(val) + : [rep_] "r"(&rep_) + : "memory"); + return val; + } + inline void Release_Store(void *v) + { + __asm__ __volatile__("membar #LoadStore|#StoreStore \n\t" + "stx %[v], [%[rep_]] \n\t" + : + : [rep_] "r"(&rep_), [v] "r"(v) + : "memory"); + } + inline void *NoBarrier_Load() const + { + return rep_; + } + inline void NoBarrier_Store(void *v) + { + rep_ = v; + } }; // Atomic pointer based on ia64 acq/rel #elif defined(__ia64) && defined(__GNUC__) class AtomicPointer { - private: - void* rep_; - public: - AtomicPointer() { } - explicit AtomicPointer(void* v) : rep_(v) { } - inline void* Acquire_Load() const { - void* val ; - __asm__ __volatile__ ( - "ld8.acq %[val] = [%[rep_]] \n\t" - : [val] "=r" (val) - : [rep_] "r" (&rep_) - : "memory" - ); - return val; - } - inline void Release_Store(void* v) { - __asm__ __volatile__ ( - "st8.rel [%[rep_]] = %[v] \n\t" - : - : [rep_] "r" (&rep_), [v] "r" (v) - : "memory" - ); - } - inline void* NoBarrier_Load() const { return rep_; } - inline void NoBarrier_Store(void* v) { rep_ = v; } +private: + void *rep_; + +public: + AtomicPointer() + { + } + explicit AtomicPointer(void *v) : rep_(v) + { + } + inline void *Acquire_Load() const + { + void *val; + __asm__ __volatile__("ld8.acq %[val] = [%[rep_]] \n\t" + : [val] "=r"(val) + : [rep_] "r"(&rep_) + : "memory"); + return val; + } + inline void Release_Store(void *v) + { + __asm__ __volatile__("st8.rel [%[rep_]] = %[v] \n\t" + : + : [rep_] "r"(&rep_), [v] "r"(v) + : "memory"); + } + inline void *NoBarrier_Load() const + { + return rep_; + } + inline void NoBarrier_Store(void *v) + { + rep_ = v; + } }; // We have neither MemoryBarrier(), nor @@ -236,7 +290,7 @@ class AtomicPointer { #undef ARCH_CPU_ARM64_FAMILY #undef ARCH_CPU_PPC_FAMILY -} // namespace port -} // namespace leveldb +} // namespace port +} // namespace leveldb -#endif // PORT_ATOMIC_POINTER_H_ +#endif // PORT_ATOMIC_POINTER_H_ diff --git a/bench/port/port_posix.cc b/bench/port/port_posix.cc index 29206b2..bfe4579 100644 --- a/bench/port/port_posix.cc +++ b/bench/port/port_posix.cc @@ -2,52 +2,77 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2020, Intel Corporation */ + #include "port/port_posix.h" #include #include #include -namespace leveldb { -namespace port { +namespace leveldb +{ +namespace port +{ -static void PthreadCall(const char* label, int result) { - if (result != 0) { - fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); - abort(); - } +static void PthreadCall(const char *label, int result) +{ + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + abort(); + } } -Mutex::Mutex() { PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL)); } +Mutex::Mutex() +{ + PthreadCall("init mutex", pthread_mutex_init(&mu_, NULL)); +} -Mutex::~Mutex() { PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); } +Mutex::~Mutex() +{ + PthreadCall("destroy mutex", pthread_mutex_destroy(&mu_)); +} -void Mutex::Lock() { PthreadCall("lock", pthread_mutex_lock(&mu_)); } +void Mutex::Lock() +{ + PthreadCall("lock", pthread_mutex_lock(&mu_)); +} -void Mutex::Unlock() { PthreadCall("unlock", pthread_mutex_unlock(&mu_)); } +void Mutex::Unlock() +{ + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); +} -CondVar::CondVar(Mutex* mu) - : mu_(mu) { - PthreadCall("init cv", pthread_cond_init(&cv_, NULL)); +CondVar::CondVar(Mutex *mu) : mu_(mu) +{ + PthreadCall("init cv", pthread_cond_init(&cv_, NULL)); } -CondVar::~CondVar() { PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); } +CondVar::~CondVar() +{ + PthreadCall("destroy cv", pthread_cond_destroy(&cv_)); +} -void CondVar::Wait() { - PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_)); +void CondVar::Wait() +{ + PthreadCall("wait", pthread_cond_wait(&cv_, &mu_->mu_)); } -void CondVar::Signal() { - PthreadCall("signal", pthread_cond_signal(&cv_)); +void CondVar::Signal() +{ + PthreadCall("signal", pthread_cond_signal(&cv_)); } -void CondVar::SignalAll() { - PthreadCall("broadcast", pthread_cond_broadcast(&cv_)); +void CondVar::SignalAll() +{ + PthreadCall("broadcast", pthread_cond_broadcast(&cv_)); } -void InitOnce(OnceType* once, void (*initializer)()) { - PthreadCall("once", pthread_once(once, initializer)); +void InitOnce(OnceType *once, void (*initializer)()) +{ + PthreadCall("once", pthread_once(once, initializer)); } -} // namespace port -} // namespace leveldb +} // namespace port +} // namespace leveldb diff --git a/bench/port/port_posix.h b/bench/port/port_posix.h index 632d046..28e6e41 100644 --- a/bench/port/port_posix.h +++ b/bench/port/port_posix.h @@ -1,7 +1,10 @@ // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. -// + +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020, Intel Corporation + // See port_example.h for documentation for the following types/functions. #ifndef STORAGE_LEVELDB_PORT_PORT_POSIX_H_ @@ -9,52 +12,49 @@ #undef PLATFORM_IS_LITTLE_ENDIAN #if defined(__APPLE__) - #include - #if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER) - #define PLATFORM_IS_LITTLE_ENDIAN \ - (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN) - #endif +#include +#if defined(__DARWIN_LITTLE_ENDIAN) && defined(__DARWIN_BYTE_ORDER) +#define PLATFORM_IS_LITTLE_ENDIAN (__DARWIN_BYTE_ORDER == __DARWIN_LITTLE_ENDIAN) +#endif #elif defined(OS_SOLARIS) - #include - #ifdef _LITTLE_ENDIAN - #define PLATFORM_IS_LITTLE_ENDIAN true - #else - #define PLATFORM_IS_LITTLE_ENDIAN false - #endif -#elif defined(OS_FREEBSD) || defined(OS_OPENBSD) ||\ - defined(OS_NETBSD) || defined(OS_DRAGONFLYBSD) - #include - #include - #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) +#include +#ifdef _LITTLE_ENDIAN +#define PLATFORM_IS_LITTLE_ENDIAN true +#else +#define PLATFORM_IS_LITTLE_ENDIAN false +#endif +#elif defined(OS_FREEBSD) || defined(OS_OPENBSD) || defined(OS_NETBSD) || defined(OS_DRAGONFLYBSD) +#include +#include +#define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) #elif defined(OS_HPUX) - #define PLATFORM_IS_LITTLE_ENDIAN false +#define PLATFORM_IS_LITTLE_ENDIAN false #elif defined(OS_ANDROID) - // Due to a bug in the NDK x86 definition, - // _BYTE_ORDER must be used instead of __BYTE_ORDER on Android. - // See http://code.google.com/p/android/issues/detail?id=39824 - #include - #define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) +// Due to a bug in the NDK x86 definition, +// _BYTE_ORDER must be used instead of __BYTE_ORDER on Android. +// See http://code.google.com/p/android/issues/detail?id=39824 +#include +#define PLATFORM_IS_LITTLE_ENDIAN (_BYTE_ORDER == _LITTLE_ENDIAN) #else - #include +#include #endif #include #if defined(HAVE_CRC32C) #include -#endif // defined(HAVE_CRC32C) +#endif // defined(HAVE_CRC32C) #ifdef HAVE_SNAPPY #include -#endif // defined(HAVE_SNAPPY) +#endif // defined(HAVE_SNAPPY) +#include "port/atomic_pointer.h" #include #include -#include "port/atomic_pointer.h" #ifndef PLATFORM_IS_LITTLE_ENDIAN #define PLATFORM_IS_LITTLE_ENDIAN (__BYTE_ORDER == __LITTLE_ENDIAN) #endif -#if defined(__APPLE__) || defined(OS_FREEBSD) ||\ - defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) +#if defined(__APPLE__) || defined(OS_FREEBSD) || defined(OS_OPENBSD) || defined(OS_DRAGONFLYBSD) // Use fsync() on platforms without fdatasync() #define fdatasync fsync #endif @@ -65,8 +65,10 @@ #define fdatasync fsync #endif -namespace leveldb { -namespace port { +namespace leveldb +{ +namespace port +{ static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN; #undef PLATFORM_IS_LITTLE_ENDIAN @@ -74,83 +76,88 @@ static const bool kLittleEndian = PLATFORM_IS_LITTLE_ENDIAN; class CondVar; class Mutex { - public: - Mutex(); - ~Mutex(); - - void Lock(); - void Unlock(); - void AssertHeld() { } - - private: - friend class CondVar; - pthread_mutex_t mu_; - - // No copying - Mutex(const Mutex&); - void operator=(const Mutex&); +public: + Mutex(); + ~Mutex(); + + void Lock(); + void Unlock(); + void AssertHeld() + { + } + +private: + friend class CondVar; + pthread_mutex_t mu_; + + // No copying + Mutex(const Mutex &); + void operator=(const Mutex &); }; class CondVar { - public: - explicit CondVar(Mutex* mu); - ~CondVar(); - void Wait(); - void Signal(); - void SignalAll(); - private: - pthread_cond_t cv_; - Mutex* mu_; +public: + explicit CondVar(Mutex *mu); + ~CondVar(); + void Wait(); + void Signal(); + void SignalAll(); + +private: + pthread_cond_t cv_; + Mutex *mu_; }; typedef pthread_once_t OnceType; #define LEVELDB_ONCE_INIT PTHREAD_ONCE_INIT -extern void InitOnce(OnceType* once, void (*initializer)()); +extern void InitOnce(OnceType *once, void (*initializer)()); -inline bool Snappy_Compress(const char* input, size_t length, - ::std::string* output) { +inline bool Snappy_Compress(const char *input, size_t length, ::std::string *output) +{ #ifdef HAVE_SNAPPY - output->resize(snappy::MaxCompressedLength(length)); - size_t outlen; - snappy::RawCompress(input, length, &(*output)[0], &outlen); - output->resize(outlen); - return true; -#endif // defined(HAVE_SNAPPY) - - return false; + output->resize(snappy::MaxCompressedLength(length)); + size_t outlen; + snappy::RawCompress(input, length, &(*output)[0], &outlen); + output->resize(outlen); + return true; +#endif // defined(HAVE_SNAPPY) + + return false; } -inline bool Snappy_GetUncompressedLength(const char* input, size_t length, - size_t* result) { +inline bool Snappy_GetUncompressedLength(const char *input, size_t length, size_t *result) +{ #ifdef HAVE_SNAPPY - return snappy::GetUncompressedLength(input, length, result); + return snappy::GetUncompressedLength(input, length, result); #else - return false; -#endif // defined(HAVE_SNAPPY) + return false; +#endif // defined(HAVE_SNAPPY) } -inline bool Snappy_Uncompress(const char* input, size_t length, - char* output) { +inline bool Snappy_Uncompress(const char *input, size_t length, char *output) +{ #ifdef HAVE_SNAPPY - return snappy::RawUncompress(input, length, output); + return snappy::RawUncompress(input, length, output); #else - return false; -#endif // defined(HAVE_SNAPPY) + return false; +#endif // defined(HAVE_SNAPPY) } -inline bool GetHeapProfile(void (*func)(void*, const char*, int), void* arg) { - return false; +inline bool GetHeapProfile(void (*func)(void *, const char *, int), void *arg) +{ + return false; } -inline uint32_t AcceleratedCRC32C(uint32_t crc, const char* buf, size_t size) { +inline uint32_t AcceleratedCRC32C(uint32_t crc, const char *buf, size_t size) +{ #if defined(HAVE_CRC32C) - return ::crc32c::Extend(crc, reinterpret_cast(buf), size); + return ::crc32c::Extend(crc, reinterpret_cast(buf), size); #else - return 0; -#endif // defined(HAVE_CRC32C) + return 0; +#endif // defined(HAVE_CRC32C) } -} // namespace port -} // namespace leveldb +} // namespace port +} // namespace leveldb -#endif // STORAGE_LEVELDB_PORT_PORT_POSIX_H_ +#endif // STORAGE_LEVELDB_PORT_PORT_POSIX_H_ diff --git a/bench/port/thread_annotations.h b/bench/port/thread_annotations.h index 1abc9bc..17aca78 100644 --- a/bench/port/thread_annotations.h +++ b/bench/port/thread_annotations.h @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020, Intel Corporation + #ifndef STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ #define STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ @@ -57,4 +60,4 @@ #define NO_THREAD_SAFETY_ANALYSIS #endif -#endif // STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ +#endif // STORAGE_LEVELDB_PORT_THREAD_ANNOTATIONS_H_ diff --git a/bench/util/csv.h b/bench/util/csv.h index fa7389d..f626760 100644 --- a/bench/util/csv.h +++ b/bench/util/csv.h @@ -3,14 +3,13 @@ #pragma once +#include #include #include #include #include -#include -class CSV -{ +class CSV { private: /* Hold data in two-dimensional map of strings: data_matrix[row][column] */ @@ -43,19 +42,16 @@ class CSV void print() { // Print first column name - std::cout<< id_name; + std::cout << id_name; - for( auto &column: columns) - { + for (auto &column : columns) { std::cout << "," << column; } std::cout << "\r\n" << std::flush; - for( auto &row: data_matrix) - { + for (auto &row : data_matrix) { std::cout << row.first; - for(auto &column: columns) - { + for (auto &column : columns) { std::cout << "," << data_matrix[row.first][column]; } std::cout << "\r\n" << std::flush; diff --git a/bench/util/env.cc b/bench/util/env.cc index 893b4a6..53e6b85 100644 --- a/bench/util/env.cc +++ b/bench/util/env.cc @@ -4,97 +4,107 @@ #include "leveldb/env.h" -namespace leveldb { +namespace leveldb +{ -Env::~Env() { +Env::~Env() +{ } -Status Env::NewAppendableFile(const std::string& fname, WritableFile** result) { - return Status::NotSupported("NewAppendableFile", fname); +Status Env::NewAppendableFile(const std::string &fname, WritableFile **result) +{ + return Status::NotSupported("NewAppendableFile", fname); } -SequentialFile::~SequentialFile() { +SequentialFile::~SequentialFile() +{ } -RandomAccessFile::~RandomAccessFile() { +RandomAccessFile::~RandomAccessFile() +{ } -WritableFile::~WritableFile() { +WritableFile::~WritableFile() +{ } -Logger::~Logger() { +Logger::~Logger() +{ } -FileLock::~FileLock() { +FileLock::~FileLock() +{ } -void Log(Logger* info_log, const char* format, ...) { - if (info_log != NULL) { - va_list ap; - va_start(ap, format); - info_log->Logv(format, ap); - va_end(ap); - } +void Log(Logger *info_log, const char *format, ...) +{ + if (info_log != NULL) { + va_list ap; + va_start(ap, format); + info_log->Logv(format, ap); + va_end(ap); + } } -static Status DoWriteStringToFile(Env* env, const Slice& data, - const std::string& fname, - bool should_sync) { - WritableFile* file; - Status s = env->NewWritableFile(fname, &file); - if (!s.ok()) { - return s; - } - s = file->Append(data); - if (s.ok() && should_sync) { - s = file->Sync(); - } - if (s.ok()) { - s = file->Close(); - } - delete file; // Will auto-close if we did not close above - if (!s.ok()) { - env->DeleteFile(fname); - } - return s; +static Status DoWriteStringToFile(Env *env, const Slice &data, const std::string &fname, bool should_sync) +{ + WritableFile *file; + Status s = env->NewWritableFile(fname, &file); + if (!s.ok()) { + return s; + } + s = file->Append(data); + if (s.ok() && should_sync) { + s = file->Sync(); + } + if (s.ok()) { + s = file->Close(); + } + delete file; // Will auto-close if we did not close above + if (!s.ok()) { + env->DeleteFile(fname); + } + return s; } -Status WriteStringToFile(Env* env, const Slice& data, - const std::string& fname) { - return DoWriteStringToFile(env, data, fname, false); +Status WriteStringToFile(Env *env, const Slice &data, const std::string &fname) +{ + return DoWriteStringToFile(env, data, fname, false); } -Status WriteStringToFileSync(Env* env, const Slice& data, - const std::string& fname) { - return DoWriteStringToFile(env, data, fname, true); +Status WriteStringToFileSync(Env *env, const Slice &data, const std::string &fname) +{ + return DoWriteStringToFile(env, data, fname, true); } -Status ReadFileToString(Env* env, const std::string& fname, std::string* data) { - data->clear(); - SequentialFile* file; - Status s = env->NewSequentialFile(fname, &file); - if (!s.ok()) { - return s; - } - static const int kBufferSize = 8192; - char* space = new char[kBufferSize]; - while (true) { - Slice fragment; - s = file->Read(kBufferSize, &fragment, space); - if (!s.ok()) { - break; - } - data->append(fragment.data(), fragment.size()); - if (fragment.empty()) { - break; - } - } - delete[] space; - delete file; - return s; +Status ReadFileToString(Env *env, const std::string &fname, std::string *data) +{ + data->clear(); + SequentialFile *file; + Status s = env->NewSequentialFile(fname, &file); + if (!s.ok()) { + return s; + } + static const int kBufferSize = 8192; + char *space = new char[kBufferSize]; + while (true) { + Slice fragment; + s = file->Read(kBufferSize, &fragment, space); + if (!s.ok()) { + break; + } + data->append(fragment.data(), fragment.size()); + if (fragment.empty()) { + break; + } + } + delete[] space; + delete file; + return s; } -EnvWrapper::~EnvWrapper() { +EnvWrapper::~EnvWrapper() +{ } -} // namespace leveldb +} // namespace leveldb diff --git a/bench/util/env_posix.cc b/bench/util/env_posix.cc index 2eddfc2..96aefc6 100644 --- a/bench/util/env_posix.cc +++ b/bench/util/env_posix.cc @@ -2,10 +2,16 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2020, Intel Corporation */ + +#include #include #include #include +#include #include +#include #include #include #include @@ -16,33 +22,33 @@ #include #include #include -#include -#include -#include #include "leveldb/env.h" #include "leveldb/slice.h" #include "port/port_posix.h" +#include "util/env_posix_test_helper.h" #include "util/logging.h" #include "util/mutexlock.h" #include "util/posix_logger.h" -#include "util/env_posix_test_helper.h" -namespace leveldb { +namespace leveldb +{ -namespace { +namespace +{ static int open_read_only_file_limit = -1; static int mmap_limit = -1; static const size_t kBufSize = 65536; -static Status PosixError(const std::string& context, int err_number) { - if (err_number == ENOENT) { - return Status::NotFound(context, strerror(err_number)); - } else { - return Status::IOError(context, strerror(err_number)); - } +static Status PosixError(const std::string &context, int err_number) +{ + if (err_number == ENOENT) { + return Status::NotFound(context, strerror(err_number)); + } else { + return Status::IOError(context, strerror(err_number)); + } } // Helper class to limit resource usage to avoid exhaustion. @@ -50,701 +56,755 @@ static Status PosixError(const std::string& context, int err_number) { // so that we do not end up running out of file descriptors, virtual memory, // or running into kernel performance problems for very large databases. class Limiter { - public: - // Limit maximum number of resources to |n|. - Limiter(intptr_t n) { - SetAllowed(n); - } - - // If another resource is available, acquire it and return true. - // Else return false. - bool Acquire() { - if (GetAllowed() <= 0) { - return false; - } - MutexLock l(&mu_); - intptr_t x = GetAllowed(); - if (x <= 0) { - return false; - } else { - SetAllowed(x - 1); - return true; - } - } - - // Release a resource acquired by a previous call to Acquire() that returned - // true. - void Release() { - MutexLock l(&mu_); - SetAllowed(GetAllowed() + 1); - } - - private: - port::Mutex mu_; - port::AtomicPointer allowed_; - - intptr_t GetAllowed() const { - return reinterpret_cast(allowed_.Acquire_Load()); - } - - // REQUIRES: mu_ must be held - void SetAllowed(intptr_t v) { - allowed_.Release_Store(reinterpret_cast(v)); - } - - Limiter(const Limiter&); - void operator=(const Limiter&); +public: + // Limit maximum number of resources to |n|. + Limiter(intptr_t n) + { + SetAllowed(n); + } + + // If another resource is available, acquire it and return true. + // Else return false. + bool Acquire() + { + if (GetAllowed() <= 0) { + return false; + } + MutexLock l(&mu_); + intptr_t x = GetAllowed(); + if (x <= 0) { + return false; + } else { + SetAllowed(x - 1); + return true; + } + } + + // Release a resource acquired by a previous call to Acquire() that returned + // true. + void Release() + { + MutexLock l(&mu_); + SetAllowed(GetAllowed() + 1); + } + +private: + port::Mutex mu_; + port::AtomicPointer allowed_; + + intptr_t GetAllowed() const + { + return reinterpret_cast(allowed_.Acquire_Load()); + } + + // REQUIRES: mu_ must be held + void SetAllowed(intptr_t v) + { + allowed_.Release_Store(reinterpret_cast(v)); + } + + Limiter(const Limiter &); + void operator=(const Limiter &); }; -class PosixSequentialFile: public SequentialFile { - private: - std::string filename_; - int fd_; - - public: - PosixSequentialFile(const std::string& fname, int fd) - : filename_(fname), fd_(fd) {} - virtual ~PosixSequentialFile() { close(fd_); } - - virtual Status Read(size_t n, Slice* result, char* scratch) { - Status s; - while (true) { - ssize_t r = read(fd_, scratch, n); - if (r < 0) { - if (errno == EINTR) { - continue; // Retry - } - s = PosixError(filename_, errno); - break; - } - *result = Slice(scratch, r); - break; - } - return s; - } - - virtual Status Skip(uint64_t n) { - if (lseek(fd_, n, SEEK_CUR) == static_cast(-1)) { - return PosixError(filename_, errno); - } - return Status::OK(); - } +class PosixSequentialFile : public SequentialFile { +private: + std::string filename_; + int fd_; + +public: + PosixSequentialFile(const std::string &fname, int fd) : filename_(fname), fd_(fd) + { + } + virtual ~PosixSequentialFile() + { + close(fd_); + } + + virtual Status Read(size_t n, Slice *result, char *scratch) + { + Status s; + while (true) { + ssize_t r = read(fd_, scratch, n); + if (r < 0) { + if (errno == EINTR) { + continue; // Retry + } + s = PosixError(filename_, errno); + break; + } + *result = Slice(scratch, r); + break; + } + return s; + } + + virtual Status Skip(uint64_t n) + { + if (lseek(fd_, n, SEEK_CUR) == static_cast(-1)) { + return PosixError(filename_, errno); + } + return Status::OK(); + } }; // pread() based random-access -class PosixRandomAccessFile: public RandomAccessFile { - private: - std::string filename_; - bool temporary_fd_; // If true, fd_ is -1 and we open on every read. - int fd_; - Limiter* limiter_; - - public: - PosixRandomAccessFile(const std::string& fname, int fd, Limiter* limiter) - : filename_(fname), fd_(fd), limiter_(limiter) { - temporary_fd_ = !limiter->Acquire(); - if (temporary_fd_) { - // Open file on every access. - close(fd_); - fd_ = -1; - } - } - - virtual ~PosixRandomAccessFile() { - if (!temporary_fd_) { - close(fd_); - limiter_->Release(); - } - } - - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { - int fd = fd_; - if (temporary_fd_) { - fd = open(filename_.c_str(), O_RDONLY); - if (fd < 0) { - return PosixError(filename_, errno); - } - } - - Status s; - ssize_t r = pread(fd, scratch, n, static_cast(offset)); - *result = Slice(scratch, (r < 0) ? 0 : r); - if (r < 0) { - // An error: return a non-ok status - s = PosixError(filename_, errno); - } - if (temporary_fd_) { - // Close the temporary file descriptor opened earlier. - close(fd); - } - return s; - } +class PosixRandomAccessFile : public RandomAccessFile { +private: + std::string filename_; + bool temporary_fd_; // If true, fd_ is -1 and we open on every read. + int fd_; + Limiter *limiter_; + +public: + PosixRandomAccessFile(const std::string &fname, int fd, Limiter *limiter) + : filename_(fname), fd_(fd), limiter_(limiter) + { + temporary_fd_ = !limiter->Acquire(); + if (temporary_fd_) { + // Open file on every access. + close(fd_); + fd_ = -1; + } + } + + virtual ~PosixRandomAccessFile() + { + if (!temporary_fd_) { + close(fd_); + limiter_->Release(); + } + } + + virtual Status Read(uint64_t offset, size_t n, Slice *result, char *scratch) const + { + int fd = fd_; + if (temporary_fd_) { + fd = open(filename_.c_str(), O_RDONLY); + if (fd < 0) { + return PosixError(filename_, errno); + } + } + + Status s; + ssize_t r = pread(fd, scratch, n, static_cast(offset)); + *result = Slice(scratch, (r < 0) ? 0 : r); + if (r < 0) { + // An error: return a non-ok status + s = PosixError(filename_, errno); + } + if (temporary_fd_) { + // Close the temporary file descriptor opened earlier. + close(fd); + } + return s; + } }; // mmap() based random-access -class PosixMmapReadableFile: public RandomAccessFile { - private: - std::string filename_; - void* mmapped_region_; - size_t length_; - Limiter* limiter_; - - public: - // base[0,length-1] contains the mmapped contents of the file. - PosixMmapReadableFile(const std::string& fname, void* base, size_t length, - Limiter* limiter) - : filename_(fname), mmapped_region_(base), length_(length), - limiter_(limiter) { - } - - virtual ~PosixMmapReadableFile() { - munmap(mmapped_region_, length_); - limiter_->Release(); - } - - virtual Status Read(uint64_t offset, size_t n, Slice* result, - char* scratch) const { - Status s; - if (offset + n > length_) { - *result = Slice(); - s = PosixError(filename_, EINVAL); - } else { - *result = Slice(reinterpret_cast(mmapped_region_) + offset, n); - } - return s; - } +class PosixMmapReadableFile : public RandomAccessFile { +private: + std::string filename_; + void *mmapped_region_; + size_t length_; + Limiter *limiter_; + +public: + // base[0,length-1] contains the mmapped contents of the file. + PosixMmapReadableFile(const std::string &fname, void *base, size_t length, Limiter *limiter) + : filename_(fname), mmapped_region_(base), length_(length), limiter_(limiter) + { + } + + virtual ~PosixMmapReadableFile() + { + munmap(mmapped_region_, length_); + limiter_->Release(); + } + + virtual Status Read(uint64_t offset, size_t n, Slice *result, char *scratch) const + { + Status s; + if (offset + n > length_) { + *result = Slice(); + s = PosixError(filename_, EINVAL); + } else { + *result = Slice(reinterpret_cast(mmapped_region_) + offset, n); + } + return s; + } }; class PosixWritableFile : public WritableFile { - private: - // buf_[0, pos_-1] contains data to be written to fd_. - std::string filename_; - int fd_; - char buf_[kBufSize]; - size_t pos_; - - public: - PosixWritableFile(const std::string& fname, int fd) - : filename_(fname), fd_(fd), pos_(0) { } - - ~PosixWritableFile() { - if (fd_ >= 0) { - // Ignoring any potential errors - Close(); - } - } - - virtual Status Append(const Slice& data) { - size_t n = data.size(); - const char* p = data.data(); - - // Fit as much as possible into buffer. - size_t copy = std::min(n, kBufSize - pos_); - memcpy(buf_ + pos_, p, copy); - p += copy; - n -= copy; - pos_ += copy; - if (n == 0) { - return Status::OK(); - } - - // Can't fit in buffer, so need to do at least one write. - Status s = FlushBuffered(); - if (!s.ok()) { - return s; - } - - // Small writes go to buffer, large writes are written directly. - if (n < kBufSize) { - memcpy(buf_, p, n); - pos_ = n; - return Status::OK(); - } - return WriteRaw(p, n); - } - - virtual Status Close() { - Status result = FlushBuffered(); - const int r = close(fd_); - if (r < 0 && result.ok()) { - result = PosixError(filename_, errno); - } - fd_ = -1; - return result; - } - - virtual Status Flush() { - return FlushBuffered(); - } - - Status SyncDirIfManifest() { - const char* f = filename_.c_str(); - const char* sep = strrchr(f, '/'); - Slice basename; - std::string dir; - if (sep == NULL) { - dir = "."; - basename = f; - } else { - dir = std::string(f, sep - f); - basename = sep + 1; - } - Status s; - if (basename.starts_with("MANIFEST")) { - int fd = open(dir.c_str(), O_RDONLY); - if (fd < 0) { - s = PosixError(dir, errno); - } else { - if (fsync(fd) < 0) { - s = PosixError(dir, errno); - } - close(fd); - } - } - return s; - } - - virtual Status Sync() { - // Ensure new files referred to by the manifest are in the filesystem. - Status s = SyncDirIfManifest(); - if (!s.ok()) { - return s; - } - s = FlushBuffered(); - if (s.ok()) { - if (fdatasync(fd_) != 0) { - s = PosixError(filename_, errno); - } - } - return s; - } - - private: - Status FlushBuffered() { - Status s = WriteRaw(buf_, pos_); - pos_ = 0; - return s; - } - - Status WriteRaw(const char* p, size_t n) { - while (n > 0) { - ssize_t r = write(fd_, p, n); - if (r < 0) { - if (errno == EINTR) { - continue; // Retry - } - return PosixError(filename_, errno); - } - p += r; - n -= r; - } - return Status::OK(); - } +private: + // buf_[0, pos_-1] contains data to be written to fd_. + std::string filename_; + int fd_; + char buf_[kBufSize]; + size_t pos_; + +public: + PosixWritableFile(const std::string &fname, int fd) : filename_(fname), fd_(fd), pos_(0) + { + } + + ~PosixWritableFile() + { + if (fd_ >= 0) { + // Ignoring any potential errors + Close(); + } + } + + virtual Status Append(const Slice &data) + { + size_t n = data.size(); + const char *p = data.data(); + + // Fit as much as possible into buffer. + size_t copy = std::min(n, kBufSize - pos_); + memcpy(buf_ + pos_, p, copy); + p += copy; + n -= copy; + pos_ += copy; + if (n == 0) { + return Status::OK(); + } + + // Can't fit in buffer, so need to do at least one write. + Status s = FlushBuffered(); + if (!s.ok()) { + return s; + } + + // Small writes go to buffer, large writes are written directly. + if (n < kBufSize) { + memcpy(buf_, p, n); + pos_ = n; + return Status::OK(); + } + return WriteRaw(p, n); + } + + virtual Status Close() + { + Status result = FlushBuffered(); + const int r = close(fd_); + if (r < 0 && result.ok()) { + result = PosixError(filename_, errno); + } + fd_ = -1; + return result; + } + + virtual Status Flush() + { + return FlushBuffered(); + } + + Status SyncDirIfManifest() + { + const char *f = filename_.c_str(); + const char *sep = strrchr(f, '/'); + Slice basename; + std::string dir; + if (sep == NULL) { + dir = "."; + basename = f; + } else { + dir = std::string(f, sep - f); + basename = sep + 1; + } + Status s; + if (basename.starts_with("MANIFEST")) { + int fd = open(dir.c_str(), O_RDONLY); + if (fd < 0) { + s = PosixError(dir, errno); + } else { + if (fsync(fd) < 0) { + s = PosixError(dir, errno); + } + close(fd); + } + } + return s; + } + + virtual Status Sync() + { + // Ensure new files referred to by the manifest are in the filesystem. + Status s = SyncDirIfManifest(); + if (!s.ok()) { + return s; + } + s = FlushBuffered(); + if (s.ok()) { + if (fdatasync(fd_) != 0) { + s = PosixError(filename_, errno); + } + } + return s; + } + +private: + Status FlushBuffered() + { + Status s = WriteRaw(buf_, pos_); + pos_ = 0; + return s; + } + + Status WriteRaw(const char *p, size_t n) + { + while (n > 0) { + ssize_t r = write(fd_, p, n); + if (r < 0) { + if (errno == EINTR) { + continue; // Retry + } + return PosixError(filename_, errno); + } + p += r; + n -= r; + } + return Status::OK(); + } }; -static int LockOrUnlock(int fd, bool lock) { - errno = 0; - struct flock f; - memset(&f, 0, sizeof(f)); - f.l_type = (lock ? F_WRLCK : F_UNLCK); - f.l_whence = SEEK_SET; - f.l_start = 0; - f.l_len = 0; // Lock/unlock entire file - return fcntl(fd, F_SETLK, &f); +static int LockOrUnlock(int fd, bool lock) +{ + errno = 0; + struct flock f; + memset(&f, 0, sizeof(f)); + f.l_type = (lock ? F_WRLCK : F_UNLCK); + f.l_whence = SEEK_SET; + f.l_start = 0; + f.l_len = 0; // Lock/unlock entire file + return fcntl(fd, F_SETLK, &f); } class PosixFileLock : public FileLock { - public: - int fd_; - std::string name_; +public: + int fd_; + std::string name_; }; // Set of locked files. We keep a separate set instead of just // relying on fcntrl(F_SETLK) since fcntl(F_SETLK) does not provide // any protection against multiple uses from the same process. class PosixLockTable { - private: - port::Mutex mu_; - std::set locked_files_; - public: - bool Insert(const std::string& fname) { - MutexLock l(&mu_); - return locked_files_.insert(fname).second; - } - void Remove(const std::string& fname) { - MutexLock l(&mu_); - locked_files_.erase(fname); - } +private: + port::Mutex mu_; + std::set locked_files_; + +public: + bool Insert(const std::string &fname) + { + MutexLock l(&mu_); + return locked_files_.insert(fname).second; + } + void Remove(const std::string &fname) + { + MutexLock l(&mu_); + locked_files_.erase(fname); + } }; class PosixEnv : public Env { - public: - PosixEnv(); - virtual ~PosixEnv() { - char msg[] = "Destroying Env::Default()\n"; - fwrite(msg, 1, sizeof(msg), stderr); - abort(); - } - - virtual Status NewSequentialFile(const std::string& fname, - SequentialFile** result) { - int fd = open(fname.c_str(), O_RDONLY); - if (fd < 0) { - *result = NULL; - return PosixError(fname, errno); - } else { - *result = new PosixSequentialFile(fname, fd); - return Status::OK(); - } - } - - virtual Status NewRandomAccessFile(const std::string& fname, - RandomAccessFile** result) { - *result = NULL; - Status s; - int fd = open(fname.c_str(), O_RDONLY); - if (fd < 0) { - s = PosixError(fname, errno); - } else if (mmap_limit_.Acquire()) { - uint64_t size; - s = GetFileSize(fname, &size); - if (s.ok()) { - void* base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - if (base != MAP_FAILED) { - *result = new PosixMmapReadableFile(fname, base, size, &mmap_limit_); - } else { - s = PosixError(fname, errno); - } - } - close(fd); - if (!s.ok()) { - mmap_limit_.Release(); - } - } else { - *result = new PosixRandomAccessFile(fname, fd, &fd_limit_); - } - return s; - } - - virtual Status NewWritableFile(const std::string& fname, - WritableFile** result) { - Status s; - int fd = open(fname.c_str(), O_TRUNC | O_WRONLY | O_CREAT, 0644); - if (fd < 0) { - *result = NULL; - s = PosixError(fname, errno); - } else { - *result = new PosixWritableFile(fname, fd); - } - return s; - } - - virtual Status NewAppendableFile(const std::string& fname, - WritableFile** result) { - Status s; - int fd = open(fname.c_str(), O_APPEND | O_WRONLY | O_CREAT, 0644); - if (fd < 0) { - *result = NULL; - s = PosixError(fname, errno); - } else { - *result = new PosixWritableFile(fname, fd); - } - return s; - } - - virtual bool FileExists(const std::string& fname) { - return access(fname.c_str(), F_OK) == 0; - } - - virtual Status GetChildren(const std::string& dir, - std::vector* result) { - result->clear(); - DIR* d = opendir(dir.c_str()); - if (d == NULL) { - return PosixError(dir, errno); - } - struct dirent* entry; - while ((entry = readdir(d)) != NULL) { - result->push_back(entry->d_name); - } - closedir(d); - return Status::OK(); - } - - virtual Status DeleteFile(const std::string& fname) { - Status result; - if (unlink(fname.c_str()) != 0) { - result = PosixError(fname, errno); - } - return result; - } - - virtual Status CreateDir(const std::string& name) { - Status result; - if (mkdir(name.c_str(), 0755) != 0) { - result = PosixError(name, errno); - } - return result; - } - - virtual Status DeleteDir(const std::string& name) { - Status result; - if (rmdir(name.c_str()) != 0) { - result = PosixError(name, errno); - } - return result; - } - - virtual Status GetFileSize(const std::string& fname, uint64_t* size) { - Status s; - struct stat sbuf; - if (stat(fname.c_str(), &sbuf) != 0) { - *size = 0; - s = PosixError(fname, errno); - } else { - *size = sbuf.st_size; - } - return s; - } - - virtual Status RenameFile(const std::string& src, const std::string& target) { - Status result; - if (rename(src.c_str(), target.c_str()) != 0) { - result = PosixError(src, errno); - } - return result; - } - - virtual Status LockFile(const std::string& fname, FileLock** lock) { - *lock = NULL; - Status result; - int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); - if (fd < 0) { - result = PosixError(fname, errno); - } else if (!locks_.Insert(fname)) { - close(fd); - result = Status::IOError("lock " + fname, "already held by process"); - } else if (LockOrUnlock(fd, true) == -1) { - result = PosixError("lock " + fname, errno); - close(fd); - locks_.Remove(fname); - } else { - PosixFileLock* my_lock = new PosixFileLock; - my_lock->fd_ = fd; - my_lock->name_ = fname; - *lock = my_lock; - } - return result; - } - - virtual Status UnlockFile(FileLock* lock) { - PosixFileLock* my_lock = reinterpret_cast(lock); - Status result; - if (LockOrUnlock(my_lock->fd_, false) == -1) { - result = PosixError("unlock", errno); - } - locks_.Remove(my_lock->name_); - close(my_lock->fd_); - delete my_lock; - return result; - } - - virtual void Schedule(void (*function)(void*), void* arg); - - virtual void StartThread(void (*function)(void* arg), void* arg); - - virtual Status GetTestDirectory(std::string* result) { - const char* env = getenv("TEST_TMPDIR"); - if (env && env[0] != '\0') { - *result = env; - } else { - char buf[100]; - snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid())); - *result = buf; - } - // Directory may already exist - CreateDir(*result); - return Status::OK(); - } - - static uint64_t gettid() { - pthread_t tid = pthread_self(); - uint64_t thread_id = 0; - memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid))); - return thread_id; - } - - virtual Status NewLogger(const std::string& fname, Logger** result) { - FILE* f = fopen(fname.c_str(), "w"); - if (f == NULL) { - *result = NULL; - return PosixError(fname, errno); - } else { - *result = new PosixLogger(f, &PosixEnv::gettid); - return Status::OK(); - } - } - - virtual uint64_t NowMicros() { - struct timeval tv; - gettimeofday(&tv, NULL); - return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; - } - - virtual void SleepForMicroseconds(int micros) { - usleep(micros); - } - - private: - void PthreadCall(const char* label, int result) { - if (result != 0) { - fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); - abort(); - } - } - - // BGThread() is the body of the background thread - void BGThread(); - static void* BGThreadWrapper(void* arg) { - reinterpret_cast(arg)->BGThread(); - return NULL; - } - - pthread_mutex_t mu_; - pthread_cond_t bgsignal_; - pthread_t bgthread_; - bool started_bgthread_; - - // Entry per Schedule() call - struct BGItem { void* arg; void (*function)(void*); }; - typedef std::deque BGQueue; - BGQueue queue_; - - PosixLockTable locks_; - Limiter mmap_limit_; - Limiter fd_limit_; +public: + PosixEnv(); + virtual ~PosixEnv() + { + char msg[] = "Destroying Env::Default()\n"; + fwrite(msg, 1, sizeof(msg), stderr); + abort(); + } + + virtual Status NewSequentialFile(const std::string &fname, SequentialFile **result) + { + int fd = open(fname.c_str(), O_RDONLY); + if (fd < 0) { + *result = NULL; + return PosixError(fname, errno); + } else { + *result = new PosixSequentialFile(fname, fd); + return Status::OK(); + } + } + + virtual Status NewRandomAccessFile(const std::string &fname, RandomAccessFile **result) + { + *result = NULL; + Status s; + int fd = open(fname.c_str(), O_RDONLY); + if (fd < 0) { + s = PosixError(fname, errno); + } else if (mmap_limit_.Acquire()) { + uint64_t size; + s = GetFileSize(fname, &size); + if (s.ok()) { + void *base = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (base != MAP_FAILED) { + *result = new PosixMmapReadableFile(fname, base, size, &mmap_limit_); + } else { + s = PosixError(fname, errno); + } + } + close(fd); + if (!s.ok()) { + mmap_limit_.Release(); + } + } else { + *result = new PosixRandomAccessFile(fname, fd, &fd_limit_); + } + return s; + } + + virtual Status NewWritableFile(const std::string &fname, WritableFile **result) + { + Status s; + int fd = open(fname.c_str(), O_TRUNC | O_WRONLY | O_CREAT, 0644); + if (fd < 0) { + *result = NULL; + s = PosixError(fname, errno); + } else { + *result = new PosixWritableFile(fname, fd); + } + return s; + } + + virtual Status NewAppendableFile(const std::string &fname, WritableFile **result) + { + Status s; + int fd = open(fname.c_str(), O_APPEND | O_WRONLY | O_CREAT, 0644); + if (fd < 0) { + *result = NULL; + s = PosixError(fname, errno); + } else { + *result = new PosixWritableFile(fname, fd); + } + return s; + } + + virtual bool FileExists(const std::string &fname) + { + return access(fname.c_str(), F_OK) == 0; + } + + virtual Status GetChildren(const std::string &dir, std::vector *result) + { + result->clear(); + DIR *d = opendir(dir.c_str()); + if (d == NULL) { + return PosixError(dir, errno); + } + struct dirent *entry; + while ((entry = readdir(d)) != NULL) { + result->push_back(entry->d_name); + } + closedir(d); + return Status::OK(); + } + + virtual Status DeleteFile(const std::string &fname) + { + Status result; + if (unlink(fname.c_str()) != 0) { + result = PosixError(fname, errno); + } + return result; + } + + virtual Status CreateDir(const std::string &name) + { + Status result; + if (mkdir(name.c_str(), 0755) != 0) { + result = PosixError(name, errno); + } + return result; + } + + virtual Status DeleteDir(const std::string &name) + { + Status result; + if (rmdir(name.c_str()) != 0) { + result = PosixError(name, errno); + } + return result; + } + + virtual Status GetFileSize(const std::string &fname, uint64_t *size) + { + Status s; + struct stat sbuf; + if (stat(fname.c_str(), &sbuf) != 0) { + *size = 0; + s = PosixError(fname, errno); + } else { + *size = sbuf.st_size; + } + return s; + } + + virtual Status RenameFile(const std::string &src, const std::string &target) + { + Status result; + if (rename(src.c_str(), target.c_str()) != 0) { + result = PosixError(src, errno); + } + return result; + } + + virtual Status LockFile(const std::string &fname, FileLock **lock) + { + *lock = NULL; + Status result; + int fd = open(fname.c_str(), O_RDWR | O_CREAT, 0644); + if (fd < 0) { + result = PosixError(fname, errno); + } else if (!locks_.Insert(fname)) { + close(fd); + result = Status::IOError("lock " + fname, "already held by process"); + } else if (LockOrUnlock(fd, true) == -1) { + result = PosixError("lock " + fname, errno); + close(fd); + locks_.Remove(fname); + } else { + PosixFileLock *my_lock = new PosixFileLock; + my_lock->fd_ = fd; + my_lock->name_ = fname; + *lock = my_lock; + } + return result; + } + + virtual Status UnlockFile(FileLock *lock) + { + PosixFileLock *my_lock = reinterpret_cast(lock); + Status result; + if (LockOrUnlock(my_lock->fd_, false) == -1) { + result = PosixError("unlock", errno); + } + locks_.Remove(my_lock->name_); + close(my_lock->fd_); + delete my_lock; + return result; + } + + virtual void Schedule(void (*function)(void *), void *arg); + + virtual void StartThread(void (*function)(void *arg), void *arg); + + virtual Status GetTestDirectory(std::string *result) + { + const char *env = getenv("TEST_TMPDIR"); + if (env && env[0] != '\0') { + *result = env; + } else { + char buf[100]; + snprintf(buf, sizeof(buf), "/tmp/leveldbtest-%d", int(geteuid())); + *result = buf; + } + // Directory may already exist + CreateDir(*result); + return Status::OK(); + } + + static uint64_t gettid() + { + pthread_t tid = pthread_self(); + uint64_t thread_id = 0; + memcpy(&thread_id, &tid, std::min(sizeof(thread_id), sizeof(tid))); + return thread_id; + } + + virtual Status NewLogger(const std::string &fname, Logger **result) + { + FILE *f = fopen(fname.c_str(), "w"); + if (f == NULL) { + *result = NULL; + return PosixError(fname, errno); + } else { + *result = new PosixLogger(f, &PosixEnv::gettid); + return Status::OK(); + } + } + + virtual uint64_t NowMicros() + { + struct timeval tv; + gettimeofday(&tv, NULL); + return static_cast(tv.tv_sec) * 1000000 + tv.tv_usec; + } + + virtual void SleepForMicroseconds(int micros) + { + usleep(micros); + } + +private: + void PthreadCall(const char *label, int result) + { + if (result != 0) { + fprintf(stderr, "pthread %s: %s\n", label, strerror(result)); + abort(); + } + } + + // BGThread() is the body of the background thread + void BGThread(); + static void *BGThreadWrapper(void *arg) + { + reinterpret_cast(arg)->BGThread(); + return NULL; + } + + pthread_mutex_t mu_; + pthread_cond_t bgsignal_; + pthread_t bgthread_; + bool started_bgthread_; + + // Entry per Schedule() call + struct BGItem { + void *arg; + void (*function)(void *); + }; + typedef std::deque BGQueue; + BGQueue queue_; + + PosixLockTable locks_; + Limiter mmap_limit_; + Limiter fd_limit_; }; // Return the maximum number of concurrent mmaps. -static int MaxMmaps() { - if (mmap_limit >= 0) { - return mmap_limit; - } - // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes. - mmap_limit = sizeof(void*) >= 8 ? 1000 : 0; - return mmap_limit; +static int MaxMmaps() +{ + if (mmap_limit >= 0) { + return mmap_limit; + } + // Up to 1000 mmaps for 64-bit binaries; none for smaller pointer sizes. + mmap_limit = sizeof(void *) >= 8 ? 1000 : 0; + return mmap_limit; } // Return the maximum number of read-only files to keep open. -static intptr_t MaxOpenFiles() { - if (open_read_only_file_limit >= 0) { - return open_read_only_file_limit; - } - struct rlimit rlim; - if (getrlimit(RLIMIT_NOFILE, &rlim)) { - // getrlimit failed, fallback to hard-coded default. - open_read_only_file_limit = 50; - } else if (rlim.rlim_cur == RLIM_INFINITY) { - open_read_only_file_limit = std::numeric_limits::max(); - } else { - // Allow use of 20% of available file descriptors for read-only files. - open_read_only_file_limit = rlim.rlim_cur / 5; - } - return open_read_only_file_limit; +static intptr_t MaxOpenFiles() +{ + if (open_read_only_file_limit >= 0) { + return open_read_only_file_limit; + } + struct rlimit rlim; + if (getrlimit(RLIMIT_NOFILE, &rlim)) { + // getrlimit failed, fallback to hard-coded default. + open_read_only_file_limit = 50; + } else if (rlim.rlim_cur == RLIM_INFINITY) { + open_read_only_file_limit = std::numeric_limits::max(); + } else { + // Allow use of 20% of available file descriptors for read-only files. + open_read_only_file_limit = rlim.rlim_cur / 5; + } + return open_read_only_file_limit; } -PosixEnv::PosixEnv() - : started_bgthread_(false), - mmap_limit_(MaxMmaps()), - fd_limit_(MaxOpenFiles()) { - PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL)); - PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL)); +PosixEnv::PosixEnv() : started_bgthread_(false), mmap_limit_(MaxMmaps()), fd_limit_(MaxOpenFiles()) +{ + PthreadCall("mutex_init", pthread_mutex_init(&mu_, NULL)); + PthreadCall("cvar_init", pthread_cond_init(&bgsignal_, NULL)); } -void PosixEnv::Schedule(void (*function)(void*), void* arg) { - PthreadCall("lock", pthread_mutex_lock(&mu_)); - - // Start background thread if necessary - if (!started_bgthread_) { - started_bgthread_ = true; - PthreadCall( - "create thread", - pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this)); - } - - // If the queue is currently empty, the background thread may currently be - // waiting. - if (queue_.empty()) { - PthreadCall("signal", pthread_cond_signal(&bgsignal_)); - } - - // Add to priority queue - queue_.push_back(BGItem()); - queue_.back().function = function; - queue_.back().arg = arg; - - PthreadCall("unlock", pthread_mutex_unlock(&mu_)); +void PosixEnv::Schedule(void (*function)(void *), void *arg) +{ + PthreadCall("lock", pthread_mutex_lock(&mu_)); + + // Start background thread if necessary + if (!started_bgthread_) { + started_bgthread_ = true; + PthreadCall("create thread", + pthread_create(&bgthread_, NULL, &PosixEnv::BGThreadWrapper, this)); + } + + // If the queue is currently empty, the background thread may currently be + // waiting. + if (queue_.empty()) { + PthreadCall("signal", pthread_cond_signal(&bgsignal_)); + } + + // Add to priority queue + queue_.push_back(BGItem()); + queue_.back().function = function; + queue_.back().arg = arg; + + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); } -void PosixEnv::BGThread() { - while (true) { - // Wait until there is an item that is ready to run - PthreadCall("lock", pthread_mutex_lock(&mu_)); - while (queue_.empty()) { - PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_)); - } - - void (*function)(void*) = queue_.front().function; - void* arg = queue_.front().arg; - queue_.pop_front(); - - PthreadCall("unlock", pthread_mutex_unlock(&mu_)); - (*function)(arg); - } +void PosixEnv::BGThread() +{ + while (true) { + // Wait until there is an item that is ready to run + PthreadCall("lock", pthread_mutex_lock(&mu_)); + while (queue_.empty()) { + PthreadCall("wait", pthread_cond_wait(&bgsignal_, &mu_)); + } + + void (*function)(void *) = queue_.front().function; + void *arg = queue_.front().arg; + queue_.pop_front(); + + PthreadCall("unlock", pthread_mutex_unlock(&mu_)); + (*function)(arg); + } } -namespace { +namespace +{ struct StartThreadState { - void (*user_function)(void*); - void* arg; + void (*user_function)(void *); + void *arg; }; } -static void* StartThreadWrapper(void* arg) { - StartThreadState* state = reinterpret_cast(arg); - state->user_function(state->arg); - delete state; - return NULL; +static void *StartThreadWrapper(void *arg) +{ + StartThreadState *state = reinterpret_cast(arg); + state->user_function(state->arg); + delete state; + return NULL; } -void PosixEnv::StartThread(void (*function)(void* arg), void* arg) { - pthread_t t; - StartThreadState* state = new StartThreadState; - state->user_function = function; - state->arg = arg; - PthreadCall("start thread", - pthread_create(&t, NULL, &StartThreadWrapper, state)); +void PosixEnv::StartThread(void (*function)(void *arg), void *arg) +{ + pthread_t t; + StartThreadState *state = new StartThreadState; + state->user_function = function; + state->arg = arg; + PthreadCall("start thread", pthread_create(&t, NULL, &StartThreadWrapper, state)); } -} // namespace +} // namespace static pthread_once_t once = PTHREAD_ONCE_INIT; -static Env* default_env; -static void InitDefaultEnv() { default_env = new PosixEnv; } +static Env *default_env; +static void InitDefaultEnv() +{ + default_env = new PosixEnv; +} -void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) { - assert(default_env == NULL); - open_read_only_file_limit = limit; +void EnvPosixTestHelper::SetReadOnlyFDLimit(int limit) +{ + assert(default_env == NULL); + open_read_only_file_limit = limit; } -void EnvPosixTestHelper::SetReadOnlyMMapLimit(int limit) { - assert(default_env == NULL); - mmap_limit = limit; +void EnvPosixTestHelper::SetReadOnlyMMapLimit(int limit) +{ + assert(default_env == NULL); + mmap_limit = limit; } -Env* Env::Default() { - pthread_once(&once, InitDefaultEnv); - return default_env; +Env *Env::Default() +{ + pthread_once(&once, InitDefaultEnv); + return default_env; } -} // namespace leveldb +} // namespace leveldb diff --git a/bench/util/env_posix_test_helper.h b/bench/util/env_posix_test_helper.h index e5e57e0..1e289b7 100644 --- a/bench/util/env_posix_test_helper.h +++ b/bench/util/env_posix_test_helper.h @@ -2,27 +2,31 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020, Intel Corporation + #ifndef STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ #define STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ -namespace leveldb { +namespace leveldb +{ class EnvPosixTest; // A helper for the POSIX Env to facilitate testing. class EnvPosixTestHelper { - private: - friend class EnvPosixTest; +private: + friend class EnvPosixTest; - // Set the maximum number of read-only files that will be opened. - // Must be called before creating an Env. - static void SetReadOnlyFDLimit(int limit); + // Set the maximum number of read-only files that will be opened. + // Must be called before creating an Env. + static void SetReadOnlyFDLimit(int limit); - // Set the maximum number of read-only files that will be mapped via mmap. - // Must be called before creating an Env. - static void SetReadOnlyMMapLimit(int limit); + // Set the maximum number of read-only files that will be mapped via mmap. + // Must be called before creating an Env. + static void SetReadOnlyMMapLimit(int limit); }; -} // namespace leveldb +} // namespace leveldb -#endif // STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ +#endif // STORAGE_LEVELDB_UTIL_ENV_POSIX_TEST_HELPER_H_ diff --git a/bench/util/histogram.cc b/bench/util/histogram.cc index 66a9425..5b44b8f 100644 --- a/bench/util/histogram.cc +++ b/bench/util/histogram.cc @@ -2,13 +2,18 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2020, Intel Corporation */ + +#include "util/histogram.h" +#include "port/port_posix.h" #include #include -#include "port/port_posix.h" -#include "util/histogram.h" -namespace leveldb { +namespace leveldb +{ +// clang-format off const double Histogram::kBucketLimit[kNumBuckets] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 12, 14, 16, 18, 20, 25, 30, 35, 40, 45, 50, 60, 70, 80, 90, 100, 120, 140, 160, 180, 200, 250, 300, 350, 400, 450, @@ -29,117 +34,129 @@ const double Histogram::kBucketLimit[kNumBuckets] = { 5000000000.0, 6000000000.0, 7000000000.0, 8000000000.0, 9000000000.0, 1e200, }; +// clang-format on -void Histogram::Clear() { - min_ = kBucketLimit[kNumBuckets-1]; - max_ = 0; - num_ = 0; - sum_ = 0; - sum_squares_ = 0; - for (int i = 0; i < kNumBuckets; i++) { - buckets_[i] = 0; - } +void Histogram::Clear() +{ + min_ = kBucketLimit[kNumBuckets - 1]; + max_ = 0; + num_ = 0; + sum_ = 0; + sum_squares_ = 0; + for (int i = 0; i < kNumBuckets; i++) { + buckets_[i] = 0; + } } -void Histogram::Add(double value) { - // Linear search is fast enough for our usage in db_bench - int b = 0; - while (b < kNumBuckets - 1 && kBucketLimit[b] <= value) { - b++; - } - buckets_[b] += 1.0; - if (min_ > value) min_ = value; - if (max_ < value) max_ = value; - num_++; - sum_ += value; - sum_squares_ += (value * value); +void Histogram::Add(double value) +{ + // Linear search is fast enough for our usage in db_bench + int b = 0; + while (b < kNumBuckets - 1 && kBucketLimit[b] <= value) { + b++; + } + buckets_[b] += 1.0; + if (min_ > value) + min_ = value; + if (max_ < value) + max_ = value; + num_++; + sum_ += value; + sum_squares_ += (value * value); } -void Histogram::Merge(const Histogram& other) { - if (other.min_ < min_) min_ = other.min_; - if (other.max_ > max_) max_ = other.max_; - num_ += other.num_; - sum_ += other.sum_; - sum_squares_ += other.sum_squares_; - for (int b = 0; b < kNumBuckets; b++) { - buckets_[b] += other.buckets_[b]; - } +void Histogram::Merge(const Histogram &other) +{ + if (other.min_ < min_) + min_ = other.min_; + if (other.max_ > max_) + max_ = other.max_; + num_ += other.num_; + sum_ += other.sum_; + sum_squares_ += other.sum_squares_; + for (int b = 0; b < kNumBuckets; b++) { + buckets_[b] += other.buckets_[b]; + } } -double Histogram::Median() const { - return Percentile(50.0); +double Histogram::Median() const +{ + return Percentile(50.0); } -double Histogram::Percentile(double p) const { - double threshold = num_ * (p / 100.0); - double sum = 0; - for (int b = 0; b < kNumBuckets; b++) { - sum += buckets_[b]; - if (sum >= threshold) { - // Scale linearly within this bucket - double left_point = (b == 0) ? 0 : kBucketLimit[b-1]; - double right_point = kBucketLimit[b]; - double left_sum = sum - buckets_[b]; - double right_sum = sum; - double pos = (threshold - left_sum) / (right_sum - left_sum); - double r = left_point + (right_point - left_point) * pos; - if (r < min_) r = min_; - if (r > max_) r = max_; - return r; - } - } - return max_; +double Histogram::Percentile(double p) const +{ + double threshold = num_ * (p / 100.0); + double sum = 0; + for (int b = 0; b < kNumBuckets; b++) { + sum += buckets_[b]; + if (sum >= threshold) { + // Scale linearly within this bucket + double left_point = (b == 0) ? 0 : kBucketLimit[b - 1]; + double right_point = kBucketLimit[b]; + double left_sum = sum - buckets_[b]; + double right_sum = sum; + double pos = (threshold - left_sum) / (right_sum - left_sum); + double r = left_point + (right_point - left_point) * pos; + if (r < min_) + r = min_; + if (r > max_) + r = max_; + return r; + } + } + return max_; } -double Histogram::Average() const { - if (num_ == 0.0) return 0; - return sum_ / num_; +double Histogram::Average() const +{ + if (num_ == 0.0) + return 0; + return sum_ / num_; } -double Histogram::StandardDeviation() const { - if (num_ == 0.0) return 0; - double variance = (sum_squares_ * num_ - sum_ * sum_) / (num_ * num_); - return sqrt(variance); +double Histogram::StandardDeviation() const +{ + if (num_ == 0.0) + return 0; + double variance = (sum_squares_ * num_ - sum_ * sum_) / (num_ * num_); + return sqrt(variance); } -std::string Histogram::ToString() const { - std::string r; - char buf[200]; - snprintf(buf, sizeof(buf), - "Count: %.0f Average: %.4f StdDev: %.2f\n", - num_, Average(), StandardDeviation()); - r.append(buf); - snprintf(buf, sizeof(buf), - "Min: %.4f Median: %.4f Max: %.4f\n", - (num_ == 0.0 ? 0.0 : min_), Median(), max_); - r.append(buf); - snprintf(buf, sizeof(buf), - "Percentiles: P50: %.2f P75: %.2f P99: %.2f P99.9: %.2f P99.99: %.2f\n", - Percentile(50), Percentile(75), Percentile(99), - Percentile(99.9), Percentile(99.99) - ); - r.append(buf); - r.append("------------------------------------------------------\n"); - const double mult = 100.0 / num_; - double sum = 0; - for (int b = 0; b < kNumBuckets; b++) { - if (buckets_[b] <= 0.0) continue; - sum += buckets_[b]; - snprintf(buf, sizeof(buf), - "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ", - ((b == 0) ? 0.0 : kBucketLimit[b-1]), // left - kBucketLimit[b], // right - buckets_[b], // count - mult * buckets_[b], // percentage - mult * sum); // cumulative percentage - r.append(buf); +std::string Histogram::ToString() const +{ + std::string r; + char buf[200]; + snprintf(buf, sizeof(buf), "Count: %.0f Average: %.4f StdDev: %.2f\n", num_, Average(), + StandardDeviation()); + r.append(buf); + snprintf(buf, sizeof(buf), "Min: %.4f Median: %.4f Max: %.4f\n", (num_ == 0.0 ? 0.0 : min_), + Median(), max_); + r.append(buf); + snprintf(buf, sizeof(buf), "Percentiles: P50: %.2f P75: %.2f P99: %.2f P99.9: %.2f P99.99: %.2f\n", + Percentile(50), Percentile(75), Percentile(99), Percentile(99.9), Percentile(99.99)); + r.append(buf); + r.append("------------------------------------------------------\n"); + const double mult = 100.0 / num_; + double sum = 0; + for (int b = 0; b < kNumBuckets; b++) { + if (buckets_[b] <= 0.0) + continue; + sum += buckets_[b]; + snprintf(buf, sizeof(buf), "[ %7.0f, %7.0f ) %7.0f %7.3f%% %7.3f%% ", + ((b == 0) ? 0.0 : kBucketLimit[b - 1]), // left + kBucketLimit[b], // right + buckets_[b], // count + mult * buckets_[b], // percentage + mult * sum); // cumulative percentage + r.append(buf); - // Add hash marks based on percentage; 20 marks for 100%. - int marks = static_cast(20*(buckets_[b] / num_) + 0.5); - r.append(marks, '#'); - r.push_back('\n'); - } - return r; + // Add hash marks based on percentage; 20 marks for 100%. + int marks = static_cast(20 * (buckets_[b] / num_) + 0.5); + r.append(marks, '#'); + r.push_back('\n'); + } + return r; } -} // namespace leveldb +} // namespace leveldb diff --git a/bench/util/histogram.h b/bench/util/histogram.h index 919c3f9..8616b6f 100644 --- a/bench/util/histogram.h +++ b/bench/util/histogram.h @@ -5,43 +5,46 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2017-2020, Intel Corporation - #ifndef STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ #define STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ #include -namespace leveldb { +namespace leveldb +{ class Histogram { - public: - Histogram() { } - ~Histogram() { } - - void Clear(); - void Add(double value); - void Merge(const Histogram& other); - - std::string ToString() const; - - double Median() const; - double Percentile(double p) const; - double Average() const; - double StandardDeviation() const; - - private: - double min_; - double max_; - double num_; - double sum_; - double sum_squares_; - - enum { kNumBuckets = 154 }; - static const double kBucketLimit[kNumBuckets]; - double buckets_[kNumBuckets]; - +public: + Histogram() + { + } + ~Histogram() + { + } + + void Clear(); + void Add(double value); + void Merge(const Histogram &other); + + std::string ToString() const; + + double Median() const; + double Percentile(double p) const; + double Average() const; + double StandardDeviation() const; + +private: + double min_; + double max_; + double num_; + double sum_; + double sum_squares_; + + enum { kNumBuckets = 154 }; + static const double kBucketLimit[kNumBuckets]; + double buckets_[kNumBuckets]; }; -} // namespace leveldb +} // namespace leveldb -#endif // STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ +#endif // STORAGE_LEVELDB_UTIL_HISTOGRAM_H_ diff --git a/bench/util/logging.cc b/bench/util/logging.cc index b20c5d1..60df067 100644 --- a/bench/util/logging.cc +++ b/bench/util/logging.cc @@ -2,72 +2,79 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2020, Intel Corporation */ + #include "util/logging.h" +#include "leveldb/env.h" +#include "leveldb/slice.h" #include #include #include #include -#include "leveldb/env.h" -#include "leveldb/slice.h" -namespace leveldb { +namespace leveldb +{ -void AppendNumberTo(std::string* str, uint64_t num) { - char buf[30]; - snprintf(buf, sizeof(buf), "%llu", (unsigned long long) num); - str->append(buf); +void AppendNumberTo(std::string *str, uint64_t num) +{ + char buf[30]; + snprintf(buf, sizeof(buf), "%llu", (unsigned long long)num); + str->append(buf); } -void AppendEscapedStringTo(std::string* str, const Slice& value) { - for (size_t i = 0; i < value.size(); i++) { - char c = value[i]; - if (c >= ' ' && c <= '~') { - str->push_back(c); - } else { - char buf[10]; - snprintf(buf, sizeof(buf), "\\x%02x", - static_cast(c) & 0xff); - str->append(buf); - } - } +void AppendEscapedStringTo(std::string *str, const Slice &value) +{ + for (size_t i = 0; i < value.size(); i++) { + char c = value[i]; + if (c >= ' ' && c <= '~') { + str->push_back(c); + } else { + char buf[10]; + snprintf(buf, sizeof(buf), "\\x%02x", static_cast(c) & 0xff); + str->append(buf); + } + } } -std::string NumberToString(uint64_t num) { - std::string r; - AppendNumberTo(&r, num); - return r; +std::string NumberToString(uint64_t num) +{ + std::string r; + AppendNumberTo(&r, num); + return r; } -std::string EscapeString(const Slice& value) { - std::string r; - AppendEscapedStringTo(&r, value); - return r; +std::string EscapeString(const Slice &value) +{ + std::string r; + AppendEscapedStringTo(&r, value); + return r; } -bool ConsumeDecimalNumber(Slice* in, uint64_t* val) { - uint64_t v = 0; - int digits = 0; - while (!in->empty()) { - char c = (*in)[0]; - if (c >= '0' && c <= '9') { - ++digits; - // |delta| intentionally unit64_t to avoid Android crash (see log). - const uint64_t delta = (c - '0'); - static const uint64_t kMaxUint64 = ~static_cast(0); - if (v > kMaxUint64/10 || - (v == kMaxUint64/10 && delta > kMaxUint64%10)) { - // Overflow - return false; - } - v = (v * 10) + delta; - in->remove_prefix(1); - } else { - break; - } - } - *val = v; - return (digits > 0); +bool ConsumeDecimalNumber(Slice *in, uint64_t *val) +{ + uint64_t v = 0; + int digits = 0; + while (!in->empty()) { + char c = (*in)[0]; + if (c >= '0' && c <= '9') { + ++digits; + // |delta| intentionally unit64_t to avoid Android crash (see log). + const uint64_t delta = (c - '0'); + static const uint64_t kMaxUint64 = ~static_cast(0); + if (v > kMaxUint64 / 10 || (v == kMaxUint64 / 10 && delta > kMaxUint64 % 10)) { + // Overflow + return false; + } + v = (v * 10) + delta; + in->remove_prefix(1); + } else { + break; + } + } + *val = v; + return (digits > 0); } -} // namespace leveldb +} // namespace leveldb diff --git a/bench/util/logging.h b/bench/util/logging.h index 0ac6566..6d3f7b8 100644 --- a/bench/util/logging.h +++ b/bench/util/logging.h @@ -1,43 +1,47 @@ // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. -// + +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020, Intel Corporation + // Must not be included from any .h files to avoid polluting the namespace // with macros. #ifndef STORAGE_LEVELDB_UTIL_LOGGING_H_ #define STORAGE_LEVELDB_UTIL_LOGGING_H_ -#include +#include "port/port_posix.h" #include +#include #include -#include "port/port_posix.h" -namespace leveldb { +namespace leveldb +{ class Slice; class WritableFile; // Append a human-readable printout of "num" to *str -extern void AppendNumberTo(std::string* str, uint64_t num); +extern void AppendNumberTo(std::string *str, uint64_t num); // Append a human-readable printout of "value" to *str. // Escapes any non-printable characters found in "value". -extern void AppendEscapedStringTo(std::string* str, const Slice& value); +extern void AppendEscapedStringTo(std::string *str, const Slice &value); // Return a human-readable printout of "num" extern std::string NumberToString(uint64_t num); // Return a human-readable version of "value". // Escapes any non-printable characters found in "value". -extern std::string EscapeString(const Slice& value); +extern std::string EscapeString(const Slice &value); // Parse a human-readable number from "*in" into *value. On success, // advances "*in" past the consumed number and sets "*val" to the // numeric value. Otherwise, returns false and leaves *in in an // unspecified state. -extern bool ConsumeDecimalNumber(Slice* in, uint64_t* val); +extern bool ConsumeDecimalNumber(Slice *in, uint64_t *val); -} // namespace leveldb +} // namespace leveldb -#endif // STORAGE_LEVELDB_UTIL_LOGGING_H_ +#endif // STORAGE_LEVELDB_UTIL_LOGGING_H_ diff --git a/bench/util/mutexlock.h b/bench/util/mutexlock.h index 2fa3ce0..ac1c0bb 100644 --- a/bench/util/mutexlock.h +++ b/bench/util/mutexlock.h @@ -2,13 +2,17 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020, Intel Corporation + #ifndef STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ #define STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ #include "port/port_posix.h" #include "port/thread_annotations.h" -namespace leveldb { +namespace leveldb +{ // Helper class that locks a mutex on construction and unlocks the mutex when // the destructor of the MutexLock object is invoked. @@ -21,21 +25,23 @@ namespace leveldb { // } class SCOPED_LOCKABLE MutexLock { - public: - explicit MutexLock(port::Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) - : mu_(mu) { - this->mu_->Lock(); - } - ~MutexLock() UNLOCK_FUNCTION() { this->mu_->Unlock(); } - - private: - port::Mutex *const mu_; - // No copying allowed - MutexLock(const MutexLock&); - void operator=(const MutexLock&); +public: + explicit MutexLock(port::Mutex *mu) EXCLUSIVE_LOCK_FUNCTION(mu) : mu_(mu) + { + this->mu_->Lock(); + } + ~MutexLock() UNLOCK_FUNCTION() + { + this->mu_->Unlock(); + } + +private: + port::Mutex *const mu_; + // No copying allowed + MutexLock(const MutexLock &); + void operator=(const MutexLock &); }; -} // namespace leveldb - +} // namespace leveldb -#endif // STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ +#endif // STORAGE_LEVELDB_UTIL_MUTEXLOCK_H_ diff --git a/bench/util/posix_logger.h b/bench/util/posix_logger.h index a6e9755..3eb15f6 100644 --- a/bench/util/posix_logger.h +++ b/bench/util/posix_logger.h @@ -1,6 +1,10 @@ // Copyright (c) 2011 The LevelDB Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. + +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020, Intel Corporation + // // Logger implementation that can be shared by all environments // where enough posix functionality is available. @@ -8,91 +12,90 @@ #ifndef STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ #define STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ +#include "leveldb/env.h" #include #include #include #include -#include "leveldb/env.h" -namespace leveldb { +namespace leveldb +{ class PosixLogger : public Logger { - private: - FILE* file_; - uint64_t (*gettid_)(); // Return the thread id for the current thread - public: - PosixLogger(FILE* f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) { } - virtual ~PosixLogger() { - fclose(file_); - } - virtual void Logv(const char* format, va_list ap) { - const uint64_t thread_id = (*gettid_)(); +private: + FILE *file_; + uint64_t (*gettid_)(); // Return the thread id for the current thread +public: + PosixLogger(FILE *f, uint64_t (*gettid)()) : file_(f), gettid_(gettid) + { + } + virtual ~PosixLogger() + { + fclose(file_); + } + virtual void Logv(const char *format, va_list ap) + { + const uint64_t thread_id = (*gettid_)(); - // We try twice: the first time with a fixed-size stack allocated buffer, - // and the second time with a much larger dynamically allocated buffer. - char buffer[500]; - for (int iter = 0; iter < 2; iter++) { - char* base; - int bufsize; - if (iter == 0) { - bufsize = sizeof(buffer); - base = buffer; - } else { - bufsize = 30000; - base = new char[bufsize]; - } - char* p = base; - char* limit = base + bufsize; + // We try twice: the first time with a fixed-size stack allocated buffer, + // and the second time with a much larger dynamically allocated buffer. + char buffer[500]; + for (int iter = 0; iter < 2; iter++) { + char *base; + int bufsize; + if (iter == 0) { + bufsize = sizeof(buffer); + base = buffer; + } else { + bufsize = 30000; + base = new char[bufsize]; + } + char *p = base; + char *limit = base + bufsize; - struct timeval now_tv; - gettimeofday(&now_tv, NULL); - const time_t seconds = now_tv.tv_sec; - struct tm t; - localtime_r(&seconds, &t); - p += snprintf(p, limit - p, - "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", - t.tm_year + 1900, - t.tm_mon + 1, - t.tm_mday, - t.tm_hour, - t.tm_min, - t.tm_sec, - static_cast(now_tv.tv_usec), - static_cast(thread_id)); + struct timeval now_tv; + gettimeofday(&now_tv, NULL); + const time_t seconds = now_tv.tv_sec; + struct tm t; + localtime_r(&seconds, &t); + p += snprintf(p, limit - p, "%04d/%02d/%02d-%02d:%02d:%02d.%06d %llx ", + t.tm_year + 1900, t.tm_mon + 1, t.tm_mday, t.tm_hour, t.tm_min, + t.tm_sec, static_cast(now_tv.tv_usec), + static_cast(thread_id)); - // Print the message - if (p < limit) { - va_list backup_ap; - va_copy(backup_ap, ap); - p += vsnprintf(p, limit - p, format, backup_ap); - va_end(backup_ap); - } + // Print the message + if (p < limit) { + va_list backup_ap; + va_copy(backup_ap, ap); + p += vsnprintf(p, limit - p, format, backup_ap); + va_end(backup_ap); + } - // Truncate to available space if necessary - if (p >= limit) { - if (iter == 0) { - continue; // Try again with larger buffer - } else { - p = limit - 1; - } - } + // Truncate to available space if necessary + if (p >= limit) { + if (iter == 0) { + continue; // Try again with larger buffer + } else { + p = limit - 1; + } + } - // Add newline if necessary - if (p == base || p[-1] != '\n') { - *p++ = '\n'; - } + // Add newline if necessary + if (p == base || p[-1] != '\n') { + *p++ = '\n'; + } - assert(p <= limit); - fwrite(base, 1, p - base, file_); - fflush(file_); - if (base != buffer) { - delete[] base; - } - break; - } - } + assert(p <= limit); + fwrite(base, 1, p - base, file_); + fflush(file_); + if (base != buffer) { + delete[] base; + } + break; + } + } }; -} // namespace leveldb +} // namespace leveldb -#endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ +#endif // STORAGE_LEVELDB_UTIL_POSIX_LOGGER_H_ diff --git a/bench/util/random.h b/bench/util/random.h index 7882bc5..6518417 100644 --- a/bench/util/random.h +++ b/bench/util/random.h @@ -2,63 +2,77 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020, Intel Corporation + #ifndef STORAGE_LEVELDB_UTIL_RANDOM_H_ #define STORAGE_LEVELDB_UTIL_RANDOM_H_ #include -namespace leveldb { +namespace leveldb +{ // A very simple random number generator. Not especially good at // generating truly random bits, but good enough for our needs in this // package. class Random { - private: - uint32_t seed_; - public: - explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) { - // Avoid bad seeds. - if (seed_ == 0 || seed_ == 2147483647L) { - seed_ = 1; - } - } - uint32_t Next() { - static const uint32_t M = 2147483647L; // 2^31-1 - static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 - // We are computing - // seed_ = (seed_ * A) % M, where M = 2^31-1 - // - // seed_ must not be zero or M, or else all subsequent computed values - // will be zero or M respectively. For all other values, seed_ will end - // up cycling through every number in [1,M-1] - uint64_t product = seed_ * A; +private: + uint32_t seed_; + +public: + explicit Random(uint32_t s) : seed_(s & 0x7fffffffu) + { + // Avoid bad seeds. + if (seed_ == 0 || seed_ == 2147483647L) { + seed_ = 1; + } + } + uint32_t Next() + { + static const uint32_t M = 2147483647L; // 2^31-1 + static const uint64_t A = 16807; // bits 14, 8, 7, 5, 2, 1, 0 + // We are computing + // seed_ = (seed_ * A) % M, where M = 2^31-1 + // + // seed_ must not be zero or M, or else all subsequent computed values + // will be zero or M respectively. For all other values, seed_ will end + // up cycling through every number in [1,M-1] + uint64_t product = seed_ * A; - // Compute (product % M) using the fact that ((x << 31) % M) == x. - seed_ = static_cast((product >> 31) + (product & M)); - // The first reduction may overflow by 1 bit, so we may need to - // repeat. mod == M is not possible; using > allows the faster - // sign-bit-based test. - if (seed_ > M) { - seed_ -= M; - } - return seed_; - } - // Returns a uniformly distributed value in the range [0..n-1] - // REQUIRES: n > 0 - uint32_t Uniform(int n) { return Next() % n; } + // Compute (product % M) using the fact that ((x << 31) % M) == x. + seed_ = static_cast((product >> 31) + (product & M)); + // The first reduction may overflow by 1 bit, so we may need to + // repeat. mod == M is not possible; using > allows the faster + // sign-bit-based test. + if (seed_ > M) { + seed_ -= M; + } + return seed_; + } + // Returns a uniformly distributed value in the range [0..n-1] + // REQUIRES: n > 0 + uint32_t Uniform(int n) + { + return Next() % n; + } - // Randomly returns true ~"1/n" of the time, and false otherwise. - // REQUIRES: n > 0 - bool OneIn(int n) { return (Next() % n) == 0; } + // Randomly returns true ~"1/n" of the time, and false otherwise. + // REQUIRES: n > 0 + bool OneIn(int n) + { + return (Next() % n) == 0; + } - // Skewed: pick "base" uniformly from range [0,max_log] and then - // return "base" random bits. The effect is to pick a number in the - // range [0,2^max_log-1] with exponential bias towards smaller numbers. - uint32_t Skewed(int max_log) { - return Uniform(1 << Uniform(max_log + 1)); - } + // Skewed: pick "base" uniformly from range [0,max_log] and then + // return "base" random bits. The effect is to pick a number in the + // range [0,2^max_log-1] with exponential bias towards smaller numbers. + uint32_t Skewed(int max_log) + { + return Uniform(1 << Uniform(max_log + 1)); + } }; -} // namespace leveldb +} // namespace leveldb -#endif // STORAGE_LEVELDB_UTIL_RANDOM_H_ +#endif // STORAGE_LEVELDB_UTIL_RANDOM_H_ diff --git a/bench/util/status.cc b/bench/util/status.cc index 9c590be..de3eecf 100644 --- a/bench/util/status.cc +++ b/bench/util/status.cc @@ -2,74 +2,80 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. -#include -#include "port/port_posix.h" +// SPDX-License-Identifier: Apache-2.0 +/* Copyright 2020, Intel Corporation */ + #include "leveldb/status.h" +#include "port/port_posix.h" +#include -namespace leveldb { +namespace leveldb +{ -const char* Status::CopyState(const char* state) { - uint32_t size; - memcpy(&size, state, sizeof(size)); - char* result = new char[size + 5]; - memcpy(result, state, size + 5); - return result; +const char *Status::CopyState(const char *state) +{ + uint32_t size; + memcpy(&size, state, sizeof(size)); + char *result = new char[size + 5]; + memcpy(result, state, size + 5); + return result; } -Status::Status(Code code, const Slice& msg, const Slice& msg2) { - assert(code != kOk); - const uint32_t len1 = msg.size(); - const uint32_t len2 = msg2.size(); - const uint32_t size = len1 + (len2 ? (2 + len2) : 0); - char* result = new char[size + 5]; - memcpy(result, &size, sizeof(size)); - result[4] = static_cast(code); - memcpy(result + 5, msg.data(), len1); - if (len2) { - result[5 + len1] = ':'; - result[6 + len1] = ' '; - memcpy(result + 7 + len1, msg2.data(), len2); - } - state_ = result; +Status::Status(Code code, const Slice &msg, const Slice &msg2) +{ + assert(code != kOk); + const uint32_t len1 = msg.size(); + const uint32_t len2 = msg2.size(); + const uint32_t size = len1 + (len2 ? (2 + len2) : 0); + char *result = new char[size + 5]; + memcpy(result, &size, sizeof(size)); + result[4] = static_cast(code); + memcpy(result + 5, msg.data(), len1); + if (len2) { + result[5 + len1] = ':'; + result[6 + len1] = ' '; + memcpy(result + 7 + len1, msg2.data(), len2); + } + state_ = result; } -std::string Status::ToString() const { - if (state_ == NULL) { - return "OK"; - } else { - char tmp[30]; - const char* type; - switch (code()) { - case kOk: - type = "OK"; - break; - case kNotFound: - type = "NotFound: "; - break; - case kCorruption: - type = "Corruption: "; - break; - case kNotSupported: - type = "Not implemented: "; - break; - case kInvalidArgument: - type = "Invalid argument: "; - break; - case kIOError: - type = "IO error: "; - break; - default: - snprintf(tmp, sizeof(tmp), "Unknown code(%d): ", - static_cast(code())); - type = tmp; - break; - } - std::string result(type); - uint32_t length; - memcpy(&length, state_, sizeof(length)); - result.append(state_ + 5, length); - return result; - } +std::string Status::ToString() const +{ + if (state_ == NULL) { + return "OK"; + } else { + char tmp[30]; + const char *type; + switch (code()) { + case kOk: + type = "OK"; + break; + case kNotFound: + type = "NotFound: "; + break; + case kCorruption: + type = "Corruption: "; + break; + case kNotSupported: + type = "Not implemented: "; + break; + case kInvalidArgument: + type = "Invalid argument: "; + break; + case kIOError: + type = "IO error: "; + break; + default: + snprintf(tmp, sizeof(tmp), "Unknown code(%d): ", static_cast(code())); + type = tmp; + break; + } + std::string result(type); + uint32_t length; + memcpy(&length, state_, sizeof(length)); + result.append(state_ + 5, length); + return result; + } } -} // namespace leveldb +} // namespace leveldb diff --git a/bench/util/testutil.cc b/bench/util/testutil.cc index 08de5d7..2ba18d5 100644 --- a/bench/util/testutil.cc +++ b/bench/util/testutil.cc @@ -6,46 +6,48 @@ #include "util/random.h" -namespace leveldb { -namespace test { - -Slice RandomString(Random* rnd, int len, std::string* dst) { - dst->resize(len); - for (int i = 0; i < len; i++) { - (*dst)[i] = static_cast(' ' + rnd->Uniform(95)); // ' ' .. '~' - } - return Slice(*dst); +namespace leveldb +{ +namespace test +{ + +Slice RandomString(Random *rnd, int len, std::string *dst) +{ + dst->resize(len); + for (int i = 0; i < len; i++) { + (*dst)[i] = static_cast(' ' + rnd->Uniform(95)); // ' ' .. '~' + } + return Slice(*dst); } -std::string RandomKey(Random* rnd, int len) { - // Make sure to generate a wide variety of characters so we - // test the boundary conditions for short-key optimizations. - static const char kTestChars[] = { - '\0', '\1', 'a', 'b', 'c', 'd', 'e', '\xfd', '\xfe', '\xff' - }; - std::string result; - for (int i = 0; i < len; i++) { - result += kTestChars[rnd->Uniform(sizeof(kTestChars))]; - } - return result; +std::string RandomKey(Random *rnd, int len) +{ + // Make sure to generate a wide variety of characters so we + // test the boundary conditions for short-key optimizations. + static const char kTestChars[] = {'\0', '\1', 'a', 'b', 'c', 'd', 'e', '\xfd', '\xfe', '\xff'}; + std::string result; + for (int i = 0; i < len; i++) { + result += kTestChars[rnd->Uniform(sizeof(kTestChars))]; + } + return result; } - -Slice CompressibleString(Random* rnd, double compressed_fraction, - size_t len, std::string* dst) { - int raw = static_cast(len * compressed_fraction); - if (raw < 1) raw = 1; - std::string raw_data; - RandomString(rnd, raw, &raw_data); - - // Duplicate the random data until we have filled "len" bytes - dst->clear(); - while (dst->size() < len) { - dst->append(raw_data); - } - dst->resize(len); - return Slice(*dst); +Slice CompressibleString(Random *rnd, double compressed_fraction, size_t len, std::string *dst) +{ + int raw = static_cast(len * compressed_fraction); + if (raw < 1) + raw = 1; + std::string raw_data; + RandomString(rnd, raw, &raw_data); + + // Duplicate the random data until we have filled "len" bytes + dst->clear(); + while (dst->size() < len) { + dst->append(raw_data); + } + dst->resize(len); + return Slice(*dst); } -} // namespace test -} // namespace leveldb +} // namespace test +} // namespace leveldb diff --git a/bench/util/testutil.h b/bench/util/testutil.h index b35c949..0cd0b39 100644 --- a/bench/util/testutil.h +++ b/bench/util/testutil.h @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE-BSD file. See the AUTHORS file for names of contributors. +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2020, Intel Corporation + #ifndef STORAGE_LEVELDB_UTIL_TESTUTIL_H_ #define STORAGE_LEVELDB_UTIL_TESTUTIL_H_ @@ -9,55 +12,56 @@ #include "leveldb/slice.h" #include "util/random.h" -namespace leveldb { -namespace test { +namespace leveldb +{ +namespace test +{ // Store in *dst a random string of length "len" and return a Slice that // references the generated data. -Slice RandomString(Random* rnd, int len, std::string* dst); +Slice RandomString(Random *rnd, int len, std::string *dst); // Return a random key with the specified length that may contain interesting // characters (e.g. \x00, \xff, etc.). -std::string RandomKey(Random* rnd, int len); +std::string RandomKey(Random *rnd, int len); // Store in *dst a string of length "len" that will compress to // "N*compressed_fraction" bytes and return a Slice that references // the generated data. -Slice CompressibleString(Random* rnd, double compressed_fraction, - size_t len, std::string* dst); +Slice CompressibleString(Random *rnd, double compressed_fraction, size_t len, std::string *dst); // A wrapper that allows injection of errors. class ErrorEnv : public EnvWrapper { - public: - bool writable_file_error_; - int num_writable_file_errors_; - - ErrorEnv() : EnvWrapper(Env::Default()), - writable_file_error_(false), - num_writable_file_errors_(0) { } - - virtual Status NewWritableFile(const std::string& fname, - WritableFile** result) { - if (writable_file_error_) { - ++num_writable_file_errors_; - *result = nullptr; - return Status::IOError(fname, "fake error"); - } - return target()->NewWritableFile(fname, result); - } - - virtual Status NewAppendableFile(const std::string& fname, - WritableFile** result) { - if (writable_file_error_) { - ++num_writable_file_errors_; - *result = nullptr; - return Status::IOError(fname, "fake error"); - } - return target()->NewAppendableFile(fname, result); - } +public: + bool writable_file_error_; + int num_writable_file_errors_; + + ErrorEnv() : EnvWrapper(Env::Default()), writable_file_error_(false), num_writable_file_errors_(0) + { + } + + virtual Status NewWritableFile(const std::string &fname, WritableFile **result) + { + if (writable_file_error_) { + ++num_writable_file_errors_; + *result = nullptr; + return Status::IOError(fname, "fake error"); + } + return target()->NewWritableFile(fname, result); + } + + virtual Status NewAppendableFile(const std::string &fname, WritableFile **result) + { + if (writable_file_error_) { + ++num_writable_file_errors_; + *result = nullptr; + return Status::IOError(fname, "fake error"); + } + return target()->NewAppendableFile(fname, result); + } }; -} // namespace test -} // namespace leveldb +} // namespace test +} // namespace leveldb -#endif // STORAGE_LEVELDB_UTIL_TESTUTIL_H_ +#endif // STORAGE_LEVELDB_UTIL_TESTUTIL_H_ From e52526cfa1e010affb40af147da064baaffba3c1 Mon Sep 17 00:00:00 2001 From: Pawel Karczewski Date: Thu, 17 Dec 2020 13:56:16 +0100 Subject: [PATCH 5/5] Add ignore revs file usage: ''' git blame --ignore-revs-file .git-blame-ignore-revs ''' --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .git-blame-ignore-revs diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs new file mode 100644 index 0000000..c58ffe5 --- /dev/null +++ b/.git-blame-ignore-revs @@ -0,0 +1,2 @@ +# Format c++ code with clang-format +b6642350ba8921fe04a0eb56190c538cf60e7743