From d152e356f19d56f789ed21c1af72198eeff1db99 Mon Sep 17 00:00:00 2001 From: Anders Olsson Date: Tue, 24 Oct 2023 16:10:40 +0200 Subject: [PATCH] Remove unnecessary allocations --- benches/batch.rs | 3 +- src/batch.rs | 50 ++++++++++++-------- src/game.rs | 116 ++++++++++++++++++++++++----------------------- src/history.rs | 22 ++++++--- 4 files changed, 108 insertions(+), 83 deletions(-) diff --git a/benches/batch.rs b/benches/batch.rs index 6fc1014..4088818 100644 --- a/benches/batch.rs +++ b/benches/batch.rs @@ -51,7 +51,8 @@ fn criterion_benchmark(criterion: &mut Criterion) { weights.push(vec![vec![1.0], vec![1.0]]); } - let mut batch = Batch::new(composition, results, weights, 1, P_DRAW, &agents); + let mut batch = Batch::new(1, P_DRAW); + batch.add_events(composition, results, weights, &agents); criterion.bench_function("Batch::iteration", |b| { b.iter(|| batch.iteration(0, &agents)) diff --git a/src/batch.rs b/src/batch.rs index e97769c..8a58007 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use crate::{ agent::Agent, game::Game, gaussian::Gaussian, player::Player, tuple_gt, tuple_max, Index, N_INF, @@ -116,29 +116,34 @@ impl Batch { } } - pub(crate) fn add_events( + pub fn add_events( &mut self, composition: Vec>>, results: Vec>, weights: Vec>>, agents: &HashMap, ) { - let this_agent = composition - .iter() - .flatten() - .flatten() - .cloned() - .collect::>(); + let mut unique = Vec::with_capacity(10); + + let this_agent = composition.iter().flatten().flatten().filter(|idx| { + if !unique.contains(idx) { + unique.push(*idx); + + return true; + } + + false + }); for idx in this_agent { let elapsed = compute_elapsed(agents[&idx].last_time, self.time); - if let Some(skill) = self.skills.get_mut(&idx) { + if let Some(skill) = self.skills.get_mut(idx) { skill.elapsed = elapsed; skill.forward = agents[&idx].receive(elapsed); } else { self.skills.insert( - idx, + *idx, Skill { forward: agents[&idx].receive(elapsed), elapsed, @@ -172,14 +177,19 @@ impl Batch { }) .collect::>(); + let weights = if weights.is_empty() { + teams + .iter() + .map(|team| vec![1.0; team.items.len()]) + .collect::>() + } else { + weights[e].clone() + }; + Event { teams, evidence: 0.0, - weights: if weights.is_empty() { - Vec::new() - } else { - weights[e].clone() - }, + weights, } }); @@ -202,7 +212,7 @@ impl Batch { let teams = event.within_priors(false, false, &self.skills, agents); let result = event.outputs(); - let g = Game::new(teams, result, event.weights.clone(), self.p_draw); + let g = Game::new(teams, &result, &event.weights, self.p_draw); for (t, team) in event.teams.iter_mut().enumerate() { for (i, item) in team.items.iter_mut().enumerate() { @@ -291,8 +301,8 @@ impl Batch { .map(|(_, event)| { Game::new( event.within_priors(online, forward, &self.skills, agents), - event.outputs(), - event.weights.clone(), + &event.outputs(), + &event.weights, self.p_draw, ) .evidence @@ -316,8 +326,8 @@ impl Batch { .map(|(_, event)| { Game::new( event.within_priors(online, forward, &self.skills, agents), - event.outputs(), - event.weights.clone(), + &event.outputs(), + &event.weights, self.p_draw, ) .evidence diff --git a/src/game.rs b/src/game.rs index 5680620..1eac357 100644 --- a/src/game.rs +++ b/src/game.rs @@ -7,39 +7,33 @@ use crate::{ }; #[derive(Debug)] -pub struct Game { +pub struct Game<'a> { teams: Vec>, - result: Vec, - weights: Vec>, + result: &'a [f64], + weights: &'a [Vec], p_draw: f64, pub(crate) likelihoods: Vec>, pub(crate) evidence: f64, } -impl Game { +impl<'a> Game<'a> { pub fn new( teams: Vec>, - mut result: Vec, - mut weights: Vec>, + result: &'a [f64], + weights: &'a [Vec], p_draw: f64, ) -> Self { assert!( - (result.is_empty() || result.len() == teams.len()), - "result must be empty or the same length as teams" + (result.len() == teams.len()), + "result must have the same length as teams" ); assert!( - (weights.is_empty() || weights.len() == teams.len()), - "weights must be empty or the same length as teams" - ); - - assert!( - weights.is_empty() - || weights - .iter() - .zip(teams.iter()) - .all(|(w, t)| w.len() == t.len()), - "weights must be empty or has the same dimensions as teams" + weights + .iter() + .zip(teams.iter()) + .all(|(w, t)| w.len() == t.len()), + "weights must have the same dimensions as teams" ); assert!( @@ -49,24 +43,13 @@ impl Game { assert!( p_draw > 0.0 || { - let mut r = result.clone(); + let mut r = result.to_vec(); r.sort_unstable_by(|a, b| a.partial_cmp(b).unwrap()); r.windows(2).all(|w| w[0] != w[1]) }, "draw must be > 0.0 if there is teams with draw" ); - if result.is_empty() { - result = (0..teams.len()).rev().map(|i| i as f64).collect::>(); - } - - if weights.is_empty() { - weights = teams - .iter() - .map(|team| vec![1.0; team.len()]) - .collect::>(); - } - let mut this = Self { teams, result, @@ -82,7 +65,7 @@ impl Game { } fn likelihoods(&mut self) { - let o = sort_perm(&self.result, true); + let o = sort_perm(self.result, true); let mut team = o .iter() @@ -235,7 +218,8 @@ mod tests { 25.0 / 300.0, ); - let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0.0, 1.0], vec![], 0.0); + let w = [vec![1.0], vec![1.0]]; + let g = Game::new(vec![vec![t_a], vec![t_b]], &[0.0, 1.0], &w, 0.0); let p = g.posteriors(); let a = p[0][0]; @@ -247,7 +231,8 @@ mod tests { let t_a = Player::new(Gaussian::from_ms(29.0, 1.0), 25.0 / 6.0, GAMMA); let t_b = Player::new(Gaussian::from_ms(25.0, 25.0 / 3.0), 25.0 / 6.0, GAMMA); - let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0.0, 1.0], vec![], 0.0); + let w = [vec![1.0], vec![1.0]]; + let g = Game::new(vec![vec![t_a], vec![t_b]], &[0.0, 1.0], &w, 0.0); let p = g.posteriors(); let a = p[0][0]; @@ -259,7 +244,8 @@ mod tests { let t_a = Player::new(Gaussian::from_ms(1.139, 0.531), 1.0, 0.2125); let t_b = Player::new(Gaussian::from_ms(15.568, 0.51), 1.0, 0.2125); - let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0.0, 1.0], vec![], 0.0); + let w = [vec![1.0], vec![1.0]]; + let g = Game::new(vec![vec![t_a], vec![t_b]], &[0.0, 1.0], &w, 0.0); assert_eq!(g.likelihoods[0][0], N_INF); assert_eq!(g.likelihoods[1][0], N_INF); @@ -285,7 +271,8 @@ mod tests { )], ]; - let g = Game::new(teams.clone(), vec![1.0, 2.0, 0.0], vec![], 0.0); + let w = [vec![1.0], vec![1.0], vec![1.0]]; + let g = Game::new(teams.clone(), &[1.0, 2.0, 0.0], &w, 0.0); let p = g.posteriors(); let a = p[0][0]; @@ -294,7 +281,8 @@ mod tests { assert_ulps_eq!(a, Gaussian::from_ms(25.000000, 6.238469), epsilon = 1e-6); assert_ulps_eq!(b, Gaussian::from_ms(31.311358, 6.698818), epsilon = 1e-6); - let g = Game::new(teams.clone(), vec![], vec![], 0.0); + let w = [vec![1.0], vec![1.0], vec![1.0]]; + let g = Game::new(teams.clone(), &[2.0, 1.0, 0.0], &w, 0.0); let p = g.posteriors(); let a = p[0][0]; @@ -303,7 +291,8 @@ mod tests { assert_ulps_eq!(a, Gaussian::from_ms(31.311358, 6.698818), epsilon = 1e-6); assert_ulps_eq!(b, Gaussian::from_ms(25.000000, 6.238469), epsilon = 1e-6); - let g = Game::new(teams, vec![1.0, 2.0, 0.0], vec![], 0.5); + let w = [vec![1.0], vec![1.0], vec![1.0]]; + let g = Game::new(teams, &[1.0, 2.0, 0.0], &w, 0.5); let p = g.posteriors(); let a = p[0][0]; @@ -328,7 +317,8 @@ mod tests { 25.0 / 300.0, ); - let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0.0, 0.0], vec![], 0.25); + let w = [vec![1.0], vec![1.0]]; + let g = Game::new(vec![vec![t_a], vec![t_b]], &[0.0, 0.0], &w, 0.25); let p = g.posteriors(); let a = p[0][0]; @@ -340,7 +330,8 @@ mod tests { let t_a = Player::new(Gaussian::from_ms(25.0, 3.0), 25.0 / 6.0, 25.0 / 300.0); let t_b = Player::new(Gaussian::from_ms(29.0, 2.0), 25.0 / 6.0, 25.0 / 300.0); - let g = Game::new(vec![vec![t_a], vec![t_b]], vec![0.0, 0.0], vec![], 0.25); + let w = [vec![1.0], vec![1.0]]; + let g = Game::new(vec![vec![t_a], vec![t_b]], &[0.0, 0.0], &w, 0.25); let p = g.posteriors(); let a = p[0][0]; @@ -368,10 +359,11 @@ mod tests { 25.0 / 300.0, ); + let w = [vec![1.0], vec![1.0], vec![1.0]]; let g = Game::new( vec![vec![t_a], vec![t_b], vec![t_c]], - vec![0.0, 0.0, 0.0], - vec![], + &[0.0, 0.0, 0.0], + &w, 0.25, ); let p = g.posteriors(); @@ -388,10 +380,11 @@ mod tests { let t_b = Player::new(Gaussian::from_ms(25.0, 3.0), 25.0 / 6.0, 25.0 / 300.0); let t_c = Player::new(Gaussian::from_ms(29.0, 2.0), 25.0 / 6.0, 25.0 / 300.0); + let w = [vec![1.0], vec![1.0], vec![1.0]]; let g = Game::new( vec![vec![t_a], vec![t_b], vec![t_c]], - vec![0.0, 0.0, 0.0], - vec![], + &[0.0, 0.0, 0.0], + &w, 0.25, ); let p = g.posteriors(); @@ -421,7 +414,8 @@ mod tests { Player::new(Gaussian::from_ms(16., 3.0), 25.0 / 6.0, 25.0 / 300.0), ]; - let g = Game::new(vec![t_a, t_b, t_c], vec![1.0, 0.0, 0.0], vec![], 0.25); + let w = [vec![1.0, 1.0], vec![1.0], vec![1.0, 1.0]]; + let g = Game::new(vec![t_a, t_b, t_c], &[1.0, 0.0, 0.0], &w, 0.25); let p = g.posteriors(); assert_ulps_eq!(p[0][0], Gaussian::from_ms(13.051, 2.864), epsilon = 1e-3); @@ -447,7 +441,8 @@ mod tests { 0.0, )]; - let g = Game::new(vec![t_a.clone(), t_b.clone()], vec![], vec![w_a, w_b], 0.0); + let w = [w_a, w_b]; + let g = Game::new(vec![t_a.clone(), t_b.clone()], &[1.0, 0.0], &w, 0.0); let p = g.posteriors(); assert_ulps_eq!( @@ -464,7 +459,8 @@ mod tests { let w_a = vec![1.0]; let w_b = vec![0.7]; - let g = Game::new(vec![t_a.clone(), t_b.clone()], vec![], vec![w_a, w_b], 0.0); + let w = [w_a, w_b]; + let g = Game::new(vec![t_a.clone(), t_b.clone()], &[1.0, 0.0], &w, 0.0); let p = g.posteriors(); assert_ulps_eq!( @@ -481,7 +477,8 @@ mod tests { let w_a = vec![1.6]; let w_b = vec![0.7]; - let g = Game::new(vec![t_a, t_b], vec![], vec![w_a, w_b], 0.0); + let w = [w_a, w_b]; + let g = Game::new(vec![t_a, t_b], &[1.0, 0.0], &w, 0.0); let p = g.posteriors(); assert_ulps_eq!( @@ -501,7 +498,8 @@ mod tests { let t_a = vec![Player::new(Gaussian::from_ms(2.0, 6.0), 1.0, 0.0)]; let t_b = vec![Player::new(Gaussian::from_ms(2.0, 6.0), 1.0, 0.0)]; - let g = Game::new(vec![t_a, t_b], vec![], vec![w_a, w_b], 0.0); + let w = [w_a, w_b]; + let g = Game::new(vec![t_a, t_b], &[1.0, 0.0], &w, 0.0); let p = g.posteriors(); assert_ulps_eq!( @@ -521,7 +519,8 @@ mod tests { let t_a = vec![Player::new(Gaussian::from_ms(2.0, 6.0), 1.0, 0.0)]; let t_b = vec![Player::new(Gaussian::from_ms(2.0, 6.0), 1.0, 0.0)]; - let g = Game::new(vec![t_a, t_b], vec![], vec![w_a, w_b], 0.0); + let w = [w_a, w_b]; + let g = Game::new(vec![t_a, t_b], &[1.0, 0.0], &w, 0.0); let p = g.posteriors(); assert_ulps_eq!(p[0][0], p[1][0], epsilon = 1e-6); @@ -541,7 +540,8 @@ mod tests { ]; let w_b = vec![0.9, 0.6]; - let g = Game::new(vec![t_a.clone(), t_b.clone()], vec![], vec![w_a, w_b], 0.0); + let w = [w_a, w_b]; + let g = Game::new(vec![t_a.clone(), t_b.clone()], &[1.0, 0.0], &w, 0.0); let p = g.posteriors(); assert_ulps_eq!( @@ -568,7 +568,8 @@ mod tests { let w_a = vec![1.3, 1.5]; let w_b = vec![0.7, 0.4]; - let g = Game::new(vec![t_a.clone(), t_b.clone()], vec![], vec![w_a, w_b], 0.0); + let w = [w_a, w_b]; + let g = Game::new(vec![t_a.clone(), t_b.clone()], &[1.0, 0.0], &w, 0.0); let p = g.posteriors(); assert_ulps_eq!( @@ -595,7 +596,8 @@ mod tests { let w_a = vec![1.6, 0.2]; let w_b = vec![0.7, 2.4]; - let g = Game::new(vec![t_a.clone(), t_b.clone()], vec![], vec![w_a, w_b], 0.0); + let w = [w_a, w_b]; + let g = Game::new(vec![t_a.clone(), t_b.clone()], &[1.0, 0.0], &w, 0.0); let p = g.posteriors(); assert_ulps_eq!( @@ -619,6 +621,7 @@ mod tests { epsilon = 1e-6 ); + let w = [vec![1.0, 1.0], vec![1.0]]; let g = Game::new( vec![ t_a.clone(), @@ -628,8 +631,8 @@ mod tests { 0.0, )], ], - vec![], - vec![], + &[1.0, 0.0], + &w, 0.0, ); let post_2vs1 = g.posteriors(); @@ -637,7 +640,8 @@ mod tests { let w_a = vec![1.0, 1.0]; let w_b = vec![1.0, 0.0]; - let g = Game::new(vec![t_a, t_b.clone()], vec![], vec![w_a, w_b], 0.0); + let w = [w_a, w_b]; + let g = Game::new(vec![t_a, t_b.clone()], &[1.0, 0.0], &w, 0.0); let p = g.posteriors(); assert_ulps_eq!(p[0][0], post_2vs1[0][0], epsilon = 1e-6); diff --git a/src/history.rs b/src/history.rs index 4780205..4919f49 100644 --- a/src/history.rs +++ b/src/history.rs @@ -1,4 +1,4 @@ -use std::collections::{HashMap, HashSet}; +use std::collections::HashMap; use crate::{ agent::{self, Agent}, @@ -267,17 +267,27 @@ impl History { "(length(weights) > 0) & (length(composition) != length(weights))" ); + let mut unique = Vec::with_capacity(10); + let this_agent = composition .iter() .flatten() .flatten() - .cloned() - .collect::>(); + .filter(|idx| { + if !unique.contains(idx) { + unique.push(*idx); + + return true; + } + + false + }) + .collect::>(); for agent in &this_agent { if !self.agents.contains_key(agent) { self.agents.insert( - *agent, + **agent, Agent { player: priors.get(agent).cloned().unwrap_or_else(|| { Player::new( @@ -473,8 +483,8 @@ mod tests { let p = Game::new( h.batches[1].events[0].within_priors(false, false, &h.batches[1].skills, &h.agents), - vec![0.0, 1.0], - vec![], + &vec![0.0, 1.0], + &vec![vec![1.0], vec![1.0]], P_DRAW, ) .posteriors();