Skip to content

Commit 0e0b733

Browse files
committed
Improve CM sketch fallback for heavy hitters
1 parent 971b6ad commit 0e0b733

1 file changed

Lines changed: 44 additions & 8 deletions

File tree

src/optimizer/core/histogram.rs

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -360,10 +360,18 @@ impl Histogram {
360360

361361
fn equal_count(&self, value: &DataValue, sketch: &CountMinSketch<DataValue>) -> usize {
362362
let average_count = self.average_count();
363-
if sketch.error_bound(self.meta.values_len) >= average_count {
364-
average_count
363+
if average_count == 0 {
364+
return 0;
365+
}
366+
367+
let estimated_count = sketch.estimate(value);
368+
let error_bound = sketch.error_bound(self.meta.values_len);
369+
if error_bound < average_count
370+
|| estimated_count.saturating_sub(error_bound) > average_count
371+
{
372+
estimated_count
365373
} else {
366-
sketch.estimate(value)
374+
average_count
367375
}
368376
}
369377

@@ -1123,19 +1131,20 @@ mod tests {
11231131
}
11241132

11251133
#[test]
1126-
fn test_eq_count_falls_back_to_hll_when_cm_error_is_large() -> Result<(), DatabaseError> {
1134+
fn test_eq_count_falls_back_to_average_when_cm_error_is_large() -> Result<(), DatabaseError> {
11271135
let mut builder = HistogramBuilder::new(&index_meta(), ANALYZE_STATISTICS_RELATIVE_ERROR)?;
11281136

11291137
for value in 0..10_000 {
11301138
builder.append(DataValue::Int32(value))?;
11311139
}
11321140

1133-
let (histogram, mut sketch, _) = builder.build(100)?;
1141+
let (histogram, sketch, _) = builder.build(100)?;
11341142
let top_n = ColumnTopN::default();
11351143
let average_count = histogram.average_count();
1136-
assert!(sketch.error_bound(histogram.values_len()) >= average_count);
1137-
1138-
sketch.add(&DataValue::Int32(7), 1000);
1144+
let error_bound = sketch.error_bound(histogram.values_len());
1145+
let estimated_count = sketch.estimate(&DataValue::Int32(7));
1146+
assert!(error_bound >= average_count);
1147+
assert!(estimated_count.saturating_sub(error_bound) <= average_count);
11391148

11401149
assert_eq!(
11411150
histogram.collect_count(&[Range::Eq(DataValue::Int32(7))], &sketch, &top_n)?,
@@ -1145,6 +1154,33 @@ mod tests {
11451154
Ok(())
11461155
}
11471156

1157+
#[test]
1158+
fn test_eq_count_uses_cm_sketch_for_clear_heavy_hitter() -> Result<(), DatabaseError> {
1159+
let mut builder = HistogramBuilder::new(&index_meta(), ANALYZE_STATISTICS_RELATIVE_ERROR)?;
1160+
1161+
for _ in 0..1_000 {
1162+
builder.append(DataValue::Int32(7))?;
1163+
}
1164+
for value in 0..9_000 {
1165+
builder.append(DataValue::Int32(10_000 + value))?;
1166+
}
1167+
1168+
let (histogram, sketch, _) = builder.build(100)?;
1169+
let top_n = ColumnTopN::default();
1170+
let average_count = histogram.average_count();
1171+
let error_bound = sketch.error_bound(histogram.values_len());
1172+
let estimated_count = sketch.estimate(&DataValue::Int32(7));
1173+
assert!(error_bound >= average_count);
1174+
assert!(estimated_count.saturating_sub(error_bound) > average_count);
1175+
1176+
assert_eq!(
1177+
histogram.collect_count(&[Range::Eq(DataValue::Int32(7))], &sketch, &top_n)?,
1178+
estimated_count
1179+
);
1180+
1181+
Ok(())
1182+
}
1183+
11481184
#[test]
11491185
fn test_eq_count_falls_back_when_top_n_error_is_large() -> Result<(), DatabaseError> {
11501186
let mut builder = HistogramBuilder::new(&index_meta(), ANALYZE_STATISTICS_RELATIVE_ERROR)?;

0 commit comments

Comments
 (0)