]>
git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/utilities/persistent_cache/hash_table_bench.cc
1 // Copyright (c) 2013, Facebook, Inc. All rights reserved.
2 // This source code is licensed under both the GPLv2 (found in the
3 // COPYING file in the root directory) and Apache 2.0 License
4 // (found in the LICENSE.Apache file in the root directory).
7 #if !defined(OS_WIN) && !defined(ROCKSDB_LITE)
11 int main() { fprintf(stderr
, "Please install gflags to run tools\n"); }
20 #include <unordered_map>
22 #include "port/port_posix.h"
23 #include "port/sys_time.h"
24 #include "rocksdb/env.h"
25 #include "util/gflags_compat.h"
26 #include "util/mutexlock.h"
27 #include "util/random.h"
28 #include "utilities/persistent_cache/hash_table.h"
32 DEFINE_int32(nsec
, 10, "nsec");
33 DEFINE_int32(nthread_write
, 1, "insert %");
34 DEFINE_int32(nthread_read
, 1, "lookup %");
35 DEFINE_int32(nthread_erase
, 1, "erase %");
37 namespace ROCKSDB_NAMESPACE
{
40 // HashTableImpl interface
42 // Abstraction of a hash table implementation
43 template <class Key
, class Value
>
46 virtual ~HashTableImpl() {}
48 virtual bool Insert(const Key
& key
, const Value
& val
) = 0;
49 virtual bool Erase(const Key
& key
) = 0;
50 virtual bool Lookup(const Key
& key
, Value
* val
) = 0;
55 // Abstraction to test a given hash table implementation. The test mostly
56 // focus on insert, lookup and erase. The test can operate in test mode and
58 class HashTableBenchmark
{
60 explicit HashTableBenchmark(HashTableImpl
<size_t, std::string
>* impl
,
61 const size_t sec
= 10,
62 const size_t nthread_write
= 1,
63 const size_t nthread_read
= 1,
64 const size_t nthread_erase
= 1)
74 StartThreads(nthread_write
, WriteMain
);
75 StartThreads(nthread_read
, ReadMain
);
76 StartThreads(nthread_erase
, EraseMain
);
78 uint64_t start
= NowInMillSec();
80 quit_
= NowInMillSec() - start
> sec_
* 1000;
81 /* sleep override */ sleep(1);
84 Env
* env
= Env::Default();
90 printf("insert/sec = %f \n", ninserts_
/ static_cast<double>(sec_
));
91 printf("read/sec = %f \n", nreads_
/ static_cast<double>(sec_
));
92 printf("erases/sec = %f \n", nerases_
/ static_cast<double>(sec_
));
93 const uint64_t ops
= ninserts_
+ nreads_
+ nerases_
;
94 printf("ops/sec = %f \n", ops
/ static_cast<double>(sec_
));
95 printf("erase fail = %d (%f%%)\n", static_cast<int>(nerases_failed_
),
96 static_cast<float>(nerases_failed_
/ nerases_
* 100));
103 size_t k
= insert_key_
++;
104 std::string
tmp(1000, k
% 255);
105 bool status
= impl_
->Insert(k
, tmp
);
112 Random64
rgen(time(nullptr));
115 size_t k
= rgen
.Next() % max_prepop_key
;
116 bool status
= impl_
->Lookup(k
, &s
);
118 assert(s
== std::string(1000, k
% 255));
125 size_t k
= erase_key_
++;
126 bool status
= impl_
->Erase(k
);
127 nerases_failed_
+= !status
;
133 // Start threads for a given function
134 void StartThreads(const size_t n
, void (*fn
)(void*)) {
135 Env
* env
= Env::Default();
136 for (size_t i
= 0; i
< n
; ++i
) {
137 env
->StartThread(fn
, this);
141 // Prepop the hash table with 1M keys
143 for (size_t i
= 0; i
< max_prepop_key
; ++i
) {
144 bool status
= impl_
->Insert(i
, std::string(1000, i
% 255));
148 erase_key_
= insert_key_
= max_prepop_key
;
150 for (size_t i
= 0; i
< 10 * max_prepop_key
; ++i
) {
151 bool status
= impl_
->Insert(insert_key_
++, std::string(1000, 'x'));
156 static uint64_t NowInMillSec() {
158 port::GetTimeOfDay(&tv
, /*tz=*/nullptr);
159 return tv
.tv_sec
* 1000 + tv
.tv_usec
/ 1000;
163 // Wrapper functions for thread entry
165 static void WriteMain(void* args
) {
166 reinterpret_cast<HashTableBenchmark
*>(args
)->RunWrite();
169 static void ReadMain(void* args
) {
170 reinterpret_cast<HashTableBenchmark
*>(args
)->RunRead();
173 static void EraseMain(void* args
) {
174 reinterpret_cast<HashTableBenchmark
*>(args
)->RunErase();
177 HashTableImpl
<size_t, std::string
>* impl_
; // Implementation to test
178 const size_t sec_
; // Test time
179 const size_t max_prepop_key
= 1ULL * 1024 * 1024; // Max prepop key
180 std::atomic
<size_t> insert_key_
; // Last inserted key
181 std::atomic
<size_t> erase_key_
; // Erase key
182 std::atomic
<size_t> ninserts_
; // Number of inserts
183 std::atomic
<size_t> nreads_
; // Number of reads
184 std::atomic
<size_t> nerases_
; // Number of erases
185 std::atomic
<size_t> nerases_failed_
; // Number of erases failed
186 bool quit_
; // Should the threads quit ?
191 // Lock safe unordered_map implementation
192 class SimpleImpl
: public HashTableImpl
<size_t, string
> {
194 bool Insert(const size_t& key
, const string
& val
) override
{
195 WriteLock
_(&rwlock_
);
196 map_
.insert(make_pair(key
, val
));
200 bool Erase(const size_t& key
) override
{
201 WriteLock
_(&rwlock_
);
202 auto it
= map_
.find(key
);
203 if (it
== map_
.end()) {
210 bool Lookup(const size_t& key
, string
* val
) override
{
211 ReadLock
_(&rwlock_
);
212 auto it
= map_
.find(key
);
213 if (it
!= map_
.end()) {
216 return it
!= map_
.end();
220 port::RWMutex rwlock_
;
221 std::unordered_map
<size_t, string
> map_
;
226 // Thread safe custom RocksDB implementation of hash table with granular
228 class GranularLockImpl
: public HashTableImpl
<size_t, string
> {
230 bool Insert(const size_t& key
, const string
& val
) override
{
232 return impl_
.Insert(n
);
235 bool Erase(const size_t& key
) override
{
236 Node
n(key
, string());
237 return impl_
.Erase(n
, nullptr);
240 bool Lookup(const size_t& key
, string
* val
) override
{
241 Node
n(key
, string());
242 port::RWMutex
* rlock
;
243 bool status
= impl_
.Find(n
, &n
, &rlock
);
253 explicit Node(const size_t key
, const string
& val
) : key_(key
), val_(val
) {}
260 uint64_t operator()(const Node
& node
) {
261 return std::hash
<uint64_t>()(node
.key_
);
266 bool operator()(const Node
& lhs
, const Node
& rhs
) {
267 return lhs
.key_
== rhs
.key_
;
271 HashTable
<Node
, Hash
, Equal
> impl_
;
274 } // namespace ROCKSDB_NAMESPACE
279 int main(int argc
, char** argv
) {
280 GFLAGS_NAMESPACE::SetUsageMessage(std::string("\nUSAGE:\n") +
281 std::string(argv
[0]) + " [OPTIONS]...");
282 GFLAGS_NAMESPACE::ParseCommandLineFlags(&argc
, &argv
, false);
285 // Micro benchmark unordered_map
287 printf("Micro benchmarking std::unordered_map \n");
289 ROCKSDB_NAMESPACE::SimpleImpl impl
;
290 ROCKSDB_NAMESPACE::HashTableBenchmark
_(
291 &impl
, FLAGS_nsec
, FLAGS_nthread_write
, FLAGS_nthread_read
,
292 FLAGS_nthread_erase
);
295 // Micro benchmark scalable hash table
297 printf("Micro benchmarking scalable hash map \n");
299 ROCKSDB_NAMESPACE::GranularLockImpl impl
;
300 ROCKSDB_NAMESPACE::HashTableBenchmark
_(
301 &impl
, FLAGS_nsec
, FLAGS_nthread_write
, FLAGS_nthread_read
,
302 FLAGS_nthread_erase
);
307 #endif // #ifndef GFLAGS
309 int main(int /*argc*/, char** /*argv*/) { return 0; }