1 // Copyright (c) 2011-present, 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). 5 // Copyright (c) 2011 The LevelDB Authors. All rights reserved. 6 // Use of this source code is governed by a BSD-style license that can be 7 // found in the LICENSE file. See the AUTHORS file for names of contributors. 8 // 9 10 #include "util/compression_context_cache.h" 11 12 #include "util/compression.h" 13 #include "util/core_local.h" 14 15 #include <atomic> 16 17 namespace ROCKSDB_NAMESPACE { 18 namespace compression_cache { 19 20 void* const SentinelValue = nullptr; 21 // Cache ZSTD uncompression contexts for reads 22 // if needed we can add ZSTD compression context caching 23 // which is currently is not done since BlockBasedTableBuilder 24 // simply creates one compression context per new SST file. 25 struct ZSTDCachedData { 26 // We choose to cache the below structure instead of a ptr 27 // because we want to avoid a) native types leak b) make 28 // cache use transparent for the user 29 ZSTDUncompressCachedData uncomp_cached_data_; 30 std::atomic<void*> zstd_uncomp_sentinel_; 31 32 char 33 padding[(CACHE_LINE_SIZE - 34 (sizeof(ZSTDUncompressCachedData) + sizeof(std::atomic<void*>)) % 35 CACHE_LINE_SIZE)]; // unused padding field 36 ZSTDCachedDataROCKSDB_NAMESPACE::compression_cache::ZSTDCachedData37 ZSTDCachedData() : zstd_uncomp_sentinel_(&uncomp_cached_data_) {} 38 ZSTDCachedData(const ZSTDCachedData&) = delete; 39 ZSTDCachedData& operator=(const ZSTDCachedData&) = delete; 40 GetUncompressDataROCKSDB_NAMESPACE::compression_cache::ZSTDCachedData41 ZSTDUncompressCachedData GetUncompressData(int64_t idx) { 42 ZSTDUncompressCachedData result; 43 void* expected = &uncomp_cached_data_; 44 if (zstd_uncomp_sentinel_.compare_exchange_strong(expected, 45 SentinelValue)) { 46 uncomp_cached_data_.CreateIfNeeded(); 47 result.InitFromCache(uncomp_cached_data_, idx); 48 } else { 49 // Creates one time use data 50 result.CreateIfNeeded(); 51 } 52 return result; 53 } 54 // Return the entry back into circulation 55 // This is executed only when we successfully obtained 56 // in the first place ReturnUncompressDataROCKSDB_NAMESPACE::compression_cache::ZSTDCachedData57 void ReturnUncompressData() { 58 if (zstd_uncomp_sentinel_.exchange(&uncomp_cached_data_) != SentinelValue) { 59 // Means we are returning while not having it acquired. 60 assert(false); 61 } 62 } 63 }; 64 static_assert(sizeof(ZSTDCachedData) % CACHE_LINE_SIZE == 0, 65 "Expected CACHE_LINE_SIZE alignment"); 66 } // namespace compression_cache 67 68 using namespace compression_cache; 69 70 class CompressionContextCache::Rep { 71 public: Rep()72 Rep() {} GetZSTDUncompressData()73 ZSTDUncompressCachedData GetZSTDUncompressData() { 74 auto p = per_core_uncompr_.AccessElementAndIndex(); 75 int64_t idx = static_cast<int64_t>(p.second); 76 return p.first->GetUncompressData(idx); 77 } ReturnZSTDUncompressData(int64_t idx)78 void ReturnZSTDUncompressData(int64_t idx) { 79 assert(idx >= 0); 80 auto* cn = per_core_uncompr_.AccessAtCore(static_cast<size_t>(idx)); 81 cn->ReturnUncompressData(); 82 } 83 84 private: 85 CoreLocalArray<ZSTDCachedData> per_core_uncompr_; 86 }; 87 CompressionContextCache()88CompressionContextCache::CompressionContextCache() : rep_(new Rep()) {} 89 Instance()90CompressionContextCache* CompressionContextCache::Instance() { 91 static CompressionContextCache instance; 92 return &instance; 93 } 94 InitSingleton()95void CompressionContextCache::InitSingleton() { Instance(); } 96 97 ZSTDUncompressCachedData GetCachedZSTDUncompressData()98CompressionContextCache::GetCachedZSTDUncompressData() { 99 return rep_->GetZSTDUncompressData(); 100 } 101 ReturnCachedZSTDUncompressData(int64_t idx)102void CompressionContextCache::ReturnCachedZSTDUncompressData(int64_t idx) { 103 rep_->ReturnZSTDUncompressData(idx); 104 } 105 ~CompressionContextCache()106CompressionContextCache::~CompressionContextCache() { delete rep_; } 107 108 } // namespace ROCKSDB_NAMESPACE 109