| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308 |
- // Copyright (c) 2013, Facebook, Inc. All rights reserved.
- // This source code is licensed under both the GPLv2 (found in the
- // COPYING file in the root directory) and Apache 2.0 License
- // (found in the LICENSE.Apache file in the root directory).
- //
- #if !defined(OS_WIN) && !defined(ROCKSDB_LITE)
- #ifndef GFLAGS
- #include <cstdio>
- int main() { fprintf(stderr, "Please install gflags to run tools\n"); }
- #else
- #include <atomic>
- #include <functional>
- #include <string>
- #include <unordered_map>
- #include <unistd.h>
- #include <sys/time.h>
- #include "port/port_posix.h"
- #include "rocksdb/env.h"
- #include "util/gflags_compat.h"
- #include "util/mutexlock.h"
- #include "util/random.h"
- #include "utilities/persistent_cache/hash_table.h"
- using std::string;
- DEFINE_int32(nsec, 10, "nsec");
- DEFINE_int32(nthread_write, 1, "insert %");
- DEFINE_int32(nthread_read, 1, "lookup %");
- DEFINE_int32(nthread_erase, 1, "erase %");
- namespace ROCKSDB_NAMESPACE {
- //
- // HashTableImpl interface
- //
- // Abstraction of a hash table implementation
- template <class Key, class Value>
- class HashTableImpl {
- public:
- virtual ~HashTableImpl() {}
- virtual bool Insert(const Key& key, const Value& val) = 0;
- virtual bool Erase(const Key& key) = 0;
- virtual bool Lookup(const Key& key, Value* val) = 0;
- };
- // HashTableBenchmark
- //
- // Abstraction to test a given hash table implementation. The test mostly
- // focus on insert, lookup and erase. The test can operate in test mode and
- // benchmark mode.
- class HashTableBenchmark {
- public:
- explicit HashTableBenchmark(HashTableImpl<size_t, std::string>* impl,
- const size_t sec = 10,
- const size_t nthread_write = 1,
- const size_t nthread_read = 1,
- const size_t nthread_erase = 1)
- : impl_(impl),
- sec_(sec),
- ninserts_(0),
- nreads_(0),
- nerases_(0),
- nerases_failed_(0),
- quit_(false) {
- Prepop();
- StartThreads(nthread_write, WriteMain);
- StartThreads(nthread_read, ReadMain);
- StartThreads(nthread_erase, EraseMain);
- uint64_t start = NowInMillSec();
- while (!quit_) {
- quit_ = NowInMillSec() - start > sec_ * 1000;
- /* sleep override */ sleep(1);
- }
- Env* env = Env::Default();
- env->WaitForJoin();
- if (sec_) {
- printf("Result \n");
- printf("====== \n");
- printf("insert/sec = %f \n", ninserts_ / static_cast<double>(sec_));
- printf("read/sec = %f \n", nreads_ / static_cast<double>(sec_));
- printf("erases/sec = %f \n", nerases_ / static_cast<double>(sec_));
- const uint64_t ops = ninserts_ + nreads_ + nerases_;
- printf("ops/sec = %f \n", ops / static_cast<double>(sec_));
- printf("erase fail = %d (%f%%)\n", static_cast<int>(nerases_failed_),
- static_cast<float>(nerases_failed_ / nerases_ * 100));
- printf("====== \n");
- }
- }
- void RunWrite() {
- while (!quit_) {
- size_t k = insert_key_++;
- std::string tmp(1000, k % 255);
- bool status = impl_->Insert(k, tmp);
- assert(status);
- ninserts_++;
- }
- }
- void RunRead() {
- Random64 rgen(time(nullptr));
- while (!quit_) {
- std::string s;
- size_t k = rgen.Next() % max_prepop_key;
- bool status = impl_->Lookup(k, &s);
- assert(status);
- assert(s == std::string(1000, k % 255));
- nreads_++;
- }
- }
- void RunErase() {
- while (!quit_) {
- size_t k = erase_key_++;
- bool status = impl_->Erase(k);
- nerases_failed_ += !status;
- nerases_++;
- }
- }
- private:
- // Start threads for a given function
- void StartThreads(const size_t n, void (*fn)(void*)) {
- Env* env = Env::Default();
- for (size_t i = 0; i < n; ++i) {
- env->StartThread(fn, this);
- }
- }
- // Prepop the hash table with 1M keys
- void Prepop() {
- for (size_t i = 0; i < max_prepop_key; ++i) {
- bool status = impl_->Insert(i, std::string(1000, i % 255));
- assert(status);
- }
- erase_key_ = insert_key_ = max_prepop_key;
- for (size_t i = 0; i < 10 * max_prepop_key; ++i) {
- bool status = impl_->Insert(insert_key_++, std::string(1000, 'x'));
- assert(status);
- }
- }
- static uint64_t NowInMillSec() {
- timeval tv;
- gettimeofday(&tv, /*tz=*/nullptr);
- return tv.tv_sec * 1000 + tv.tv_usec / 1000;
- }
- //
- // Wrapper functions for thread entry
- //
- static void WriteMain(void* args) {
- reinterpret_cast<HashTableBenchmark*>(args)->RunWrite();
- }
- static void ReadMain(void* args) {
- reinterpret_cast<HashTableBenchmark*>(args)->RunRead();
- }
- static void EraseMain(void* args) {
- reinterpret_cast<HashTableBenchmark*>(args)->RunErase();
- }
- HashTableImpl<size_t, std::string>* impl_; // Implementation to test
- const size_t sec_; // Test time
- const size_t max_prepop_key = 1ULL * 1024 * 1024; // Max prepop key
- std::atomic<size_t> insert_key_; // Last inserted key
- std::atomic<size_t> erase_key_; // Erase key
- std::atomic<size_t> ninserts_; // Number of inserts
- std::atomic<size_t> nreads_; // Number of reads
- std::atomic<size_t> nerases_; // Number of erases
- std::atomic<size_t> nerases_failed_; // Number of erases failed
- bool quit_; // Should the threads quit ?
- };
- //
- // SimpleImpl
- // Lock safe unordered_map implementation
- class SimpleImpl : public HashTableImpl<size_t, string> {
- public:
- bool Insert(const size_t& key, const string& val) override {
- WriteLock _(&rwlock_);
- map_.insert(make_pair(key, val));
- return true;
- }
- bool Erase(const size_t& key) override {
- WriteLock _(&rwlock_);
- auto it = map_.find(key);
- if (it == map_.end()) {
- return false;
- }
- map_.erase(it);
- return true;
- }
- bool Lookup(const size_t& key, string* val) override {
- ReadLock _(&rwlock_);
- auto it = map_.find(key);
- if (it != map_.end()) {
- *val = it->second;
- }
- return it != map_.end();
- }
- private:
- port::RWMutex rwlock_;
- std::unordered_map<size_t, string> map_;
- };
- //
- // GranularLockImpl
- // Thread safe custom RocksDB implementation of hash table with granular
- // locking
- class GranularLockImpl : public HashTableImpl<size_t, string> {
- public:
- bool Insert(const size_t& key, const string& val) override {
- Node n(key, val);
- return impl_.Insert(n);
- }
- bool Erase(const size_t& key) override {
- Node n(key, string());
- return impl_.Erase(n, nullptr);
- }
- bool Lookup(const size_t& key, string* val) override {
- Node n(key, string());
- port::RWMutex* rlock;
- bool status = impl_.Find(n, &n, &rlock);
- if (status) {
- ReadUnlock _(rlock);
- *val = n.val_;
- }
- return status;
- }
- private:
- struct Node {
- explicit Node(const size_t key, const string& val) : key_(key), val_(val) {}
- size_t key_ = 0;
- string val_;
- };
- struct Hash {
- uint64_t operator()(const Node& node) {
- return std::hash<uint64_t>()(node.key_);
- }
- };
- struct Equal {
- bool operator()(const Node& lhs, const Node& rhs) {
- return lhs.key_ == rhs.key_;
- }
- };
- HashTable<Node, Hash, Equal> impl_;
- };
- } // namespace ROCKSDB_NAMESPACE
- //
- // main
- //
- int main(int argc, char** argv) {
- GFLAGS_NAMESPACE::SetUsageMessage(std::string("\nUSAGE:\n") +
- std::string(argv[0]) + " [OPTIONS]...");
- GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc, &argv, false);
- //
- // Micro benchmark unordered_map
- //
- printf("Micro benchmarking std::unordered_map \n");
- {
- ROCKSDB_NAMESPACE::SimpleImpl impl;
- ROCKSDB_NAMESPACE::HashTableBenchmark _(
- &impl, FLAGS_nsec, FLAGS_nthread_write, FLAGS_nthread_read,
- FLAGS_nthread_erase);
- }
- //
- // Micro benchmark scalable hash table
- //
- printf("Micro benchmarking scalable hash map \n");
- {
- ROCKSDB_NAMESPACE::GranularLockImpl impl;
- ROCKSDB_NAMESPACE::HashTableBenchmark _(
- &impl, FLAGS_nsec, FLAGS_nthread_write, FLAGS_nthread_read,
- FLAGS_nthread_erase);
- }
- return 0;
- }
- #endif // #ifndef GFLAGS
- #else
- int main(int /*argc*/, char** /*argv*/) { return 0; }
- #endif
|