From 710f53c981201efb3bacaeac21ccfbd96aa200c6 Mon Sep 17 00:00:00 2001 From: Christopher Serr Date: Tue, 9 Jun 2020 16:16:04 +0200 Subject: [PATCH 1/2] Speed up the comparison time lookups We use a Vec here now because a HashMap would require hashing the comparison and then comparing the comparison with the string at the index calculated from the hash. This means at least two full iterations over the string are necessary, with one of them being somewhat expensive due to the hashing. Most of the time it is faster to just iterate the few comparisons we have and compare them directly. Most will be rejected right away as the first byte doesn't even match, so in the end you'll end up with less than two full iterations over the string. In fact most of the time Personal Best will be the first in the list and that's the one we most often want to look up anyway. One additional reason for doing this is that the ahash that was calculated for the HashMap uses 128-bit multiplications which regressed a lot in Rust 1.44 for targets where the `compiler-builtins` helpers were used. https://github.com/rust-lang/rust/issues/73135 We could potentially look into interning our comparisons in the future which could yield even better performance. --- src/rendering/glyph_cache.rs | 2 +- src/run/comparisons.rs | 83 ++++++++++++++++++++++++++++++++++++ src/run/editor/mod.rs | 2 +- src/run/mod.rs | 2 + src/run/segment.rs | 22 +++------- src/settings/gradient.rs | 2 +- 6 files changed, 95 insertions(+), 18 deletions(-) create mode 100644 src/run/comparisons.rs diff --git a/src/rendering/glyph_cache.rs b/src/rendering/glyph_cache.rs index 8a809c3d..90c4f748 100644 --- a/src/rendering/glyph_cache.rs +++ b/src/rendering/glyph_cache.rs @@ -1,11 +1,11 @@ use super::mesh::{fill_builder, Mesh}; use super::Backend; +use hashbrown::HashMap; use lyon::{ path::{self, math::point, Path}, tessellation::{FillOptions, FillTessellator}, }; use rusttype::{Font, GlyphId, OutlineBuilder, Scale}; -use std::collections::HashMap; struct PathBuilder(path::Builder); diff --git a/src/run/comparisons.rs b/src/run/comparisons.rs new file mode 100644 index 00000000..fb9bfb86 --- /dev/null +++ b/src/run/comparisons.rs @@ -0,0 +1,83 @@ +use crate::platform::prelude::*; +use crate::Time; + +// We use a Vec here because a HashMap would require hashing the comparison and +// then comparing the comparison with the string at the index calculated from +// the hash. This means at least two full iterations over the string are +// necessary, with one of them being somewhat expensive due to the hashing. Most +// of the time it is faster to just iterate the few comparisons we have and +// compare them directly. Most will be rejected right away as the first byte +// doesn't even match, so in the end you'll end up with less than two full +// iterations over the string. In fact most of the time Personal Best will be +// the first in the list and that's the one we most often want to look up +// anyway. +// +// One additional reason for doing this is that the ahash that was calculated +// for the HashMap uses 128-bit multiplications which regressed a lot in Rust +// 1.44 for targets where the `compiler-builtins` helpers were used. +// https://github.com/rust-lang/rust/issues/73135 +// +// We could potentially look into interning our comparisons in the future which +// could yield even better performance. + +/// A collection of a segment's comparison times. +#[derive(Clone, Default, Debug, PartialEq)] +pub struct Comparisons(Vec<(Box, Time)>); + +impl Comparisons { + fn index_of(&self, comparison: &str) -> Option { + Some( + self.0 + .iter() + .enumerate() + .find(|(_, (c, _))| &**c == comparison)? + .0, + ) + } + + /// Accesses the time for the comparison specified. + pub fn get(&self, comparison: &str) -> Option