Skip to content

Commit 2a96485

Browse files
author
Andrei Popescu
authored
Optimize reading data from leveldb with iterator. (#862)
When reading data from leveldb::Get() we need to use a string which is poorly initialized and data is copied slowly and inefficient to it which then again is memcpy-ied to a shared vector which adds another layer of memcpy and slow initialization. The overhead for frequent data reads is more then enough to consider an alternative. This alternative consist in reading the data directly from the DB into the resulting shared vector without any intermediate steps by using iterators. The leveldb iterator gives us the possibility to access the raw DB data and size without passing it into a inefficient string before, and gives a huge performance bump. Relates-To: OLPEDGE-1983 Signed-off-by: Andrei Popescu <andrei.popescu@here.com>
1 parent d5a9c2a commit 2a96485

File tree

5 files changed

+110
-62
lines changed

5 files changed

+110
-62
lines changed

olp-cpp-sdk-core/src/cache/DefaultCacheImpl.cpp

Lines changed: 46 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@
2121

2222
#include <algorithm>
2323
#include <chrono>
24+
#include <memory>
25+
#include <string>
26+
#include <utility>
2427

2528
#include "olp/core/logging/Log.h"
2629
#include "olp/core/porting/make_unique.h"
@@ -231,24 +234,22 @@ KeyValueCache::ValueTypePtr DefaultCacheImpl::Get(const std::string& key) {
231234

232235
if (memory_cache_) {
233236
auto value = memory_cache_->Get(key);
234-
235237
if (!value.empty()) {
236238
PromoteKeyLru(key);
237239
return boost::any_cast<KeyValueCache::ValueTypePtr>(value);
238240
}
239241
}
240242

241-
auto disc_cache = GetFromDiscCache(key);
242-
if (disc_cache) {
243-
const std::string& cached_data = disc_cache->first;
244-
auto data = std::make_shared<KeyValueCache::ValueType>(cached_data.size());
245-
246-
std::memcpy(&data->front(), cached_data.data(), cached_data.size());
243+
KeyValueCache::ValueTypePtr value = nullptr;
244+
time_t expiry = KeyValueCache::kDefaultExpiry;
247245

246+
auto result = GetFromDiskCache(key, value, expiry);
247+
if (result && value) {
248248
if (memory_cache_) {
249-
memory_cache_->Put(key, data, disc_cache->second, data->size());
249+
memory_cache_->Put(key, value, expiry, value->size());
250250
}
251-
return data;
251+
252+
return value;
252253
}
253254

254255
return nullptr;
@@ -515,38 +516,54 @@ DefaultCache::StorageOpenResult DefaultCacheImpl::SetupStorage() {
515516
return result;
516517
}
517518

518-
boost::optional<std::pair<std::string, time_t>>
519-
DefaultCacheImpl::GetFromDiscCache(const std::string& key) {
519+
bool DefaultCacheImpl::GetFromDiskCache(const std::string& key,
520+
KeyValueCache::ValueTypePtr& value,
521+
time_t& expiry) {
522+
// Make sure we do not get a dirty entry
523+
value = nullptr;
524+
expiry = KeyValueCache::kDefaultExpiry;
525+
520526
if (protected_cache_) {
521-
auto result = protected_cache_->Get(key);
522-
if (result) {
523-
auto default_expiry = KeyValueCache::kDefaultExpiry;
524-
return std::make_pair(std::move(result.value()), default_expiry);
527+
auto result = protected_cache_->Get(key, value);
528+
if (result && value && !value->empty()) {
529+
return true;
525530
}
526531
}
527532

528533
if (mutable_cache_) {
529534
auto expiry = GetRemainingExpiryTime(key, *mutable_cache_);
530-
if (expiry <= 0) {
531-
uint64_t removed_data_size = 0u;
532-
PurgeDiskItem(key, *mutable_cache_, removed_data_size);
533-
mutable_cache_data_size_ -= removed_data_size;
534-
535-
RemoveKeyLru(key);
536-
} else {
535+
if (expiry > 0) {
536+
// Entry didn't expire yet, we can still use it
537537
if (!PromoteKeyLru(key)) {
538538
// If not found in LRU no need to look in disk cache either.
539-
OLP_SDK_LOG_DEBUG_F(
540-
kLogTag, "Key is not found LRU mutable cache: key %s", key.c_str());
541-
return boost::none;
539+
OLP_SDK_LOG_DEBUG_F(kLogTag, "Key not found in LRU, key='%s'",
540+
key.c_str());
541+
return false;
542542
}
543543

544-
auto result = mutable_cache_->Get(key);
545-
if (result) {
546-
return std::make_pair(std::move(result.value()), expiry);
547-
}
544+
auto result = mutable_cache_->Get(key, value);
545+
return result && value;
548546
}
547+
548+
// Data expired in cache -> remove
549+
uint64_t removed_data_size = 0u;
550+
PurgeDiskItem(key, *mutable_cache_, removed_data_size);
551+
mutable_cache_data_size_ -= removed_data_size;
552+
RemoveKeyLru(key);
553+
}
554+
555+
return false;
556+
}
557+
558+
boost::optional<std::pair<std::string, time_t>>
559+
DefaultCacheImpl::GetFromDiscCache(const std::string& key) {
560+
KeyValueCache::ValueTypePtr value = nullptr;
561+
time_t expiry = KeyValueCache::kDefaultExpiry;
562+
auto result = GetFromDiskCache(key, value, expiry);
563+
if (result && value) {
564+
return std::make_pair(std::string(value->begin(), value->end()), expiry);
549565
}
566+
550567
return boost::none;
551568
}
552569

olp-cpp-sdk-core/src/cache/DefaultCacheImpl.h

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,10 @@
2121

2222
#include "olp/core/cache/DefaultCache.h"
2323

24+
#include <memory>
25+
#include <string>
26+
#include <utility>
27+
2428
#include "DiskCache.h"
2529
#include "InMemoryCache.h"
2630

@@ -52,24 +56,24 @@ class DefaultCacheImpl {
5256
bool RemoveKeysWithPrefix(const std::string& key);
5357

5458
protected:
55-
/// The LRU cache definition using the leveldb keys as key and the value size
59+
/// Alias for the LRU cache using the leveldb keys as key and the value size
5660
/// as value.
5761
using DiskLruCache = utils::LruCache<std::string, size_t>;
5862

5963
/// Returns LRU mutable cache, used for tests.
6064
const std::unique_ptr<DiskLruCache>& GetMutableCacheLru() const {
6165
return mutable_cache_lru_;
62-
};
66+
}
6367

6468
/// Returns mutable cache, used for tests.
6569
const std::unique_ptr<DiskCache>& GetMutableCache() const {
6670
return mutable_cache_;
67-
};
71+
}
6872

6973
/// Returns memory cache, used for tests.
7074
const std::unique_ptr<InMemoryCache>& GetMemoryCache() const {
7175
return memory_cache_;
72-
};
76+
}
7377

7478
/// Returns mutable cache size, used for tests.
7579
uint64_t GetMutableCacheSize() const { return mutable_cache_data_size_; }
@@ -99,6 +103,9 @@ class DefaultCacheImpl {
99103

100104
DefaultCache::StorageOpenResult SetupStorage();
101105

106+
bool GetFromDiskCache(const std::string& key,
107+
KeyValueCache::ValueTypePtr& value, time_t& expiry);
108+
102109
boost::optional<std::pair<std::string, time_t>> GetFromDiscCache(
103110
const std::string& key);
104111

olp-cpp-sdk-core/src/cache/DiskCache.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <memory>
2525
#include <regex>
2626
#include <string>
27+
#include <utility>
2728
#include <vector>
2829

2930
#include <leveldb/db.h>
@@ -272,6 +273,30 @@ boost::optional<std::string> DiskCache::Get(const std::string& key) {
272273
: boost::none;
273274
}
274275

276+
bool DiskCache::Get(const std::string& key,
277+
KeyValueCache::ValueTypePtr& value) {
278+
if (!database_) {
279+
OLP_SDK_LOG_ERROR(kLogTag, "Get: Database not initialized");
280+
return false;
281+
}
282+
283+
value = nullptr;
284+
leveldb::ReadOptions options;
285+
options.verify_checksums = check_crc_;
286+
auto iterator = NewIterator(options);
287+
288+
iterator->Seek(key);
289+
if (iterator->Valid() && iterator->key() == key) {
290+
auto slice_value = iterator->value();
291+
if (!slice_value.empty()) {
292+
value = std::make_shared<KeyValueCache::ValueType>(
293+
slice_value.data(), slice_value.data() + slice_value.size());
294+
}
295+
}
296+
297+
return true;
298+
}
299+
275300
bool DiskCache::Remove(const std::string& key, uint64_t& removed_data_size) {
276301
if (!database_) {
277302
removed_data_size = 0;

olp-cpp-sdk-core/src/cache/DiskCache.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121

2222
#include <time.h>
2323
#include <functional>
24+
#include <limits>
2425
#include <map>
2526
#include <memory>
2627
#include <mutex>
@@ -33,6 +34,7 @@
3334
#include <leveldb/options.h>
3435
#include <leveldb/write_batch.h>
3536
#include <olp/core/cache/CacheSettings.h>
37+
#include <olp/core/cache/KeyValueCache.h>
3638
#include <olp/core/client/ApiError.h>
3739
#include <olp/core/client/ApiResponse.h>
3840

@@ -110,8 +112,12 @@ class DiskCache {
110112

111113
bool Put(const std::string& key, leveldb::Slice slice);
112114

115+
/// @deprecated Please use Get(const std::string&,
116+
/// KeyValueCache::ValueTypePtr&) instead.
113117
boost::optional<std::string> Get(const std::string& key);
114118

119+
bool Get(const std::string& key, KeyValueCache::ValueTypePtr& value);
120+
115121
/// Remove single key/value from DB.
116122
bool Remove(const std::string& key, uint64_t& removed_data_size);
117123

olp-cpp-sdk-core/tests/cache/DefaultCacheImplTest.cpp

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,13 @@
2222
#include <cache/DefaultCacheImpl.h>
2323
#include <olp/core/utils/Dir.h>
2424

25-
using namespace olp::cache;
25+
namespace cache = olp::cache;
2626

2727
namespace {
28-
class DefaultCacheImplHelper : public DefaultCacheImpl {
28+
class DefaultCacheImplHelper : public cache::DefaultCacheImpl {
2929
public:
30-
DefaultCacheImplHelper(const CacheSettings& settings)
31-
: DefaultCacheImpl(settings){};
30+
explicit DefaultCacheImplHelper(const cache::CacheSettings& settings)
31+
: DefaultCacheImpl(settings) {}
3232

3333
bool HasLruCache() const { return GetMutableCacheLru().get() != nullptr; }
3434

@@ -114,7 +114,7 @@ TEST(DefaultCacheImplTest, LruCache) {
114114

115115
olp::cache::CacheSettings settings;
116116
settings.disk_path_mutable = cache_path;
117-
settings.eviction_policy = EvictionPolicy::kLeastRecentlyUsed;
117+
settings.eviction_policy = cache::EvictionPolicy::kLeastRecentlyUsed;
118118
DefaultCacheImplHelper cache(settings);
119119
cache.Open();
120120

@@ -170,7 +170,7 @@ TEST(DefaultCacheImplTest, LruCache) {
170170

171171
olp::cache::CacheSettings settings;
172172
settings.disk_path_mutable = cache_path;
173-
settings.eviction_policy = EvictionPolicy::kNone;
173+
settings.eviction_policy = cache::EvictionPolicy::kNone;
174174
DefaultCacheImplHelper cache(settings);
175175
cache.Open();
176176

@@ -340,20 +340,18 @@ TEST(DefaultCacheImplTest, MutableCacheSize) {
340340
{
341341
SCOPED_TRACE("Put decode");
342342

343-
settings.eviction_policy = EvictionPolicy::kNone;
343+
settings.eviction_policy = cache::EvictionPolicy::kNone;
344344
DefaultCacheImplHelper cache(settings);
345345
cache.Open();
346346
cache.Clear();
347347

348-
cache.Put(
349-
key1, data_string, [=]() { return data_string; },
350-
(std::numeric_limits<time_t>::max)());
348+
cache.Put(key1, data_string, [=]() { return data_string; },
349+
(std::numeric_limits<time_t>::max)());
351350
auto data_size = key1.size() + data_string.size();
352351

353352
EXPECT_EQ(data_size, cache.Size());
354353

355-
cache.Put(
356-
key2, data_string, [=]() { return data_string; }, expiry);
354+
cache.Put(key2, data_string, [=]() { return data_string; }, expiry);
357355
data_size +=
358356
key2.size() + data_string.size() + cache.CalculateExpirySize(key2);
359357

@@ -363,7 +361,7 @@ TEST(DefaultCacheImplTest, MutableCacheSize) {
363361
{
364362
SCOPED_TRACE("Put binary");
365363

366-
settings.eviction_policy = EvictionPolicy::kNone;
364+
settings.eviction_policy = cache::EvictionPolicy::kNone;
367365
DefaultCacheImplHelper cache(settings);
368366
cache.Open();
369367
cache.Clear();
@@ -388,9 +386,8 @@ TEST(DefaultCacheImplTest, MutableCacheSize) {
388386
cache.Clear();
389387

390388
cache.Put(key1, data_ptr, (std::numeric_limits<time_t>::max)());
391-
cache.Put(
392-
key2, data_string, [=]() { return data_string; },
393-
(std::numeric_limits<time_t>::max)());
389+
cache.Put(key2, data_string, [=]() { return data_string; },
390+
(std::numeric_limits<time_t>::max)());
394391

395392
cache.Remove(key1);
396393
cache.Remove(key2);
@@ -407,8 +404,7 @@ TEST(DefaultCacheImplTest, MutableCacheSize) {
407404
cache.Clear();
408405

409406
cache.Put(key1, data_ptr, expiry);
410-
cache.Put(
411-
key2, data_string, [=]() { return data_string; }, expiry);
407+
cache.Put(key2, data_string, [=]() { return data_string; }, expiry);
412408

413409
cache.Remove(key1);
414410
cache.Remove(key2);
@@ -426,8 +422,7 @@ TEST(DefaultCacheImplTest, MutableCacheSize) {
426422

427423
cache.Put(key1, data_ptr, (std::numeric_limits<time_t>::max)());
428424
cache.Put(key2, data_ptr, expiry);
429-
cache.Put(
430-
key3, data_string, [=]() { return data_string; }, expiry);
425+
cache.Put(key3, data_string, [=]() { return data_string; }, expiry);
431426
const auto data_size =
432427
key3.size() + data_string.size() + cache.CalculateExpirySize(key3);
433428

@@ -445,8 +440,7 @@ TEST(DefaultCacheImplTest, MutableCacheSize) {
445440
cache.Clear();
446441

447442
cache.Put(key1, data_ptr, -1);
448-
cache.Put(
449-
key2, data_string, [=]() { return data_string; }, -1);
443+
cache.Put(key2, data_string, [=]() { return data_string; }, -1);
450444

451445
cache.Get(key1);
452446
cache.Get(key2, [](const std::string& value) { return value; });
@@ -459,15 +453,14 @@ TEST(DefaultCacheImplTest, MutableCacheSize) {
459453
const std::string data_string{"this is key's data"};
460454
const std::string key{"somekey"};
461455

462-
settings.eviction_policy = EvictionPolicy::kNone;
456+
settings.eviction_policy = cache::EvictionPolicy::kNone;
463457
DefaultCacheImplHelper cache(settings);
464458

465459
cache.Open();
466460
cache.Clear();
467461

468-
cache.Put(
469-
key, data_string, [=]() { return data_string; },
470-
(std::numeric_limits<time_t>::max)());
462+
cache.Put(key, data_string, [=]() { return data_string; },
463+
(std::numeric_limits<time_t>::max)());
471464
const auto data_size = key.size() + data_string.size();
472465
cache.Close();
473466
EXPECT_EQ(0u, cache.Size());
@@ -487,7 +480,7 @@ TEST(DefaultCacheImplTest, MutableCacheSize) {
487480
std::vector<unsigned char> binary_data(data_size);
488481
olp::cache::CacheSettings settings;
489482
settings.disk_path_mutable = cache_path;
490-
settings.eviction_policy = EvictionPolicy::kNone;
483+
settings.eviction_policy = cache::EvictionPolicy::kNone;
491484
settings.max_disk_storage = 2u * 1024u * 1024u;
492485
DefaultCacheImplHelper cache(settings);
493486

@@ -543,7 +536,7 @@ TEST(DefaultCacheImplTest, LruCacheEviction) {
543536
std::vector<unsigned char> binary_data(data_size);
544537
olp::cache::CacheSettings settings;
545538
settings.disk_path_mutable = cache_path;
546-
settings.eviction_policy = EvictionPolicy::kNone;
539+
settings.eviction_policy = cache::EvictionPolicy::kNone;
547540
settings.max_disk_storage = 2u * 1024u * 1024u;
548541
DefaultCacheImplHelper cache(settings);
549542

@@ -590,7 +583,7 @@ TEST(DefaultCacheImplTest, LruCacheEviction) {
590583
std::vector<unsigned char> binary_data(data_size);
591584
olp::cache::CacheSettings settings;
592585
settings.disk_path_mutable = cache_path;
593-
settings.eviction_policy = EvictionPolicy::kLeastRecentlyUsed;
586+
settings.eviction_policy = cache::EvictionPolicy::kLeastRecentlyUsed;
594587
settings.max_disk_storage = 2u * 1024u * 1024u;
595588
DefaultCacheImplHelper cache(settings);
596589

0 commit comments

Comments
 (0)