This is an automated email from the ASF dual-hosted git repository.

tisonkun pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/datasketches-rust.git


The following commit(s) were added to refs/heads/main by this push:
     new 48165ed  perf(tdigest): reuse cumulative weights in CDF and PMF 
queries (#289)
48165ed is described below

commit 48165edd11c2b0bd5d8dfa439c8d84694cd6a712
Author: tison <[email protected]>
AuthorDate: Mon Oct 5 08:22:34 2026 +0800

    perf(tdigest): reuse cumulative weights in CDF and PMF queries (#289)
---
 CHANGELOG.md                       |  1 +
 datasketches/src/tdigest/sketch.rs | 52 ++++++++++++++++++++++++--------------
 2 files changed, 34 insertions(+), 19 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index 995aa64..8d4b1de 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -19,6 +19,7 @@ All significant changes to this project will be documented in 
this file.
 ### Improvements
 
 * Serializing buffered `TDigestMut` inputs is faster when their total weight 
is at most `k / 2`.
+* `TDigest` and `TDigestMut` `cdf` and `pmf` queries are faster when answering 
many split points.
 * Improve the readability of `Debug` output for HLL and CPC sketches and 
unions.
 * `KllSketch::quantile` and `ReqSketch::quantile` are faster at ranks `0.0` 
and `1.0`, with query work independent of the number of retained samples.
 * `BloomFilter::insert` is faster for integer and raw-byte inputs. 
`BloomFilter::contains_and_insert` is also faster when checking already-present 
integer values.
diff --git a/datasketches/src/tdigest/sketch.rs 
b/datasketches/src/tdigest/sketch.rs
index ef39ffd..4b6a27d 100644
--- a/datasketches/src/tdigest/sketch.rs
+++ b/datasketches/src/tdigest/sketch.rs
@@ -1629,12 +1629,21 @@ impl TDigestView<'_> {
             return None;
         }
 
+        // Split points increase, so their lower centroid indexes never move 
backwards.
+        // Accumulate integer weights only as far as each query needs.
+        let mut previous_lower = 0;
+        let mut weight_below = 0;
+
         let mut ranks = Vec::with_capacity(split_points.len() + 1);
         for &p in split_points {
-            match self.rank(p) {
-                Some(rank) => ranks.push(rank),
-                None => unreachable!("checked non-empty above"),
-            }
+            ranks.push(self.rank_with_weights(p, |lower| {
+                weight_below += self.centroids[previous_lower..lower]
+                    .iter()
+                    .map(|c| c.weight.get())
+                    .sum::<u64>();
+                previous_lower = lower;
+                weight_below
+            }));
         }
         ranks.push(1.0);
         Some(ranks)
@@ -1646,14 +1655,21 @@ impl TDigestView<'_> {
         if self.centroids.is_empty() {
             return None;
         }
+        Some(self.rank_with_weights(value, |lower| {
+            self.centroids[..lower].iter().map(|c| c.weight.get()).sum()
+        }))
+    }
+
+    /// Rank within a non-empty centroid sequence, using the weight before its 
lower anchor.
+    fn rank_with_weights(&self, value: f64, weight_before: impl FnOnce(usize) 
-> u64) -> f64 {
         if value < self.min {
-            return Some(0.0);
+            return 0.0;
         }
         if value > self.max {
-            return Some(1.0);
+            return 1.0;
         }
         if self.min == self.max {
-            return Some(0.5);
+            return 0.5;
         }
 
         let centroids_weight = self.centroids_weight as f64;
@@ -1665,7 +1681,7 @@ impl TDigestView<'_> {
         let first = &self.centroids[0];
         if value < first.mean {
             let center = centroid_center(0, first.weight.get());
-            return Some(if value == self.min {
+            return if value == self.min {
                 0.5 / centroids_weight
             } else {
                 interpolate(
@@ -1673,14 +1689,14 @@ impl TDigestView<'_> {
                     center,
                     interpolation_fraction(value, self.min, first.mean),
                 ) / centroids_weight
-            });
+            };
         }
 
         let last = &self.centroids[num_centroids - 1];
         if value > last.mean {
             let center =
                 centroid_center(self.centroids_weight - last.weight.get(), 
last.weight.get());
-            return Some(if value == self.max {
+            return if value == self.max {
                 // Round the maximum's mass in the same coordinates as the 
centroid centers.
                 centroid_center(self.centroids_weight - 1, 1) / 
centroids_weight
             } else {
@@ -1689,7 +1705,7 @@ impl TDigestView<'_> {
                     center.max((self.centroids_weight - 1) as f64),
                     interpolation_fraction(value, last.mean, self.max),
                 ) / centroids_weight
-            });
+            };
         }
 
         let mut lower = self
@@ -1709,7 +1725,7 @@ impl TDigestView<'_> {
             upper -= 1;
         }
 
-        let weight_below = self.centroids[..lower].iter().map(|c| 
c.weight.get()).sum();
+        let weight_below = weight_before(lower);
         let weight_between: u64 = self.centroids[lower..upper]
             .iter()
             .map(|c| c.weight.get())
@@ -1721,13 +1737,11 @@ impl TDigestView<'_> {
         } else {
             0.5
         };
-        Some(
-            interpolate(
-                centroid_center(weight_below, left.weight.get()),
-                centroid_center(weight_below + weight_between, 
right.weight.get()),
-                fraction,
-            ) / centroids_weight,
-        )
+        interpolate(
+            centroid_center(weight_below, left.weight.get()),
+            centroid_center(weight_below + weight_between, right.weight.get()),
+            fraction,
+        ) / centroids_weight
     }
 
     fn quantile(&self, rank: f64) -> Option<f64> {


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to