Refine Ribbon configuration, improve testing, add Homogeneous (#7879)
Summary: This change only affects non-schema-critical aspects of the production candidate Ribbon filter. Specifically, it refines choice of internal configuration parameters based on inputs. The changes are minor enough that the schema tests in bloom_test, some of which depend on this, are unaffected. There are also some minor optimizations and refactorings. This would be a schema change for "smash" Ribbon, to fix some known issues with small filters, but "smash" Ribbon is not accessible in public APIs. Unit test CompactnessAndBacktrackAndFpRate updated to test small and medium-large filters. Run with --thoroughness=100 or so for much better detection power (not appropriate for continuous regression testing). Homogenous Ribbon: This change adds internally a Ribbon filter variant we call Homogeneous Ribbon, in collaboration with Stefan Walzer. The expected "result" value for every key is zero, instead of computed from a hash. Entropy for queries not to be false positives comes from free variables ("overhead") in the solution structure, which are populated pseudorandomly. Construction is slightly faster for not tracking result values, and never fails. Instead, FP rate can jump up whenever and whereever entries are packed too tightly. For small structures, we can choose overhead to make this FP rate jump unlikely, as seen in updated unit test CompactnessAndBacktrackAndFpRate. Unlike standard Ribbon, Homogeneous Ribbon seems to scale to arbitrary number of keys when accepting an FP rate penalty for small pockets of high FP rate in the structure. For example, 64-bit ribbon with 8 solution columns and 10% allocated space overhead for slots seems to achieve about 10.5% space overhead vs. information-theoretic minimum based on its observed FP rate with expected pockets of degradation. (FP rate is close to 1/256.) If targeting a higher FP rate with fewer solution columns, Homogeneous Ribbon can be even more space efficient, because the penalty from degradation is relatively smaller. If targeting a lower FP rate, Homogeneous Ribbon is less space efficient, as more allocated overhead is needed to keep the FP rate impact of degradation relatively under control. The new OptimizeHomogAtScale tool in ribbon_test helps to find these optimal allocation overheads for different numbers of solution columns. And Ribbon widths, with 128-bit Ribbon apparently cutting space overheads in half vs. 64-bit. Other misc item specifics: * Ribbon APIs in util/ribbon_config.h now provide configuration data for not just 5% construction failure rate (95% success), but also 50% and 0.1%. * Note that the Ribbon structure does not exhibit "threshold" behavior as standard Xor filter does, so there is a roughly fixed space penalty to cut construction failure rate in half. Thus, there isn't really an "almost sure" setting. * Although we can extrapolate settings for large filters, we don't have a good formula for configuring smaller filters (< 2^17 slots or so), and efforts to summarize with a formula have failed. Thus, small data is hard-coded from updated FindOccupancy tool. * Enhances ApproximateNumEntries for public API Ribbon using more precise data (new API GetNumToAdd), thus a more accurate but not perfect reversal of CalculateSpace. (bloom_test updated to expect the greater precision) * Move EndianSwapValue from coding.h to coding_lean.h to keep Ribbon code easily transferable from RocksDB * Add some missing 'const' to member functions * Small optimization to 128-bit BitParity * Small refactoring of BandingStorage in ribbon_alg.h to support Homogeneous Ribbon * CompactnessAndBacktrackAndFpRate now has an "expand" test: on construction failure, a possible alternative to re-seeding hash functions is simply to increase the number of slots (allocated space overhead) and try again with essentially the same hash values. (Start locations will be different roundings of the same scaled hash values--because fastrange not mod.) This seems to be as effective or more effective than re-seeding, as long as we increase the number of slots (m) by roughly m += m/w where w is the Ribbon width. This way, there is effectively an expansion by one slot for each ribbon-width window in the banding. (This approach assumes that getting "bad data" from your hash function is as unlikely as it naturally should be, e.g. no adversary.) * 32-bit and 16-bit Ribbon configurations are added to ribbon_test for understanding their behavior, e.g. with FindOccupancy. They are not considered useful at this time and not tested with CompactnessAndBacktrackAndFpRate. Pull Request resolved: https://github.com/facebook/rocksdb/pull/7879 Test Plan: unit test updates included Reviewed By: jay-zhuang Differential Revision: D26371245 Pulled By: pdillinger fbshipit-source-id: da6600d90a3785b99ad17a88b2a3027710b4ea3amain
parent
c370d8aa12
commit
a8b3b9a20c
@ -0,0 +1,506 @@ |
||||
// Copyright (c) Facebook, Inc. and its affiliates. 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).
|
||||
|
||||
#include "util/ribbon_config.h" |
||||
|
||||
namespace ROCKSDB_NAMESPACE { |
||||
|
||||
namespace ribbon { |
||||
|
||||
namespace detail { |
||||
|
||||
// Each instantiation of this struct is sufficiently unique for configuration
|
||||
// purposes, and is only instantiated for settings where we support the
|
||||
// configuration API. An application might only reference one instantiation,
|
||||
// meaning the rest could be pruned at link time.
|
||||
template <ConstructionFailureChance kCfc, uint64_t kCoeffBits, bool kUseSmash> |
||||
struct BandingConfigHelperData { |
||||
static constexpr size_t kKnownSize = 18U; |
||||
|
||||
// Because of complexity in the data, for smaller numbers of slots
|
||||
// (powers of two up to 2^17), we record known numbers that can be added
|
||||
// with kCfc chance of construction failure and settings in template
|
||||
// parameters. Zero means "unsupported (too small) number of slots".
|
||||
// (GetNumToAdd below will use interpolation for numbers of slots
|
||||
// between powers of two; double rather than integer values here make
|
||||
// that more accurate.)
|
||||
static const std::array<double, kKnownSize> kKnownToAddByPow2; |
||||
|
||||
// For sufficiently large number of slots, doubling the number of
|
||||
// slots will increase the expected overhead (slots over number added)
|
||||
// by approximately this constant.
|
||||
// (This is roughly constant regardless of ConstructionFailureChance and
|
||||
// smash setting.)
|
||||
// (Would be a constant if we had partial template specialization for
|
||||
// static const members.)
|
||||
static inline double GetFactorPerPow2() { |
||||
if (kCoeffBits == 128U) { |
||||
return 0.0038; |
||||
} else { |
||||
assert(kCoeffBits == 64U); |
||||
return 0.0083; |
||||
} |
||||
} |
||||
|
||||
// Overhead factor for 2^(kKnownSize-1) slots
|
||||
// (Would be a constant if we had partial template specialization for
|
||||
// static const members.)
|
||||
static inline double GetFinalKnownFactor() { |
||||
return 1.0 * (uint32_t{1} << (kKnownSize - 1)) / |
||||
kKnownToAddByPow2[kKnownSize - 1]; |
||||
} |
||||
|
||||
// GetFinalKnownFactor() - (kKnownSize-1) * GetFactorPerPow2()
|
||||
// (Would be a constant if we had partial template specialization for
|
||||
// static const members.)
|
||||
static inline double GetBaseFactor() { |
||||
return GetFinalKnownFactor() - (kKnownSize - 1) * GetFactorPerPow2(); |
||||
} |
||||
|
||||
// Get overhead factor (slots over number to add) for sufficiently large
|
||||
// number of slots (by log base 2)
|
||||
static inline double GetFactorForLarge(double log2_num_slots) { |
||||
return GetBaseFactor() + log2_num_slots * GetFactorPerPow2(); |
||||
} |
||||
|
||||
// For a given power of two number of slots (specified by whole number
|
||||
// log base 2), implements GetNumToAdd for such limited case, returning
|
||||
// double for better interpolation in GetNumToAdd and GetNumSlots.
|
||||
static inline double GetNumToAddForPow2(uint32_t log2_num_slots) { |
||||
assert(log2_num_slots <= 32); // help clang-analyze
|
||||
if (log2_num_slots < kKnownSize) { |
||||
return kKnownToAddByPow2[log2_num_slots]; |
||||
} else { |
||||
return 1.0 * (uint64_t{1} << log2_num_slots) / |
||||
GetFactorForLarge(1.0 * log2_num_slots); |
||||
} |
||||
} |
||||
}; |
||||
|
||||
// Based on data from FindOccupancy in ribbon_test
|
||||
template <> |
||||
const std::array<double, 18> |
||||
BandingConfigHelperData<kOneIn2, 128U, false>::kKnownToAddByPow2{{ |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, // unsupported
|
||||
252.984, |
||||
506.109, |
||||
1013.71, |
||||
2029.47, |
||||
4060.43, |
||||
8115.63, |
||||
16202.2, |
||||
32305.1, |
||||
64383.5, |
||||
128274, |
||||
}}; |
||||
|
||||
template <> |
||||
const std::array<double, 18> |
||||
BandingConfigHelperData<kOneIn2, 128U, /*smash*/ true>::kKnownToAddByPow2{{ |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, // unsupported
|
||||
126.274, |
||||
254.279, |
||||
510.27, |
||||
1022.24, |
||||
2046.02, |
||||
4091.99, |
||||
8154.98, |
||||
16244.3, |
||||
32349.7, |
||||
64426.6, |
||||
128307, |
||||
}}; |
||||
|
||||
template <> |
||||
const std::array<double, 18> |
||||
BandingConfigHelperData<kOneIn2, 64U, false>::kKnownToAddByPow2{{ |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, // unsupported
|
||||
124.94, |
||||
249.968, |
||||
501.234, |
||||
1004.06, |
||||
2006.15, |
||||
3997.89, |
||||
7946.99, |
||||
15778.4, |
||||
31306.9, |
||||
62115.3, |
||||
123284, |
||||
}}; |
||||
|
||||
template <> |
||||
const std::array<double, 18> |
||||
BandingConfigHelperData<kOneIn2, 64U, /*smash*/ true>::kKnownToAddByPow2{{ |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, // unsupported
|
||||
62.2683, |
||||
126.259, |
||||
254.268, |
||||
509.975, |
||||
1019.98, |
||||
2026.16, |
||||
4019.75, |
||||
7969.8, |
||||
15798.2, |
||||
31330.3, |
||||
62134.2, |
||||
123255, |
||||
}}; |
||||
|
||||
template <> |
||||
const std::array<double, 18> |
||||
BandingConfigHelperData<kOneIn20, 128U, false>::kKnownToAddByPow2{{ |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, // unsupported
|
||||
248.851, |
||||
499.532, |
||||
1001.26, |
||||
2003.97, |
||||
4005.59, |
||||
8000.39, |
||||
15966.6, |
||||
31828.1, |
||||
63447.3, |
||||
126506, |
||||
}}; |
||||
|
||||
template <> |
||||
const std::array<double, 18> |
||||
BandingConfigHelperData<kOneIn20, 128U, /*smash*/ true>::kKnownToAddByPow2{{ |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, // unsupported
|
||||
122.637, |
||||
250.651, |
||||
506.625, |
||||
1018.54, |
||||
2036.43, |
||||
4041.6, |
||||
8039.25, |
||||
16005, |
||||
31869.6, |
||||
63492.8, |
||||
126537, |
||||
}}; |
||||
|
||||
template <> |
||||
const std::array<double, 18> |
||||
BandingConfigHelperData<kOneIn20, 64U, false>::kKnownToAddByPow2{{ |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, // unsupported
|
||||
120.659, |
||||
243.346, |
||||
488.168, |
||||
976.373, |
||||
1948.86, |
||||
3875.85, |
||||
7704.97, |
||||
15312.4, |
||||
30395.1, |
||||
60321.8, |
||||
119813, |
||||
}}; |
||||
|
||||
template <> |
||||
const std::array<double, 18> |
||||
BandingConfigHelperData<kOneIn20, 64U, /*smash*/ true>::kKnownToAddByPow2{{ |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, // unsupported
|
||||
58.6016, |
||||
122.619, |
||||
250.641, |
||||
503.595, |
||||
994.165, |
||||
1967.36, |
||||
3898.17, |
||||
7727.21, |
||||
15331.5, |
||||
30405.8, |
||||
60376.2, |
||||
119836, |
||||
}}; |
||||
|
||||
template <> |
||||
const std::array<double, 18> |
||||
BandingConfigHelperData<kOneIn1000, 128U, false>::kKnownToAddByPow2{{ |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, // unsupported
|
||||
242.61, |
||||
491.887, |
||||
983.603, |
||||
1968.21, |
||||
3926.98, |
||||
7833.99, |
||||
15629, |
||||
31199.9, |
||||
62307.8, |
||||
123870, |
||||
}}; |
||||
|
||||
template <> |
||||
const std::array<double, 18> BandingConfigHelperData< |
||||
kOneIn1000, 128U, /*smash*/ true>::kKnownToAddByPow2{{ |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, // unsupported
|
||||
117.19, |
||||
245.105, |
||||
500.748, |
||||
1010.67, |
||||
1993.4, |
||||
3950.01, |
||||
7863.31, |
||||
15652, |
||||
31262.1, |
||||
62462.8, |
||||
124095, |
||||
}}; |
||||
|
||||
template <> |
||||
const std::array<double, 18> |
||||
BandingConfigHelperData<kOneIn1000, 64U, false>::kKnownToAddByPow2{{ |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, // unsupported
|
||||
114, |
||||
234.8, |
||||
471.498, |
||||
940.165, |
||||
1874, |
||||
3721.5, |
||||
7387.5, |
||||
14592, |
||||
29160, |
||||
57745, |
||||
115082, |
||||
}}; |
||||
|
||||
template <> |
||||
const std::array<double, 18> |
||||
BandingConfigHelperData<kOneIn1000, 64U, /*smash*/ true>::kKnownToAddByPow2{ |
||||
{ |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, |
||||
0, // unsupported
|
||||
53.0434, |
||||
117, |
||||
245.312, |
||||
483.571, |
||||
950.251, |
||||
1878, |
||||
3736.34, |
||||
7387.97, |
||||
14618, |
||||
29142.9, |
||||
57838.8, |
||||
114932, |
||||
}}; |
||||
|
||||
// We hide these implementation details from the .h file with explicit
|
||||
// instantiations below these partial specializations.
|
||||
|
||||
template <ConstructionFailureChance kCfc, uint64_t kCoeffBits, bool kUseSmash, |
||||
bool kHomogeneous> |
||||
uint32_t BandingConfigHelper1MaybeSupported< |
||||
kCfc, kCoeffBits, kUseSmash, kHomogeneous, |
||||
true /* kIsSupported */>::GetNumToAdd(uint32_t num_slots) { |
||||
using Data = detail::BandingConfigHelperData<kCfc, kCoeffBits, kUseSmash>; |
||||
if (num_slots == 0) { |
||||
return 0; |
||||
} |
||||
uint32_t num_to_add; |
||||
double log2_num_slots = std::log(num_slots) * 1.4426950409; |
||||
uint32_t floor_log2 = static_cast<uint32_t>(log2_num_slots); |
||||
if (floor_log2 + 1 < Data::kKnownSize) { |
||||
double ceil_portion = 1.0 * num_slots / (uint32_t{1} << floor_log2) - 1.0; |
||||
// Must be a supported number of slots
|
||||
assert(Data::kKnownToAddByPow2[floor_log2] > 0.0); |
||||
// Weighted average of two nearest known data points
|
||||
num_to_add = static_cast<uint32_t>( |
||||
ceil_portion * Data::kKnownToAddByPow2[floor_log2 + 1] + |
||||
(1.0 - ceil_portion) * Data::kKnownToAddByPow2[floor_log2]); |
||||
} else { |
||||
// Use formula for large values
|
||||
double factor = Data::GetFactorForLarge(log2_num_slots); |
||||
assert(factor >= 1.0); |
||||
num_to_add = static_cast<uint32_t>(num_slots / factor); |
||||
} |
||||
if (kHomogeneous) { |
||||
// Even when standard filter construction would succeed, we might
|
||||
// have loaded things up too much for Homogeneous filter. (Complete
|
||||
// explanation not known but observed empirically.) This seems to
|
||||
// correct for that, mostly affecting small filter configurations.
|
||||
if (num_to_add >= 8) { |
||||
num_to_add -= 8; |
||||
} else { |
||||
assert(false); |
||||
} |
||||
} |
||||
return num_to_add; |
||||
} |
||||
|
||||
template <ConstructionFailureChance kCfc, uint64_t kCoeffBits, bool kUseSmash, |
||||
bool kHomogeneous> |
||||
uint32_t BandingConfigHelper1MaybeSupported< |
||||
kCfc, kCoeffBits, kUseSmash, kHomogeneous, |
||||
true /* kIsSupported */>::GetNumSlots(uint32_t num_to_add) { |
||||
using Data = detail::BandingConfigHelperData<kCfc, kCoeffBits, kUseSmash>; |
||||
|
||||
if (num_to_add == 0) { |
||||
return 0; |
||||
} |
||||
if (kHomogeneous) { |
||||
// Reverse of above in GetNumToAdd
|
||||
num_to_add += 8; |
||||
} |
||||
double log2_num_to_add = std::log(num_to_add) * 1.4426950409; |
||||
uint32_t approx_log2_slots = static_cast<uint32_t>(log2_num_to_add + 0.5); |
||||
assert(approx_log2_slots <= 32); // help clang-analyze
|
||||
|
||||
double lower_num_to_add = Data::GetNumToAddForPow2(approx_log2_slots); |
||||
double upper_num_to_add; |
||||
if (approx_log2_slots == 0 || lower_num_to_add == /* unsupported */ 0) { |
||||
// Return minimum non-zero slots in standard implementation
|
||||
return kUseSmash ? kCoeffBits : 2 * kCoeffBits; |
||||
} else if (num_to_add < lower_num_to_add) { |
||||
upper_num_to_add = lower_num_to_add; |
||||
--approx_log2_slots; |
||||
lower_num_to_add = Data::GetNumToAddForPow2(approx_log2_slots); |
||||
} else { |
||||
upper_num_to_add = Data::GetNumToAddForPow2(approx_log2_slots + 1); |
||||
} |
||||
|
||||
assert(num_to_add >= lower_num_to_add); |
||||
assert(num_to_add < upper_num_to_add); |
||||
|
||||
double upper_portion = |
||||
(num_to_add - lower_num_to_add) / (upper_num_to_add - lower_num_to_add); |
||||
|
||||
double lower_num_slots = 1.0 * (uint64_t{1} << approx_log2_slots); |
||||
|
||||
// Interpolation, round up
|
||||
return static_cast<uint32_t>(upper_portion * lower_num_slots + |
||||
lower_num_slots + 0.999999999); |
||||
} |
||||
|
||||
// These explicit instantiations enable us to hide most of the
|
||||
// implementation details from the .h file. (The .h file currently
|
||||
// needs to determine whether settings are "supported" or not.)
|
||||
|
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn2, 128U, /*sm*/ false, |
||||
/*hm*/ false, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn2, 128U, /*sm*/ true, |
||||
/*hm*/ false, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn2, 128U, /*sm*/ false, |
||||
/*hm*/ true, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn2, 128U, /*sm*/ true, |
||||
/*hm*/ true, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn2, 64U, /*sm*/ false, |
||||
/*hm*/ false, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn2, 64U, /*sm*/ true, |
||||
/*hm*/ false, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn2, 64U, /*sm*/ false, |
||||
/*hm*/ true, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn2, 64U, /*sm*/ true, |
||||
/*hm*/ true, /*sup*/ true>; |
||||
|
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn20, 128U, /*sm*/ false, |
||||
/*hm*/ false, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn20, 128U, /*sm*/ true, |
||||
/*hm*/ false, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn20, 128U, /*sm*/ false, |
||||
/*hm*/ true, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn20, 128U, /*sm*/ true, |
||||
/*hm*/ true, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn20, 64U, /*sm*/ false, |
||||
/*hm*/ false, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn20, 64U, /*sm*/ true, |
||||
/*hm*/ false, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn20, 64U, /*sm*/ false, |
||||
/*hm*/ true, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn20, 64U, /*sm*/ true, |
||||
/*hm*/ true, /*sup*/ true>; |
||||
|
||||
template struct BandingConfigHelper1MaybeSupported< |
||||
kOneIn1000, 128U, /*sm*/ false, /*hm*/ false, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported< |
||||
kOneIn1000, 128U, /*sm*/ true, /*hm*/ false, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported< |
||||
kOneIn1000, 128U, /*sm*/ false, /*hm*/ true, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported< |
||||
kOneIn1000, 128U, /*sm*/ true, /*hm*/ true, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported< |
||||
kOneIn1000, 64U, /*sm*/ false, /*hm*/ false, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn1000, 64U, /*sm*/ true, |
||||
/*hm*/ false, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported< |
||||
kOneIn1000, 64U, /*sm*/ false, /*hm*/ true, /*sup*/ true>; |
||||
template struct BandingConfigHelper1MaybeSupported<kOneIn1000, 64U, /*sm*/ true, |
||||
/*hm*/ true, /*sup*/ true>; |
||||
|
||||
} // namespace detail
|
||||
|
||||
} // namespace ribbon
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
@ -0,0 +1,182 @@ |
||||
// Copyright (c) Facebook, Inc. and its affiliates. 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).
|
||||
|
||||
#pragma once |
||||
|
||||
#include <array> |
||||
#include <cassert> |
||||
#include <cmath> |
||||
#include <cstdint> |
||||
|
||||
#include "port/lang.h" // for FALLTHROUGH_INTENDED |
||||
#include "rocksdb/rocksdb_namespace.h" |
||||
|
||||
namespace ROCKSDB_NAMESPACE { |
||||
|
||||
namespace ribbon { |
||||
|
||||
// RIBBON PHSF & RIBBON Filter (Rapid Incremental Boolean Banding ON-the-fly)
|
||||
//
|
||||
// ribbon_config.h: APIs for relating numbers of slots with numbers of
|
||||
// additions for tolerable construction failure probabilities. This is
|
||||
// separate from ribbon_impl.h because it might not be needed for
|
||||
// some applications.
|
||||
//
|
||||
// This API assumes uint32_t for number of slots, as a single Ribbon
|
||||
// linear system should not normally overflow that without big penalties.
|
||||
//
|
||||
// Template parameter kCoeffBits uses uint64_t for convenience in case it
|
||||
// comes from size_t.
|
||||
//
|
||||
// Most of the complexity here is trying to optimize speed and
|
||||
// compiled code size, using templates to minimize table look-ups and
|
||||
// the compiled size of all linked look-up tables. Look-up tables are
|
||||
// required because we don't have good formulas, and the data comes
|
||||
// from running FindOccupancy in ribbon_test.
|
||||
|
||||
// Represents a chosen chance of successful Ribbon construction for a single
|
||||
// seed. Allowing higher chance of failed construction can reduce space
|
||||
// overhead but takes extra time in construction.
|
||||
enum ConstructionFailureChance { |
||||
kOneIn2, |
||||
kOneIn20, |
||||
// When using kHomogeneous==true, construction failure chance should
|
||||
// not generally exceed target FP rate, so it unlikely useful to
|
||||
// allow a higher "failure" chance. In some cases, even more overhead
|
||||
// is appropriate. (TODO)
|
||||
kOneIn1000, |
||||
}; |
||||
|
||||
namespace detail { |
||||
|
||||
// It is useful to compile ribbon_test linking to BandingConfigHelper with
|
||||
// settings for which we do not have configuration data, as long as we don't
|
||||
// run the code. This template hack supports that.
|
||||
template <ConstructionFailureChance kCfc, uint64_t kCoeffBits, bool kUseSmash, |
||||
bool kHomogeneous, bool kIsSupported> |
||||
struct BandingConfigHelper1MaybeSupported { |
||||
public: |
||||
static uint32_t GetNumToAdd(uint32_t num_slots) { |
||||
// Unsupported
|
||||
assert(num_slots == 0); |
||||
(void)num_slots; |
||||
return 0; |
||||
} |
||||
|
||||
static uint32_t GetNumSlots(uint32_t num_to_add) { |
||||
// Unsupported
|
||||
assert(num_to_add == 0); |
||||
(void)num_to_add; |
||||
return 0; |
||||
} |
||||
}; |
||||
|
||||
// Base class for BandingConfigHelper1 and helper for BandingConfigHelper
|
||||
// with core implementations built on above data
|
||||
template <ConstructionFailureChance kCfc, uint64_t kCoeffBits, bool kUseSmash, |
||||
bool kHomogeneous> |
||||
struct BandingConfigHelper1MaybeSupported< |
||||
kCfc, kCoeffBits, kUseSmash, kHomogeneous, true /* kIsSupported */> { |
||||
public: |
||||
// See BandingConfigHelper1. Implementation in ribbon_config.cc
|
||||
static uint32_t GetNumToAdd(uint32_t num_slots); |
||||
|
||||
// See BandingConfigHelper1. Implementation in ribbon_config.cc
|
||||
static uint32_t GetNumSlots(uint32_t num_to_add); |
||||
}; |
||||
|
||||
} // namespace detail
|
||||
|
||||
template <ConstructionFailureChance kCfc, uint64_t kCoeffBits, bool kUseSmash, |
||||
bool kHomogeneous> |
||||
struct BandingConfigHelper1 |
||||
: public detail::BandingConfigHelper1MaybeSupported< |
||||
kCfc, kCoeffBits, kUseSmash, kHomogeneous, |
||||
/* kIsSupported */ kCoeffBits == 64 || kCoeffBits == 128> { |
||||
public: |
||||
// Returns a number of entries that can be added to a given number of
|
||||
// slots, with roughly kCfc chance of construction failure per seed,
|
||||
// or better. Does NOT do rounding for InterleavedSoln; call
|
||||
// RoundUpNumSlots for that.
|
||||
//
|
||||
// inherited:
|
||||
// static uint32_t GetNumToAdd(uint32_t num_slots);
|
||||
|
||||
// Returns a number of slots for a given number of entries to add
|
||||
// that should have roughly kCfc chance of construction failure per
|
||||
// seed, or better. Does NOT do rounding for InterleavedSoln; call
|
||||
// RoundUpNumSlots for that.
|
||||
//
|
||||
// num_to_add should not exceed roughly 2/3rds of the maximum value
|
||||
// of the uint32_t type to avoid overflow.
|
||||
//
|
||||
// inherited:
|
||||
// static uint32_t GetNumSlots(uint32_t num_to_add);
|
||||
}; |
||||
|
||||
// Configured using TypesAndSettings as in ribbon_impl.h
|
||||
template <ConstructionFailureChance kCfc, class TypesAndSettings> |
||||
struct BandingConfigHelper1TS |
||||
: public BandingConfigHelper1< |
||||
kCfc, |
||||
/* kCoeffBits */ sizeof(typename TypesAndSettings::CoeffRow) * 8U, |
||||
TypesAndSettings::kUseSmash, TypesAndSettings::kHomogeneous> {}; |
||||
|
||||
// Like BandingConfigHelper1TS except failure chance can be a runtime rather
|
||||
// than compile time value.
|
||||
template <class TypesAndSettings> |
||||
struct BandingConfigHelper { |
||||
public: |
||||
static constexpr ConstructionFailureChance kDefaultFailureChance = |
||||
TypesAndSettings::kHomogeneous ? kOneIn1000 : kOneIn20; |
||||
|
||||
static uint32_t GetNumToAdd( |
||||
uint32_t num_slots, |
||||
ConstructionFailureChance max_failure = kDefaultFailureChance) { |
||||
switch (max_failure) { |
||||
default: |
||||
assert(false); |
||||
FALLTHROUGH_INTENDED; |
||||
case kOneIn20: { |
||||
using H1 = BandingConfigHelper1TS<kOneIn20, TypesAndSettings>; |
||||
return H1::GetNumToAdd(num_slots); |
||||
} |
||||
case kOneIn2: { |
||||
using H1 = BandingConfigHelper1TS<kOneIn2, TypesAndSettings>; |
||||
return H1::GetNumToAdd(num_slots); |
||||
} |
||||
case kOneIn1000: { |
||||
using H1 = BandingConfigHelper1TS<kOneIn1000, TypesAndSettings>; |
||||
return H1::GetNumToAdd(num_slots); |
||||
} |
||||
} |
||||
} |
||||
|
||||
static uint32_t GetNumSlots( |
||||
uint32_t num_to_add, |
||||
ConstructionFailureChance max_failure = kDefaultFailureChance) { |
||||
switch (max_failure) { |
||||
default: |
||||
assert(false); |
||||
FALLTHROUGH_INTENDED; |
||||
case kOneIn20: { |
||||
using H1 = BandingConfigHelper1TS<kOneIn20, TypesAndSettings>; |
||||
return H1::GetNumSlots(num_to_add); |
||||
} |
||||
case kOneIn2: { |
||||
using H1 = BandingConfigHelper1TS<kOneIn2, TypesAndSettings>; |
||||
return H1::GetNumSlots(num_to_add); |
||||
} |
||||
case kOneIn1000: { |
||||
using H1 = BandingConfigHelper1TS<kOneIn1000, TypesAndSettings>; |
||||
return H1::GetNumSlots(num_to_add); |
||||
} |
||||
} |
||||
} |
||||
}; |
||||
|
||||
} // namespace ribbon
|
||||
|
||||
} // namespace ROCKSDB_NAMESPACE
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in new issue