-
Notifications
You must be signed in to change notification settings - Fork 264
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add more histogram types #226
Conversation
Regarding the polynomial interpolations, we'd like to put forward arguments in favor of supporting them:
I think that the polynomial interpolations (between powers of 2) that minimize the memory footprint while ensuring the relative accuracy guarantee of the quantiles can be naturally favored.
See the rationale for the coefficients of the cubic interpolation in DDSketch. The method for getting the optimal coefficients of the quadratic interpolation is similar.
Probably true to some extent, although we are already doing it for log-linear and the mapping logic can be reduced to a few self-contained lines of codes.
The quadratic and especially the cubic interpolations memory footprints are comparable to the optimal logarithmic mapping (8% and 1% overhead respectively), while being like the log-linear mapping considerably faster than the logarithmic mapping (typically around 3 or 4 times faster to record input values in the sketch), so we could refute it's really a trade-off. |
// When bounds have a pattern, a more efficient encoding method may be used. | ||
// Only one method should be defined. For performance reason, "oneof" is not used. | ||
// When more than one methods are defined, the receiver will use the first one found using this order: | ||
// explicit_bounds, linear_bounds, exponential_bounds, log_linear_bounds | ||
LinearBounds linear_bounds = 9; | ||
ExponentialBounds exponential_bounds = 10; | ||
LogLinearBounds log_linear_bounds = 11; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider to move this closer to explicit bounds, no need to keep the ids ordered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
new commit added to group all "bound" types together.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm having a bit of trouble w/ the use of oneof
in some places and not here. It's wire compatible, I believe, to say:
oneof {
LinearBounds linear_bounds = 9;
ExponentialBounds exponential_bounds = 10;
LogLinearBounds log_linear_bounds = 11;
}
if so, then given the savings implied by these special bounds, perhaps we can afford to move the three new bounds into a oneof
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ideally, we want:
oneof {
ExplicitBounds explicit_bounds = 8
LinearBounds linear_bounds = 9;
ExponentialBounds exponential_bounds = 10;
LogLinearBounds log_linear_bounds = 11;
}
message ExplicitBounds {
repeated double explicit_bounds = 7
}
But " repeated double explicit_bounds = 7" is already at the top level. Though we could remove it at the cost of breaking compatibility (I heard compatibility is nice to have, but not required)
The bigger question is performance. @bogdandrutu said that oneof performance is so horrible that we should avoid it if we can.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@bogdandrutu We have a few good questions to answer here, particularly about the oneof
. I don't think we should bother moving field 7 or breaking compatibility, though it's sort of like keeping the worst of both worlds. 😞
One of the questions that I found troublesome when converting to or from Prometheus is that the "le" bounds contradict the [bounds[i-1], bounds[i])
statements here.
(I still have to catch up on the youtube video above...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
field order is a pain. We have these options:
- Append new fields at the end, keeping field number order, but breaking logical grouping of fields. (my 1st version)
- Insert new fields to their logical groups, breaking field number order. (my 2nd version, responding to Bogdan's comment)
- Group fields logically and update field number in sequential order, breaking wire backward compatibility
So no option is ideal. Option 1 and 2 have no impact once compiled, it's just for human readers. So changing between 1 and 2 is easy. We need to be careful on choosing 3.
As for "le" vs. "ge" bounds, I remember we basically punted on it, saying "we could add an optional boolean flag later". The bigger issue is how will a message receiver handles it. If a receiver is natively "le", it is difficult to convert incoming "ge" buckets to "le", and vise versa. The good news is that in practice, ge vs le usually has little impact on "continuous" number set, where we are concerned on the area under a histogram curve, between two bucket bounds. As long as "area" is concerned, inclusiveness/exclusiveness at a bound is irrelevant. Even for discrete data set, le/ge will only introduce errors no more than the width of a bucket (if population count is mistakenly included to a neighboring bucket, it would introduce an error no wider than the bucket width for quantile calculation). So I think we can continue to punt the le/ge question.
I think we are close to approval on this PR. There are only minor things like field order and "oneof". @bogdandrutu I'd like to hear from you and get this PR resolved soon.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I vote for a oneof. Option 2 is fine with me.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that to keep backward compatibility, we can only have
repeated double explicit_bounds = 7;
oneof bound_type {
LinearBounds linear_bounds = 9;
ExponentialBounds exponential_bounds = 10;
LogLinearBounds log_linear_bounds = 11;
}
explicit_bounds is a preexisting field. Although existing single field can be included in a new oneof without breaking backward compatibility, existing "repeated" cannot. In fact, "repeated" cannot be oneof member at all.
So with oneof, we still leave out explicit_bounds. If we don't mind breaking compatibility, we can do:
oneof bound_type {
ExplicitBounds explicit_bounds = 8;
LinearBounds linear_bounds = 9;
ExponentialBounds exponential_bounds = 10;
LogLinearBounds log_linear_bounds = 11;
}
message ExplicitBounds {
repeated double explicit_bounds = 1;
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with your first suggestion, the backward-compatible one:
repeated double explicit_bounds = 7;
oneof bound_type {
LinearBounds linear_bounds = 9;
ExponentialBounds exponential_bounds = 10;
LogLinearBounds log_linear_bounds = 11;
}
If we're going to break the protocol in any way, I'd fix this however. If we decide to break the protocol because of labels vs attributes, maybe?
I still think that quadratic and cubic forms are not quite ready to be included in the standard at this moment. To be standard ready, I think we have a check list to do:
Even with all these technical items above resolved, how the OTel community will think of the requirement for all metric receivers to decode log-quadratic and log-cubic is still open. And all producers SDKs should (but not must) produce the new formats. It's still an open business question. As this protocol stands today, it does not preclude future extension to add quadratic and/or cubic forms. I think we should go one step at a time and do the simple form (log and log-linear) first. I suggest that the quadratic and cubic issue be taken out of this PR discussion, and leave as research and potential future items, though I am not sure on the venue to continue the exploration (an OTel issue?). |
@jmacd can you review this PR? |
At today's meeting Theo S. (@postwait) mentioned this video about Prometheus histogram support: https://www.youtube.com/watch?v=HG7uzON-IDM I'd like to watch this before commenting further here, but generally I'm excited that we seem to have agreed on the shape of these new bounds options. I'm not sure I've followed what protocol structures are needed and what additional code will be needed in all the place that may have to interpret histogram bounds, if we are to add one of these other interpolation techniques. @yzhuge or @CharlesMasson would you spell it out for us? Thanks. |
@jmacd I watched the youtube video. The proposed new histogram format for Prometheus has exponential bounds. So it can be efficiently encoded in the proposed protobuf format here. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should the protocol support a sparse encoding of counts as well?
Generally, telemetry such as response time spans a range continuously (no or few empty buckets from min to max). So the buckets end up being dense. So I think we should just start from the simple case of dense counter arrays. |
My advice would to use sparsely encoded bin/count tuples. Of course, my advice is also to fix on a universal set of bins so that users are left with an absolute mess they cannot effective use for calculations. It is unreasonable to expect even the best of data scientists to cope well with the statistical error and bias introduced in the re-binning required to translate from one set of arbitrary bins to another. Making this protocol format flexible is a great engineering approach that ignores the math problems that persist underneath everything and isn't in the best interest of end-users. |
Can we also add |
+1 There are standing requests to include |
We (Google) have identified a scheme for exponentially bucketed histograms that ensures that any two histograms belonging to that scheme can be rebucketed and merged in a way that boundaries of the merged histogram will align with the old histogram and all values will be accurately assigned to the correct buckets in the new histogram. I am hoping that we will be able to include the parameters required for this alternate approach as one of the bucketing types. The parameters required are:
Knowing minimum_base and compression, you can derive the traditional exponential base as:
To harmonize with the proposed exponential distribution parameters which allow differentiating negative and positive buckets, compression and index_offset could be split into negative_compression/positive_compression and negative_index_offset/positive_indexx_offset. Although this is also an exponential bucketer, the addition of the minimum_base and compression would give the monitoring backend additional information about whether/how two histograms with exponential bucketers can be merged without losing accuracy, and with deterministic loss of precision. Monitoring backends that do not support the additional alignment features associated with these parameters and do not plan to use them directly could easily just convert to traditional exponential parameters during export, so there is no risk of backend compatibility issues. This is outside the scope of this PR, but we will be sharing an algorithm and open source implementation for dynamically (re-)bucketing histograms under the aligned histogram scheme I described above so that it is possible to record sample distributions with bounded memory, unlimited range, and guaranteed tradeoffs between range and precision. Our algorithm is somewhat similar in principle to DDSketch, but instead of using fixed precision and allowing unbounded memory and range (or using fixed precision and allowing unbounded memory but limited range, as in one of the alternative bounded-memory DDSketch implementations), our algorithm bounds memory and allows precision to fall as the range grows to cover new observations. Our motivation for using this instead of alternatives such as DDSketch is that we have metrics in shared libraries that are widely used by internal users that have modestly high cardinality, so converting these metrics to a histogram scheme like DDSketch that allows unlimited number of buckets isn't feasible, and the bounded-memory approach of discarding lower buckets to bound memory also does not work for us because these buckets may include SLI's. Our proposed approach allows us to continue using the same number of buckets as in the low-preciion fixed-bucket histograms to cover the same range in the worst case, while providing much better precision for the vast majority of cases where observed values tend to be clustered into a much smaller range. This is an unambiguous improvement with few downsides over the low-precision fixed-bucketed histograms, which allows it to be dropped in as a replacement without risking memory regressions. We expect that some other folks might be facing the same constraints that are preventing us from switching to using DDSketch, and could find this approach useful. |
Given two exponential histograms with base1 and base2, and identical "reference", if Theoretically, N can be computed given base1 and 2. But because of inaccuracy in floating point math, we cannot always accurately determine if N is an integer. As a work around, a processor may snap it to an integer when it is “close enough”. This way a backend can merge histograms transported via standard exponential format. I suggest starting from standard exponential form, and leave additional parameters to future work. One drawback of computing base from Exponential bound calculation using a given base already faces the math variation problem (so far we pretend it does not exist). Any inaccuracy in “base” itself will be magnified by bound calculations. So I hesitate to introduce more complexity and source of computational error. |
This seems reasonable to me - as you mentioned, in spite of floating point imprecision, we can determine whether traditional exponential bucketing parameters are "close enough" to aligning for now. Thanks for your feedback! |
Ah I had missed the existing sum and count. Can we add |
@githomin we discussed min/max in meetings. The decision was to handle it in another PR, to keep each PR focused. Min/max goes beyond histogram, some other structures may need min/max too. It needs some holistic consideration. As for "bump the rest of the numbers up by 2", our approach is to favor backward compatibility, keeping existing fields' number unchanged. There is no ideal option here. See #226 (comment) |
// if (i >= 0) { | ||
// exponent = i / num_of_linear_subbuckets; | ||
// subbucketNumber = i % num_of_linear_subbuckets; | ||
// } else { | ||
// exponent = (i - num_of_linear_subbuckets + 1) / num_of_linear_subbuckets; // Round down toward -infinity | ||
// subbucketNumber = i - exponent * num_of_linear_subbuckets; | ||
// } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It takes a while to comprehend and it makes me think a comment would help. I suggest:
// Set exponent and subbucketNumber according to i. For index (i >= 0) we are setting
// a (bound >= 1), and for (i < 0) we are setting a (0 < bound < 1).
// When i == 0: exponent == 0 and subbucketNumber == 0
// When i == -1: exponent == -1 and subbucketNumber == (num_of_linear_subbuckets - 1)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On line 586: A comment like
// exponent is negative, same as (i + abs(exponent) * num_of_linear_subbuckets)
I'm not sure that helps, but it may have helped me.
// ExponentialBounds. Bounds are on log scale. | ||
// The bound sequence is stitched together using negative bounds, zero bound, and positive bounds. | ||
// The formula to generate explicit bounds here are for demonstration purpose only. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think a bit more explanation will help the reader into this description. This might have helped me:
// We use only integer-value exponents.
// Exponent 0 maps to value == 1.
// Exponents > maps to values > 1.
// Exponents < 0 map to values > 0 and < 1.
// No exponent maps to zero, therefore we treat it as a special bucket of its own.
// A "sequence" of integer exponents is described by two parameters, described
// by `index_offset_for_` and `num_of_bounds_for_`-prefixed fields. A sequence
// thus describes a range of values > 0.
// This encoding consists of two sequences, one describing positive values and
// one describing negative values, plus an optional zero-valued bucket.
// When bounds have a pattern, a more efficient encoding method may be used. | ||
// Only one method should be defined. For performance reason, "oneof" is not used. | ||
// When more than one methods are defined, the receiver will use the first one found using this order: | ||
// explicit_bounds, linear_bounds, exponential_bounds, log_linear_bounds | ||
LinearBounds linear_bounds = 9; | ||
ExponentialBounds exponential_bounds = 10; | ||
LogLinearBounds log_linear_bounds = 11; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm OK with your first suggestion, the backward-compatible one:
repeated double explicit_bounds = 7;
oneof bound_type {
LinearBounds linear_bounds = 9;
ExponentialBounds exponential_bounds = 10;
LogLinearBounds log_linear_bounds = 11;
}
If we're going to break the protocol in any way, I'd fix this however. If we decide to break the protocol because of labels vs attributes, maybe?
@@ -503,9 +500,107 @@ message DoubleHistogramDataPoint { | |||
// a boolean value which decides what type of intervals to use. | |||
repeated double explicit_bounds = 7; | |||
|
|||
// When bounds have a pattern, a more efficient encoding method may be used. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There may be a technicality that we need to add above. I'm interested in the zero "bucket" of an exponential bounds. If there are no negative values in the distribution, but there are zeros, how will it be represented? Do we need to insert an unused 0 boundary into the sequence so that the corresponding explicit bounds are (-Inf, 0), [0, x)
and the 0th bucket goes unused?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, you need set the has_counter_for_zero flag in exponential format so that the bound sequence starts from 0.
@yzhuge ?I'm ready to approve, but I think I'd prefer the oneof approach. @open-telemetry/specs-metrics-approvers please review! |
// If the message receiver natively stores exponential histograms, it will not need to generate explicit bounds. | ||
// It will only need to record the exponential bound parameters. | ||
message ExponentialBounds { | ||
double reference = 1; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
May we added that if reference
is omitted, it's value may be presumed to equal 1?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
comment accepted.
@oertl A good example is worth a thousand words. |
Another concern I have regarding |
@oertl yes, there is a reason. Mathematically, we can set reference to the smallest bound and declare its offset being always zero (therefore omitted from the protocol). But "double" may not be able to exactly represent the small value. In contrast, "reference" is typically chosen at some "special" number such as 1 that can be exactly represented. Thus the protocol chose to use a reference and an index_offset_for_positive_numbers. |
@yzhuge I do not follow your argumentation. The implicit definition of the smallest bound as When defining the boundaries implicitly (as with |
@oertl I meant that if we use the smallest bound as reference, any inaccuracy on it will be amplified toward the bigger end. Thus defining a (typically) precise reference is preferred. |
@yzhuge I still don't get it. The only "advantage" I see in having two parameters By the way, the problem of numerical inaccuracies also exists for |
@oertl In exponential series, bound = reference * (base ^ index)
In linear series, bound = start + width * index In exponential series, unless base is integer, it is hard to make all bounds exactly representable by double. So we need to be sure to at least represent the key parameters exactly. |
@yzhuge Now it becomes clearer since you mentioned that the first boundary will be data dependent. I think the specification for
I believe that discretization and data compaction are orthogonal to each other. Therefore, the histogram specification should allow choosing the discretization and the compaction strategy independently. Given that, I have following concerns regarding
|
I've read the above, and I begin to fear that very few readers will make it to the bottom of this thread. I am not sure we can make effective progress by continuing the discussion in this PR. @yzhuge Thank you for your effort here and for working with @CharlesMasson, @githomin and the DDSketch authors. Thank you @postwait and @oertl for joining this discussion; you've both brought up new considerations. Let me summarize where we stand. What do users want?I think the unifying goal across all of our histogram efforts is that users should not have to configure histogram boundaries. Some will say our goal is high resolution or low error ratio, but I see these as secondary goals. Min and MaxThere is a standing proposal to introduce Min and Max fields to the histogram data point. ExplicitBoundsThe existing ExplicitBounds field will be moved inside the This format address both discretization and compaction, using the @oertl's terminology. These histograms allow for infinite-width buckets and prevent zero-width buckets, two features making them difficult to convert into the proposed Exponential representation of this PR. There is also @postwait's concern: no histogram format is suitable for merging arbitrary histogram boundaries. Therefore we believe this format is only suitable in a few cases:
ExponentialBounds parametersAs @oertl notes, there is a concern about the use of "data-dependent parameters", again bringing up @postwait's concern:
The idea here is that we are all worse off for choosing arbitrary parameters, no matter which histogram representation is used. Even if we were to fix recommended parameters for use in an ExponentialBounds strategy, it appears to be most useful when used with a few hundred buckets. Exponential bounds are not a good approach for use with a small number of buckets, simply because it's difficult to find fixed, universal parameters for small histograms. Should we separate Discretization from Compaction?I do not believe we can satisfy all use-cases with a single histogram representation. Perhaps treating discretization and compaction separately will help, as @oertl suggests. Let's look at that. The Prometheus default bounds, quoted above, can be thought of as a discretization of the number line with 12 buckets, 10 finite and 2 infinite. The coordinates of those buckets can be mapped exactly into the Circllhist discretization (a case of the log-linear representation proposed by @yzhuge). The finite Prometheus histogram boundaries cover 320 Circlhist buckets (using its "universal" N=10/B=90 parameters), but it's not not very sensible to represent such coarse boundaries using those paraemters. It seems that more than one set of parameters is desireable, but arbitrary parameters are not. A few approaches have been mentioned, one by @jdmontana in this thread, and the other one UDDSketch. The overall theme of these developments can be illustrated using the Prometheus example in the paragraph above. The Prometheus boundaries can be exactly discretized using a B=10/N=90 Circlhist scheme (covering 320 buckets), and they can also be exactly discretized using a B=10/N=18 scheme (covering 64 buckets). These two schemes can be merged into the lower-resolution form exactly, without errors and bias (I think?). It begins to look like we should specify a small number of parameter values that should be used for exponential bucketing schemes, at the very least. Next step?I think we should:
@oertl Do you have ideas about how to separate discretization from compaction that avoid the problems with merging arbitrary parameters discussed here? @jdmontana does Google have any news to report on this topic? |
@jmacd Regarding merging, I can imagine 3 different approaches:
|
@jmacd I think it is a good idea to have a dedicated doc on the histogram data type, stating things like what is accepted, what is recommended, rationale, limitations, future work, etc. The doc will become part of the spec. It will be the guide for people working on histograms, be it in the front end, back end, or the transport protocol.
|
I think we should keep in mind the goal of providing histograms with relative accuracy guarantees where users don't have to set any parameters. I think ExponentialBounds in its simplest form with decent defaults achieves this. The issues brought up about floating point accuracy are less important when we think about them in terms of relative accuracy. E.g, if a point is so close to a bucket boundary that machine precision can determine which bucket they're in, it doesn't matter for the user as relative accuracy is maintained by either bucket. Merging won't be an issue for users that stick to the defaults, and if users choose to merge histograms of different types with different parameters, they should expect the results to be lossy. (I'm happy to move these comments to the new doc once it has a home.) |
@githomin I have been thinking about this a lot (about 8 years) and believe the currently proposed approach does not meet this goal. Conversion between different different binning methodologies (or boundaries) in almost all cases (all proposed) introduces error and given that all of this telemetry can forward (and thus convert more than once) the error becomes unbounded. It's a tragic failing in my opinion that should be strictly solved herein. I believe there are too many opinions here that will not give ground to accomplish this goal. We have a solution to this, but is seems to compete with agendas here. I'm hoping I can make a stronger case soon. |
If OTel standard puts restrictions on exponential histogram parameters, for example, base must be 1.01, 1.02, 1.05, 1.1, etc. I am afraid senders not meeting the restriction will simply use explicit bounds, which makes things worse in that it is still “non standard”, it cost more space, and it does not give the receiver information like “this is a base 1.03 exponential histogram”. Disallowing explicit bounds is probably out of the question. This is not to say that we cannot recommend certain parameters. My recommendation is to use bases that support 2 for 1 merging. A series of bases are recommended, where the next is the square of the previous. This is the same approach from UDDSketch and the Google scheme from @jdmontana. I will further pin the series on base 2 log scale. The general formula of recommended base is:
The advantages of pinning the series on base2 are in mapping a “double” to a bucket.
The main disadvantage is of course not supporting an arbitrary base. Base can only increase by square, or decrease by square root. For universally mergeable histogram, it is a reasonable trade off. Basically, my opinion is to “recommend”, but not “require” parameters. |
Not to derail this conversation any further, but there is parallel work in the Prometheus community: https://docs.google.com/document/d/1cLNv3aufPZb3fNfaJgdaRBZsInZKKIHo9E6HinJVbpM/edit#heading=h.2ywoyp13c0bx |
Note, though, that the linked document is about adding better histogram specifically to Prometheus, quite explicitly using merely a PoC exposition format until there is confidence how the final solution will look like so that the final treatment thereof in the exposition format will fit that solution optimally. This PR seems to have a very different goal, mainly to be able to map many different existing histogram representations into the proposed format. |
@yzhuge I think the best way to carry this forward would be through an OTEP document in the https://github.com/open-telemetry/oteps repository. There is a new issue to discuss how to make room for multiple bucketing schemes to co-exist, see Thank you all for this discussion. I believe @yzhuge the best we can do is close this PR and start over, it's been very constructive. I'm keen on your remarks here: #226 (comment) Thanks! |
@yzhuge Worth to mention, that N >= 0 will probably not be the standard case, because the relative error would be in the order of 100% or greater. For N < 0 many of the advantages (fast mapping, no floating-point errors) of your proposed base-2 bucketing do not apply. |
started working on an OTEP. Will close this PR and link to OTEP once I have EP link |
Work continues on open-telemetry/oteps#149 |
This is a PR for open-telemetry/opentelemetry-specification#982. Main changes
Terminology note: For exponential histograms, "base" is used for exponent base, consistent with standard math terminology. "Reference" is used for the multiplier on exponential scale, consistent with common usage in log scale unit such as decibel.
The proposed protocol is closely related to custom protocol of DDSketch (https://github.com/DataDog/sketches-java/blob/master/src/main/proto/DDSketch.proto). A DDsketch using the logarithm method can be represented as reference=1, base=gamma, index_offset=contiguousBinIndexOffset. A DDSketch using the "fast" option (https://github.com/DataDog/sketches-java/blob/master/src/main/java/com/datadoghq/sketch/ddsketch/mapping/BitwiseLinearlyInterpolatedMapping.java) can be represented as reference=1, base=2, num_linear_subbuckets=2^ numSignificantBinaryDigits. DDSketches using log approximation methods such as quadratic or cubic methods have to use the explicit bound encoding.
The rationale for supporting logarithm and log linear format is:
The rationale for not supporting quadratic and cubic log approximation is:
Open issues:
Protocol now uses "repeated fixed64 bucket_counts" for counters in buckets. Each counter always costs 8 bytes. Histograms often have counters repeating hundreds or thousands of times. A "varint" counter may be better. In most cases, a counter will need fewer than 4 bytes. A previous PR (Use fixed64 and sfixed64 for the sums and counts #214) changed counters from uint64 to fixed64. For counters occurring a few times, the space cost may not matter, for histogram counter list, we may need to reconsider.
There is talk about adding min and max to histogram. One argument for adding them is: Consider the case where we have fooHisto metric tracking histogram of "foo", and a fooSummary metric tracking max of "foo", we could get results like
percentile(fooHistogram, 100%) = 1000
max(fooSummary) = 900
In the result above, the returned 100% percentile is higher than the max. In fact, this could occur even before we reach 100%, in percentiles like 99.9%. If we have min/max included in histogram, the percentile function can internally clamp 100% to max, and 0% to min, returning more reasonable results.