From 327323ad25d126f6394f26e1442667647022c383 Mon Sep 17 00:00:00 2001 From: Aleksey Kladov Date: Tue, 13 Apr 2021 12:49:10 +0300 Subject: internal: fix flakiness of accidentally quadratic test --- crates/ide/src/syntax_highlighting/tests.rs | 98 +++++------------------------ 1 file changed, 17 insertions(+), 81 deletions(-) (limited to 'crates/ide/src/syntax_highlighting') diff --git a/crates/ide/src/syntax_highlighting/tests.rs b/crates/ide/src/syntax_highlighting/tests.rs index b4818060f..933cfa6f3 100644 --- a/crates/ide/src/syntax_highlighting/tests.rs +++ b/crates/ide/src/syntax_highlighting/tests.rs @@ -2,8 +2,7 @@ use std::time::Instant; use expect_test::{expect_file, ExpectFile}; use ide_db::SymbolKind; -use stdx::format_to; -use test_utils::{bench, bench_fixture, skip_slow_tests}; +use test_utils::{bench, bench_fixture, skip_slow_tests, AssertLinear}; use crate::{fixture, FileRange, HlTag, TextRange}; @@ -266,90 +265,27 @@ fn syntax_highlighting_not_quadratic() { return; } - let mut measures = Vec::new(); - for i in 6..=10 { - let n = 1 << i; - let fixture = bench_fixture::big_struct_n(n); - let (analysis, file_id) = fixture::file(&fixture); + let mut al = AssertLinear::default(); + while al.next_round() { + for i in 6..=10 { + let n = 1 << i; - let time = Instant::now(); + let fixture = bench_fixture::big_struct_n(n); + let (analysis, file_id) = fixture::file(&fixture); - let hash = analysis - .highlight(file_id) - .unwrap() - .iter() - .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct)) - .count(); - assert!(hash > n as usize); + let time = Instant::now(); - let elapsed = time.elapsed(); - measures.push((n as f64, elapsed.as_millis() as f64)) - } + let hash = analysis + .highlight(file_id) + .unwrap() + .iter() + .filter(|it| it.highlight.tag == HlTag::Symbol(SymbolKind::Struct)) + .count(); + assert!(hash > n as usize); - assert_linear(&measures) -} - -/// Checks that a set of measurements looks like a linear function rather than -/// like a quadratic function. Algorithm: -/// -/// 1. Linearly scale input to be in [0; 1) -/// 2. Using linear regression, compute the best linear function approximating -/// the input. -/// 3. Compute RMSE and maximal absolute error. -/// 4. Check that errors are within tolerances and that the constant term is not -/// too negative. -/// -/// Ideally, we should use a proper "model selection" to directly compare -/// quadratic and linear models, but that sounds rather complicated: -/// -/// https://stats.stackexchange.com/questions/21844/selecting-best-model-based-on-linear-quadratic-and-cubic-fit-of-data -fn assert_linear(xy: &[(f64, f64)]) { - let (mut xs, mut ys): (Vec<_>, Vec<_>) = xy.iter().copied().unzip(); - normalize(&mut xs); - normalize(&mut ys); - let xy = xs.iter().copied().zip(ys.iter().copied()); - - // Linear regression: finding a and b to fit y = a + b*x. - - let mean_x = mean(&xs); - let mean_y = mean(&ys); - - let b = { - let mut num = 0.0; - let mut denom = 0.0; - for (x, y) in xy.clone() { - num += (x - mean_x) * (y - mean_y); - denom += (x - mean_x).powi(2); + let elapsed = time.elapsed(); + al.sample(n as f64, elapsed.as_millis() as f64); } - num / denom - }; - - let a = mean_y - b * mean_x; - - let mut plot = format!("y_pred = {:.3} + {:.3} * x\n\nx y y_pred\n", a, b); - - let mut se = 0.0; - let mut max_error = 0.0f64; - for (x, y) in xy { - let y_pred = a + b * x; - se += (y - y_pred).powi(2); - max_error = max_error.max((y_pred - y).abs()); - - format_to!(plot, "{:.3} {:.3} {:.3}\n", x, y, y_pred); - } - - let rmse = (se / xs.len() as f64).sqrt(); - format_to!(plot, "\nrmse = {:.3} max error = {:.3}", rmse, max_error); - - assert!(rmse < 0.05 && max_error < 0.1 && a > -0.1, "\nLooks quadratic\n{}", plot); - - fn normalize(xs: &mut Vec) { - let max = xs.iter().copied().max_by(|a, b| a.partial_cmp(b).unwrap()).unwrap(); - xs.iter_mut().for_each(|it| *it /= max); - } - - fn mean(xs: &[f64]) -> f64 { - xs.iter().copied().sum::() / (xs.len() as f64) } } -- cgit v1.2.3