-
Notifications
You must be signed in to change notification settings - Fork 438
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
[SDK] Add base2 exponential histogram indexer (#2173)
- Loading branch information
Ruslan Nigmatullin
authored
Jun 3, 2023
1 parent
41416ce
commit 082bb93
Showing
6 changed files
with
312 additions
and
0 deletions.
There are no files selected for viewing
46 changes: 46 additions & 0 deletions
46
sdk/include/opentelemetry/sdk/metrics/aggregation/base2_exponential_histogram_indexer.h
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,46 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#pragma once | ||
|
||
#include "opentelemetry/version.h" | ||
|
||
#include <cstdint> | ||
|
||
OPENTELEMETRY_BEGIN_NAMESPACE | ||
namespace sdk | ||
{ | ||
namespace metrics | ||
{ | ||
|
||
/* | ||
* An indexer for base2 exponential histograms. It is used to calculate index for a given value and | ||
* scale. | ||
*/ | ||
class Base2ExponentialHistogramIndexer | ||
{ | ||
public: | ||
/* | ||
* Construct a new indexer for a given scale. | ||
*/ | ||
explicit Base2ExponentialHistogramIndexer(int32_t scale = 0); | ||
Base2ExponentialHistogramIndexer(const Base2ExponentialHistogramIndexer &other) = default; | ||
Base2ExponentialHistogramIndexer &operator=(const Base2ExponentialHistogramIndexer &other) = | ||
default; | ||
|
||
/** | ||
* Compute the index for the given value. | ||
* | ||
* @param value Measured value (must be non-zero). | ||
* @return the index of the bucket which the value maps to. | ||
*/ | ||
int32_t ComputeIndex(double value) const; | ||
|
||
private: | ||
int32_t scale_; | ||
double scale_factor_; | ||
}; | ||
|
||
} // namespace metrics | ||
} // namespace sdk | ||
OPENTELEMETRY_END_NAMESPACE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
71 changes: 71 additions & 0 deletions
71
sdk/src/metrics/aggregation/base2_exponential_histogram_indexer.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#include "opentelemetry/sdk/metrics/aggregation/base2_exponential_histogram_indexer.h" | ||
|
||
#include <cmath> | ||
|
||
OPENTELEMETRY_BEGIN_NAMESPACE | ||
namespace sdk | ||
{ | ||
namespace metrics | ||
{ | ||
|
||
namespace | ||
{ | ||
|
||
const double kLogBase2E = 1.0 / std::log(2.0); | ||
|
||
double ComputeScaleFactor(int32_t scale) | ||
{ | ||
return std::scalbn(kLogBase2E, scale); | ||
} | ||
|
||
// Compute the bucket index using a logarithm based approach. | ||
int32_t GetIndexByLogarithm(double value, double scale_factor) | ||
{ | ||
return static_cast<int32_t>(std::ceil(std::log(value) * scale_factor)) - 1; | ||
} | ||
|
||
// Compute the bucket index at scale 0. | ||
int32_t MapToIndexScaleZero(double value) | ||
{ | ||
// Note: std::frexp() rounds submnormal values to the smallest normal value and returns an | ||
// exponent corresponding to fractions in the range [0.5, 1), whereas an exponent for the range | ||
// [1, 2), so subtract 1 from the exponent immediately. | ||
int exp; | ||
double frac = std::frexp(value, &exp); | ||
exp--; | ||
|
||
if (frac == 0.5) | ||
{ | ||
// Special case for powers of two: they fall into the bucket numbered one less. | ||
exp--; | ||
} | ||
return exp; | ||
} | ||
|
||
} // namespace | ||
|
||
Base2ExponentialHistogramIndexer::Base2ExponentialHistogramIndexer(int32_t scale) | ||
: scale_(scale), scale_factor_(scale > 0 ? ComputeScaleFactor(scale) : 0) | ||
{} | ||
|
||
int32_t Base2ExponentialHistogramIndexer::ComputeIndex(double value) const | ||
{ | ||
const double abs_value = std::fabs(value); | ||
// For positive scales, compute the index by logarithm, which is simpler but may be | ||
// inaccurate near bucket boundaries | ||
if (scale_ > 0) | ||
{ | ||
return GetIndexByLogarithm(abs_value, scale_factor_); | ||
} | ||
// For scale zero, compute the exact index by extracting the exponent. | ||
// For negative scales, compute the exact index by extracting the exponent and shifting it to | ||
// the right by -scale | ||
return MapToIndexScaleZero(abs_value) >> -scale_; | ||
} | ||
|
||
} // namespace metrics | ||
} // namespace sdk | ||
OPENTELEMETRY_END_NAMESPACE |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
178 changes: 178 additions & 0 deletions
178
sdk/test/metrics/base2_exponential_histogram_indexer_test.cc
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
// Copyright The OpenTelemetry Authors | ||
// SPDX-License-Identifier: Apache-2.0 | ||
|
||
#include "opentelemetry/sdk/metrics/aggregation/base2_exponential_histogram_indexer.h" | ||
|
||
#include <gtest/gtest.h> | ||
|
||
using namespace opentelemetry::sdk::metrics; | ||
|
||
TEST(Base2ExponentialHistogramIndexerTest, ScaleOne) | ||
{ | ||
const Base2ExponentialHistogramIndexer indexer{1}; | ||
auto compute_index = [indexer](double value) { return indexer.ComputeIndex(value); }; | ||
|
||
EXPECT_EQ(compute_index(std::numeric_limits<double>::max()), 2047); | ||
EXPECT_EQ(compute_index(strtod("0x1p1023", nullptr)), 2045); | ||
EXPECT_EQ(compute_index(strtod("0x1.1p1023", nullptr)), 2046); | ||
EXPECT_EQ(compute_index(strtod("0x1p1022", nullptr)), 2043); | ||
EXPECT_EQ(compute_index(strtod("0x1.1p1022", nullptr)), 2044); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1022", nullptr)), -2045); | ||
EXPECT_EQ(compute_index(strtod("0x1.1p-1022", nullptr)), -2044); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1021", nullptr)), -2043); | ||
EXPECT_EQ(compute_index(strtod("0x1.1p-1021", nullptr)), -2042); | ||
EXPECT_EQ(compute_index(std::numeric_limits<double>::min()), -2045); | ||
EXPECT_EQ(compute_index(std::numeric_limits<double>::denorm_min()), -2149); | ||
EXPECT_EQ(compute_index(15.0), 7); | ||
EXPECT_EQ(compute_index(9.0), 6); | ||
EXPECT_EQ(compute_index(7.0), 5); | ||
EXPECT_EQ(compute_index(5.0), 4); | ||
EXPECT_EQ(compute_index(3.0), 3); | ||
EXPECT_EQ(compute_index(2.5), 2); | ||
EXPECT_EQ(compute_index(1.5), 1); | ||
EXPECT_EQ(compute_index(1.2), 0); | ||
EXPECT_EQ(compute_index(1.0), -1); | ||
EXPECT_EQ(compute_index(0.75), -1); | ||
EXPECT_EQ(compute_index(0.55), -2); | ||
EXPECT_EQ(compute_index(0.45), -3); | ||
} | ||
|
||
TEST(Base2ExponentialHistogramIndexerTest, ScaleZero) | ||
{ | ||
const Base2ExponentialHistogramIndexer indexer{0}; | ||
auto compute_index = [indexer](double value) { return indexer.ComputeIndex(value); }; | ||
|
||
// Near +Inf. | ||
// Use constant for exp, as max_exponent constant includes bias. | ||
EXPECT_EQ(compute_index(std::numeric_limits<double>::max()), 1023); | ||
EXPECT_EQ(compute_index(strtod("0x1p1023", nullptr)), 1022); | ||
EXPECT_EQ(compute_index(strtod("0x1.1p1023", nullptr)), 1023); | ||
EXPECT_EQ(compute_index(strtod("0x1p1022", nullptr)), 1021); | ||
EXPECT_EQ(compute_index(strtod("0x1.1p1022", nullptr)), 1022); | ||
// Near 0. | ||
EXPECT_EQ(compute_index(strtod("0x1p-1022", nullptr)), -1023); | ||
EXPECT_EQ(compute_index(strtod("0x1.1p-1022", nullptr)), -1022); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1021", nullptr)), -1022); | ||
EXPECT_EQ(compute_index(strtod("0x1.1p-1021", nullptr)), -1021); | ||
EXPECT_EQ(compute_index(std::numeric_limits<double>::min()), -1023); | ||
EXPECT_EQ(compute_index(std::numeric_limits<double>::denorm_min()), -1075); | ||
// Near 1. | ||
EXPECT_EQ(compute_index(4.0), 1); | ||
EXPECT_EQ(compute_index(3.0), 1); | ||
EXPECT_EQ(compute_index(2.0), 0); | ||
EXPECT_EQ(compute_index(1.5), 0); | ||
EXPECT_EQ(compute_index(1.0), -1); | ||
EXPECT_EQ(compute_index(0.75), -1); | ||
EXPECT_EQ(compute_index(0.51), -1); | ||
EXPECT_EQ(compute_index(0.5), -2); | ||
EXPECT_EQ(compute_index(0.26), -2); | ||
EXPECT_EQ(compute_index(0.25), -3); | ||
EXPECT_EQ(compute_index(0.126), -3); | ||
EXPECT_EQ(compute_index(0.125), -4); | ||
} | ||
|
||
TEST(Base2ExponentialHistogramIndexerTest, ScaleNegativeOne) | ||
{ | ||
const Base2ExponentialHistogramIndexer indexer{-1}; | ||
auto compute_index = [indexer](double value) { return indexer.ComputeIndex(value); }; | ||
|
||
EXPECT_EQ(compute_index(17.0), 2); | ||
EXPECT_EQ(compute_index(16.0), 1); | ||
EXPECT_EQ(compute_index(15.0), 1); | ||
EXPECT_EQ(compute_index(9.0), 1); | ||
EXPECT_EQ(compute_index(8.0), 1); | ||
EXPECT_EQ(compute_index(5.0), 1); | ||
EXPECT_EQ(compute_index(4.0), 0); | ||
EXPECT_EQ(compute_index(3.0), 0); | ||
EXPECT_EQ(compute_index(2.0), 0); | ||
EXPECT_EQ(compute_index(1.5), 0); | ||
EXPECT_EQ(compute_index(1.0), -1); | ||
EXPECT_EQ(compute_index(0.75), -1); | ||
EXPECT_EQ(compute_index(0.5), -1); | ||
EXPECT_EQ(compute_index(0.25), -2); | ||
EXPECT_EQ(compute_index(0.20), -2); | ||
EXPECT_EQ(compute_index(0.13), -2); | ||
EXPECT_EQ(compute_index(0.125), -2); | ||
EXPECT_EQ(compute_index(0.10), -2); | ||
EXPECT_EQ(compute_index(0.0625), -3); | ||
EXPECT_EQ(compute_index(0.06), -3); | ||
} | ||
|
||
TEST(Base2ExponentialHistogramIndexerTest, ScaleNegativeFour) | ||
{ | ||
const Base2ExponentialHistogramIndexer indexer{-4}; | ||
auto compute_index = [indexer](double value) { return indexer.ComputeIndex(value); }; | ||
|
||
EXPECT_EQ(compute_index(strtod("0x1p0", nullptr)), -1); | ||
EXPECT_EQ(compute_index(strtod("0x10p0", nullptr)), 0); | ||
EXPECT_EQ(compute_index(strtod("0x100p0", nullptr)), 0); | ||
EXPECT_EQ(compute_index(strtod("0x1000p0", nullptr)), 0); | ||
EXPECT_EQ(compute_index(strtod("0x10000p0", nullptr)), 0); // Base == 2**16 | ||
EXPECT_EQ(compute_index(strtod("0x100000p0", nullptr)), 1); | ||
EXPECT_EQ(compute_index(strtod("0x1000000p0", nullptr)), 1); | ||
EXPECT_EQ(compute_index(strtod("0x10000000p0", nullptr)), 1); | ||
EXPECT_EQ(compute_index(strtod("0x100000000p0", nullptr)), 1); // == 2**32 | ||
EXPECT_EQ(compute_index(strtod("0x1000000000p0", nullptr)), 2); | ||
EXPECT_EQ(compute_index(strtod("0x10000000000p0", nullptr)), 2); | ||
EXPECT_EQ(compute_index(strtod("0x100000000000p0", nullptr)), 2); | ||
EXPECT_EQ(compute_index(strtod("0x1000000000000p0", nullptr)), 2); // 2**48 | ||
EXPECT_EQ(compute_index(strtod("0x10000000000000p0", nullptr)), 3); | ||
EXPECT_EQ(compute_index(strtod("0x100000000000000p0", nullptr)), 3); | ||
EXPECT_EQ(compute_index(strtod("0x1000000000000000p0", nullptr)), 3); | ||
EXPECT_EQ(compute_index(strtod("0x10000000000000000p0", nullptr)), 3); // 2**64 | ||
EXPECT_EQ(compute_index(strtod("0x100000000000000000p0", nullptr)), 4); | ||
EXPECT_EQ(compute_index(strtod("0x1000000000000000000p0", nullptr)), 4); | ||
EXPECT_EQ(compute_index(strtod("0x10000000000000000000p0", nullptr)), 4); | ||
EXPECT_EQ(compute_index(strtod("0x100000000000000000000p0", nullptr)), 4); | ||
EXPECT_EQ(compute_index(strtod("0x1000000000000000000000p0", nullptr)), 5); | ||
EXPECT_EQ(compute_index(1 / strtod("0x1p0", nullptr)), -1); | ||
EXPECT_EQ(compute_index(1 / strtod("0x10p0", nullptr)), -1); | ||
EXPECT_EQ(compute_index(1 / strtod("0x100p0", nullptr)), -1); | ||
EXPECT_EQ(compute_index(1 / strtod("0x1000p0", nullptr)), -1); | ||
EXPECT_EQ(compute_index(1 / strtod("0x10000p0", nullptr)), -2); // 2**-16 | ||
EXPECT_EQ(compute_index(1 / strtod("0x100000p0", nullptr)), -2); | ||
EXPECT_EQ(compute_index(1 / strtod("0x1000000p0", nullptr)), -2); | ||
EXPECT_EQ(compute_index(1 / strtod("0x10000000p0", nullptr)), -2); | ||
EXPECT_EQ(compute_index(1 / strtod("0x100000000p0", nullptr)), -3); // 2**-32 | ||
EXPECT_EQ(compute_index(1 / strtod("0x1000000000p0", nullptr)), -3); | ||
EXPECT_EQ(compute_index(1 / strtod("0x10000000000p0", nullptr)), -3); | ||
EXPECT_EQ(compute_index(1 / strtod("0x100000000000p0", nullptr)), -3); | ||
EXPECT_EQ(compute_index(1 / strtod("0x1000000000000p0", nullptr)), -4); // 2**-48 | ||
EXPECT_EQ(compute_index(1 / strtod("0x10000000000000p0", nullptr)), -4); | ||
EXPECT_EQ(compute_index(1 / strtod("0x100000000000000p0", nullptr)), -4); | ||
EXPECT_EQ(compute_index(1 / strtod("0x1000000000000000p0", nullptr)), -4); | ||
EXPECT_EQ(compute_index(1 / strtod("0x10000000000000000p0", nullptr)), -5); // 2**-64 | ||
EXPECT_EQ(compute_index(1 / strtod("0x100000000000000000p0", nullptr)), -5); | ||
// Max values. | ||
EXPECT_EQ(compute_index(0x1.FFFFFFFFFFFFFp1023), 63); | ||
EXPECT_EQ(compute_index(strtod("0x1p1023", nullptr)), 63); | ||
EXPECT_EQ(compute_index(strtod("0x1p1019", nullptr)), 63); | ||
EXPECT_EQ(compute_index(strtod("0x1p1009", nullptr)), 63); | ||
EXPECT_EQ(compute_index(strtod("0x1p1008", nullptr)), 62); | ||
EXPECT_EQ(compute_index(strtod("0x1p1007", nullptr)), 62); | ||
EXPECT_EQ(compute_index(strtod("0x1p1000", nullptr)), 62); | ||
EXPECT_EQ(compute_index(strtod("0x1p0993", nullptr)), 62); | ||
EXPECT_EQ(compute_index(strtod("0x1p0992", nullptr)), 61); | ||
EXPECT_EQ(compute_index(strtod("0x1p0991", nullptr)), 61); | ||
// Min and subnormal values. | ||
EXPECT_EQ(compute_index(strtod("0x1p-1074", nullptr)), -68); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1073", nullptr)), -68); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1072", nullptr)), -68); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1057", nullptr)), -67); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1056", nullptr)), -67); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1041", nullptr)), -66); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1040", nullptr)), -66); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1025", nullptr)), -65); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1024", nullptr)), -65); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1023", nullptr)), -64); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1022", nullptr)), -64); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1009", nullptr)), -64); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1008", nullptr)), -64); | ||
EXPECT_EQ(compute_index(strtod("0x1p-1007", nullptr)), -63); | ||
EXPECT_EQ(compute_index(strtod("0x1p-0993", nullptr)), -63); | ||
EXPECT_EQ(compute_index(strtod("0x1p-0992", nullptr)), -63); | ||
EXPECT_EQ(compute_index(strtod("0x1p-0991", nullptr)), -62); | ||
EXPECT_EQ(compute_index(strtod("0x1p-0977", nullptr)), -62); | ||
EXPECT_EQ(compute_index(strtod("0x1p-0976", nullptr)), -62); | ||
EXPECT_EQ(compute_index(strtod("0x1p-0975", nullptr)), -61); | ||
} |