taiko: micro optimizations
This commit is contained in:
@@ -4,38 +4,47 @@ use std::cmp::Ordering;
|
||||
|
||||
static COMMON_RHYTHMS: [HitObjectRhythm; 9] = [
|
||||
HitObjectRhythm {
|
||||
id: 0,
|
||||
ratio: 1.0,
|
||||
difficulty: 0.0,
|
||||
},
|
||||
HitObjectRhythm {
|
||||
id: 1,
|
||||
ratio: 2.0 / 1.0,
|
||||
difficulty: 0.3,
|
||||
},
|
||||
HitObjectRhythm {
|
||||
id: 2,
|
||||
ratio: 1.0 / 2.0,
|
||||
difficulty: 0.5,
|
||||
},
|
||||
HitObjectRhythm {
|
||||
id: 3,
|
||||
ratio: 3.0 / 1.0,
|
||||
difficulty: 0.3,
|
||||
},
|
||||
HitObjectRhythm {
|
||||
id: 4,
|
||||
ratio: 1.0 / 3.0,
|
||||
difficulty: 0.35,
|
||||
},
|
||||
HitObjectRhythm {
|
||||
id: 5,
|
||||
ratio: 3.0 / 2.0,
|
||||
difficulty: 0.6,
|
||||
},
|
||||
HitObjectRhythm {
|
||||
id: 6,
|
||||
ratio: 2.0 / 3.0,
|
||||
difficulty: 0.4,
|
||||
},
|
||||
HitObjectRhythm {
|
||||
id: 7,
|
||||
ratio: 5.0 / 4.0,
|
||||
difficulty: 0.5,
|
||||
},
|
||||
HitObjectRhythm {
|
||||
id: 8,
|
||||
ratio: 4.0 / 5.0,
|
||||
difficulty: 0.7,
|
||||
},
|
||||
@@ -43,6 +52,7 @@ static COMMON_RHYTHMS: [HitObjectRhythm; 9] = [
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub(crate) struct HitObjectRhythm {
|
||||
id: u8,
|
||||
ratio: f32,
|
||||
pub(crate) difficulty: f32,
|
||||
}
|
||||
@@ -50,8 +60,7 @@ pub(crate) struct HitObjectRhythm {
|
||||
impl PartialEq for HitObjectRhythm {
|
||||
#[inline]
|
||||
fn eq(&self, other: &Self) -> bool {
|
||||
(self.ratio - other.ratio).abs() <= f32::EPSILON
|
||||
&& (self.difficulty - other.difficulty).abs() <= f32::EPSILON
|
||||
self.id == other.id
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+44
-14
@@ -1,5 +1,7 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::iter::{Cycle, Skip, Take};
|
||||
use std::ops::Index;
|
||||
use std::slice::Iter;
|
||||
|
||||
pub(crate) struct LimitedQueue<T> {
|
||||
queue: Vec<T>,
|
||||
@@ -8,7 +10,6 @@ pub(crate) struct LimitedQueue<T> {
|
||||
}
|
||||
|
||||
impl<T> LimitedQueue<T> {
|
||||
/// Panics if `capacity` is zero.
|
||||
#[inline]
|
||||
pub(crate) fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
@@ -25,7 +26,7 @@ impl<T> LimitedQueue<T> {
|
||||
|
||||
if self.queue.len() == capacity {
|
||||
self.start = (self.start + 1) % capacity;
|
||||
self.queue[self.end as usize] = elem;
|
||||
self.queue[self.end] = elem;
|
||||
} else {
|
||||
self.queue.push(elem);
|
||||
}
|
||||
@@ -38,7 +39,7 @@ impl<T> LimitedQueue<T> {
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn last(&self) -> Option<&T> {
|
||||
self.queue.get(self.end as usize)
|
||||
self.queue.get(self.end)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
@@ -52,21 +53,30 @@ impl<T> LimitedQueue<T> {
|
||||
pub(crate) fn full(&self) -> bool {
|
||||
self.queue.len() == self.queue.capacity()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn iter(&self) -> LimitedQueueIter<T> {
|
||||
let iter = self
|
||||
.queue
|
||||
.iter()
|
||||
.cycle()
|
||||
.skip(self.start)
|
||||
.take(self.queue.len());
|
||||
|
||||
LimitedQueueIter { iter }
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: PartialOrd> LimitedQueue<T> {
|
||||
pub(crate) fn min(&self) -> Option<&T> {
|
||||
let mut iter = self.queue.iter();
|
||||
|
||||
let first = iter.next()?;
|
||||
let min = iter.fold(first, |min, next| match min.partial_cmp(next) {
|
||||
Some(Ordering::Less) => min,
|
||||
Some(Ordering::Equal) => min,
|
||||
Some(Ordering::Greater) => next,
|
||||
None => min,
|
||||
});
|
||||
|
||||
Some(min)
|
||||
self.queue
|
||||
.iter()
|
||||
.reduce(|min, next| match min.partial_cmp(next) {
|
||||
Some(Ordering::Less) => min,
|
||||
Some(Ordering::Equal) => min,
|
||||
Some(Ordering::Greater) => next,
|
||||
None => min,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -78,3 +88,23 @@ impl<T> Index<usize> for LimitedQueue<T> {
|
||||
&self.queue[(self.start + idx) % self.queue.capacity()]
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct LimitedQueueIter<'a, T> {
|
||||
iter: Take<Skip<Cycle<Iter<'a, T>>>>,
|
||||
}
|
||||
|
||||
impl<'a, T> Iterator for LimitedQueueIter<'a, T> {
|
||||
type Item = &'a T;
|
||||
|
||||
#[inline]
|
||||
fn next(&mut self) -> Option<Self::Item> {
|
||||
self.iter.next()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
fn size_hint(&self) -> (usize, Option<usize>) {
|
||||
self.iter.size_hint()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, T> ExactSizeIterator for LimitedQueueIter<'a, T> {}
|
||||
|
||||
+12
-8
@@ -1,5 +1,7 @@
|
||||
use super::{DifficultyObject, HitObjectRhythm, LimitedQueue, Rim};
|
||||
|
||||
use std::ops::Index;
|
||||
|
||||
const RHYTHM_STRAIN_DECAY: f32 = 0.96;
|
||||
const MOST_RECENT_PATTERNS_TO_COMPARE: usize = 2;
|
||||
|
||||
@@ -102,9 +104,10 @@ impl SkillKind {
|
||||
|
||||
for start in iter {
|
||||
let different_pattern = (0..MOST_RECENT_PATTERNS_TO_COMPARE).any(|i| {
|
||||
mono_history[start + i]
|
||||
!= mono_history
|
||||
[mono_history.len() + i - MOST_RECENT_PATTERNS_TO_COMPARE]
|
||||
let to_compare =
|
||||
mono_history.len() + i - MOST_RECENT_PATTERNS_TO_COMPARE;
|
||||
|
||||
mono_history.index(start + i) != mono_history.index(to_compare)
|
||||
});
|
||||
|
||||
if different_pattern {
|
||||
@@ -168,17 +171,18 @@ impl SkillKind {
|
||||
|
||||
for start in iter {
|
||||
let different_pattern = (0..most_recent_patterns_to_compare).any(|i| {
|
||||
rhythm_history[start + i].1
|
||||
!= rhythm_history
|
||||
[rhythm_history.len() + i - most_recent_patterns_to_compare]
|
||||
.1
|
||||
let to_compare =
|
||||
rhythm_history.len() + i - most_recent_patterns_to_compare;
|
||||
|
||||
rhythm_history.index(start + i).1 != rhythm_history.index(to_compare).1
|
||||
});
|
||||
|
||||
if different_pattern {
|
||||
continue;
|
||||
}
|
||||
|
||||
reps_penalty *= repetition_penalty(current.idx - rhythm_history[start].0);
|
||||
reps_penalty *=
|
||||
repetition_penalty(current.idx - rhythm_history.index(start).0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
use super::{LimitedQueue, Rim};
|
||||
|
||||
use crate::{parse::HitObject, Beatmap};
|
||||
|
||||
const ROLL_MIN_REPETITIONS: usize = 12;
|
||||
@@ -12,7 +11,6 @@ pub(crate) trait StaminaCheeseDetector {
|
||||
}
|
||||
|
||||
impl StaminaCheeseDetector for Beatmap {
|
||||
// TODO: Optimize
|
||||
fn find_cheese(&self) -> Vec<bool> {
|
||||
let mut cheese = vec![false; self.hit_objects.len()];
|
||||
|
||||
@@ -33,8 +31,8 @@ impl StaminaCheeseDetector for Beatmap {
|
||||
let mut index_before_last_repeat = -1;
|
||||
let mut last_mark_end = 0;
|
||||
|
||||
for i in 0..self.hit_objects.len() {
|
||||
history.push(&self.hit_objects[i]);
|
||||
for (i, h) in self.hit_objects.iter().enumerate() {
|
||||
history.push(h);
|
||||
|
||||
if !history.full() {
|
||||
continue;
|
||||
@@ -44,6 +42,7 @@ impl StaminaCheeseDetector for Beatmap {
|
||||
|
||||
if !contains {
|
||||
index_before_last_repeat = (i + 1 - history.len()) as isize;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -63,8 +62,8 @@ impl StaminaCheeseDetector for Beatmap {
|
||||
let mut tl_len = -2;
|
||||
let mut last_mark_end = 0;
|
||||
|
||||
for i in (parity..self.hit_objects.len()).step_by(2) {
|
||||
if self.hit_objects[i].is_rim() == is_rin {
|
||||
for (i, h) in self.hit_objects.iter().enumerate().skip(parity).step_by(2) {
|
||||
if h.is_rim() == is_rin {
|
||||
tl_len += 2;
|
||||
} else {
|
||||
tl_len = -2;
|
||||
@@ -96,8 +95,8 @@ fn mark_as_cheese(start: usize, end: usize, cheese: &mut [bool]) {
|
||||
|
||||
#[inline]
|
||||
fn contains_pattern_repeat(history: &LimitedQueue<&HitObject>, pattern_len: usize) -> bool {
|
||||
for j in 0..pattern_len {
|
||||
if history[j].is_rim() != history[j + pattern_len].is_rim() {
|
||||
for (&curr, &to_compare) in history.iter().zip(history.iter().skip(pattern_len)) {
|
||||
if curr.is_rim() != to_compare.is_rim() {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user