fix(glam): Compute power of 2 buckets the same way Glean does - #9749
Conversation
There was a problem hiding this comment.
This changes glam.histogram_generate_functional_buckets to compute bucket minimums as POW(POW(log_base, 1 / buckets_per_magnitude), idx) instead of POW(log_base, idx / buckets_per_magnitude), so the floating-point rounding matches Glean's functional.rs, which precomputes the per-magnitude exponent once and raises it to the bucket index. A new assertion pins the (2, 16) power-of-two bucket minimums to their truncated values. The change is confined to one mozfun UDF, and the two existing assertions are left intact, which is good evidence that non-power-of-two buckets are unaffected.
Two comments inline, both about clarity rather than the math. One thing worth calling out for the merge plan rather than the diff: the only in-repo consumers are bigquery_etl/glam/templates/probe_counts_v1.sql and the two glam_etl__histogram_probe_counts_v1 queries, so partitions already built with the old bucket set will keep the old boundaries until those tables are rebuilt — worth deciding explicitly whether that needs a backfill or whether GLAM's full recompute covers it.
| FLOOR(POW(POW(log_base, 1 / buckets_per_magnitude), idx)) AS bucket | ||
| FROM | ||
| bucket_indexes, | ||
| UNNEST(indexes) AS idx | ||
| WHERE | ||
| FLOOR(POW(log_base, (idx) / buckets_per_magnitude)) <= range_max | ||
| FLOOR(POW(POW(log_base, 1 / buckets_per_magnitude), idx)) <= range_max |
There was a problem hiding this comment.
suggestion: Hoist the base into bucket_indexes instead of repeating the nested POW in both the SELECT and the WHERE. The two copies now have to be kept byte-identical by hand — if a future edit changes one and not the other, the filter and the emitted value silently disagree. Naming it also makes the intent self-documenting, since it mirrors Glean's exponent = log_base.powf(1.0 / buckets_per_magnitude) that is computed once and then raised to the index:
WITH bucket_indexes AS (
-- Generate all bucket indexes
-- https://github.com/mozilla/glean/blob/main/glean-core/src/histogram/functional.rs
SELECT
-- Glean precomputes this exponent once and raises it to the bucket index; the
-- rounding of this intermediate value is what makes the bucket minimums match.
POW(log_base, 1 / buckets_per_magnitude) AS exponent,
GENERATE_ARRAY(0, CEIL(LOG(range_max + 1, log_base) * buckets_per_magnitude)) AS indexes
),
buckets AS (
SELECT
FLOOR(POW(exponent, idx)) AS bucket
FROM
bucket_indexes,
UNNEST(indexes) AS idx
WHERE
FLOOR(POW(exponent, idx)) <= range_max
)exponent is the same FLOAT64 value whether it is inlined or read from the CTE, so this is bit-for-bit equivalent.
| -- Bucket minimums at powers of two must match Glean, which truncates them to | ||
| -- 2^n - 1. Emitting 2^n instead makes downstream bucket fills drop every | ||
| -- sample that lands on a power-of-two bucket minimum. |
There was a problem hiding this comment.
suggestion: This comment overstates the rule and contradicts the assertion 25 lines above it. The (2, 8, 305) case at line 41 still expects 16, 32, 64, 128, 256 — exact powers of two, not 2^n - 1 — and this PR does not change that expectation. Truncation is not a property of powers of two in general; it happens only for the specific (log_base, buckets_per_magnitude, index) combinations where POW(POW(log_base, 1 / buckets_per_magnitude), idx) lands just under the integer. Scoping the comment to the case actually being asserted would avoid sending a future reader looking for a bug in the (2, 8) expectations:
-- For log_base=2, buckets_per_magnitude=16, several bucket minimums at powers of
-- two evaluate to just under 2^n and Glean truncates them to 2^n - 1. Emitting 2^n
-- instead makes downstream bucket fills drop every sample that lands on one of
-- these bucket minimums.
This comment has been minimized.
This comment has been minimized.
scholtzan
left a comment
There was a problem hiding this comment.
The Claude suggestions seem worth looking into
Integration report
|
Description
fixes mozilla/glam#3537
Fixes the BQ implementation of functional bucket calculation to match the Glean one for bucket labels that are power of 2.
Related Tickets & Documents
Reviewer, please follow this checklist