From 9a4af322cada9b132a4afcb47a21795d2dd7b245 Mon Sep 17 00:00:00 2001 From: RunasSudo Date: Sat, 20 Aug 2022 22:33:48 +1000 Subject: [PATCH] Avoid more allocations in fold --- src/cli/stv.rs | 4 ++-- src/election.rs | 2 +- src/stv/mod.rs | 62 +++++++++++++++++++++++++++++++++++++------------ src/stv/wasm.rs | 6 ++--- 4 files changed, 53 insertions(+), 21 deletions(-) diff --git a/src/cli/stv.rs b/src/cli/stv.rs index 35ea96f..6cf6b65 100644 --- a/src/cli/stv.rs +++ b/src/cli/stv.rs @@ -343,7 +343,7 @@ where for<'r> &'r N: ops::Neg { // Describe count - let total_ballots = election.ballots.iter().fold(N::zero(), |acc, b| { acc + &b.orig_value }); + let total_ballots = election.ballots.iter().fold(N::new(), |mut acc, b| { acc += &b.orig_value; acc }); print!("Count computed by OpenTally (revision {}). Read {:.0} ballots from \"{}\" for election \"{}\". There are {} candidates for {} vacancies. ", crate::VERSION, total_ballots, filename, election.name, election.candidates.iter().filter(|c| !c.is_dummy).count(), election.seats); let opts_str = opts.describe::(); if !opts_str.is_empty() { @@ -441,7 +441,7 @@ where for<'r> &'r N: ops::Neg { // Header rows - let total_ballots = election.ballots.iter().fold(N::zero(), |acc, b| { acc + &b.orig_value }); + let total_ballots = election.ballots.iter().fold(N::new(), |mut acc, b| { acc += &b.orig_value; acc }); // eSTV does not consistently quote records, so we won't use a CSV library here println!(r#""Election for","{}""#, election.name); diff --git a/src/election.rs b/src/election.rs index a99954f..d2f0b8c 100644 --- a/src/election.rs +++ b/src/election.rs @@ -301,7 +301,7 @@ impl<'a, N: Number> CountState<'a, N> { result.push_str(&format!("Exhausted: {:.dps$} ({:.dps$})\n", self.exhausted.votes, self.exhausted.transfers, dps=opts.pp_decimals)); result.push_str(&format!("Loss by fraction: {:.dps$} ({:.dps$})\n", self.loss_fraction.votes, self.loss_fraction.transfers, dps=opts.pp_decimals)); - let mut total_vote = self.candidates.iter().filter_map(|(c, cc)| if c.is_dummy { None } else { Some(cc) }).fold(N::zero(), |acc, cc| { acc + &cc.votes }); + let mut total_vote = self.candidates.iter().filter_map(|(c, cc)| if c.is_dummy { None } else { Some(cc) }).fold(N::new(), |mut acc, cc| { acc += &cc.votes; acc }); total_vote += &self.exhausted.votes; total_vote += &self.loss_fraction.votes; result.push_str(&format!("Total votes: {:.dps$}\n", total_vote, dps=opts.pp_decimals)); diff --git a/src/stv/mod.rs b/src/stv/mod.rs index e73ed09..4f58fbe 100644 --- a/src/stv/mod.rs +++ b/src/stv/mod.rs @@ -902,11 +902,19 @@ fn update_vre_ers(state: &mut CountState, opts: &STVOptions) { let mut log = String::new(); // Calculate active vote - let active_vote = state.candidates.values().fold(N::zero(), |acc, cc| { + let active_vote = state.candidates.values().fold(N::new(), |mut acc, cc| { match cc.state { - CandidateState::Elected => { if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() { acc + &cc.votes - state.quota.as_ref().unwrap() } else { acc } } - _ => { acc + &cc.votes } + CandidateState::Elected => { + if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() { + acc += &cc.votes; + acc -= state.quota.as_ref().unwrap(); + } + } + _ => { + acc += &cc.votes; + } } + acc }); log.push_str(format!("Active vote is {:.dps$}, so the vote required for election is ", active_vote, dps=opts.pp_decimals).as_str()); @@ -942,11 +950,19 @@ fn update_vre_bulk(state: &mut CountState, _opts: &STVOptions) { //let mut log = String::new(); // Calculate active vote - let active_vote = state.candidates.values().fold(N::zero(), |acc, cc| { + let active_vote = state.candidates.values().fold(N::new(), |mut acc, cc| { match cc.state { - CandidateState::Elected => { if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() { acc + &cc.votes - state.quota.as_ref().unwrap() } else { acc } } - _ => { acc + &cc.votes } + CandidateState::Elected => { + if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() { + acc += &cc.votes; + acc -= state.quota.as_ref().unwrap(); + } + } + _ => { + acc += &cc.votes; + } } + acc }); //log.push_str(format!("Active vote is {:.dps$}, so the vote required for election is ", active_vote, dps=opts.pp_decimals).as_str()); @@ -977,7 +993,7 @@ fn calculate_quota(state: &mut CountState, opts: &STVOptions) { let mut log = String::new(); // Calculate the total vote - let total_vote = state.candidates.values().fold(N::zero(), |acc, cc| { acc + &cc.votes }); + let total_vote = state.candidates.values().fold(N::new(), |mut acc, cc| { acc += &cc.votes; acc }); log.push_str(format!("{:.dps$} usable votes, so the quota is ", total_vote, dps=opts.pp_decimals).as_str()); let quota = total_to_quota(total_vote, state.election.seats, opts); @@ -992,11 +1008,19 @@ fn calculate_quota(state: &mut CountState, opts: &STVOptions) { let mut log = String::new(); // Calculate the active vote - let active_vote = state.candidates.values().fold(N::zero(), |acc, cc| { + let active_vote = state.candidates.values().fold(N::new(), |mut acc, cc| { match cc.state { - CandidateState::Elected => { if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() { acc + &cc.votes - state.quota.as_ref().unwrap() } else { acc } } - _ => { acc + &cc.votes } + CandidateState::Elected => { + if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() { + acc += &cc.votes; + acc -= state.quota.as_ref().unwrap(); + } + } + _ => { + acc += &cc.votes; + } } + acc }); log.push_str(format!("Active vote is {:.dps$}, so the quota is is ", active_vote, dps=opts.pp_decimals).as_str()); @@ -1018,7 +1042,7 @@ fn calculate_quota(state: &mut CountState, opts: &STVOptions) { let mut log = String::new(); // Calculate the total vote - let total_vote = state.candidates.values().fold(N::zero(), |acc, cc| { acc + &cc.votes }); + let total_vote = state.candidates.values().fold(N::new(), |mut acc, cc| { acc += &cc.votes; acc }); log.push_str(format!("{:.dps$} usable votes, so the quota is reduced to ", total_vote, dps=opts.pp_decimals).as_str()); let quota = total_to_quota(total_vote, state.election.seats, opts); @@ -1102,12 +1126,20 @@ fn elect_sure_winners<'a, N: Number>(state: &mut CountState<'a, N>, opts: &STVOp // For trailing candidates, count all votes total_trailing += hopefuls.iter().skip(num_vacancies).fold(N::new(), |mut acc, (_, cc)| { acc += &cc.votes; acc }); // Add finally any votes awaiting transfer - total_trailing += state.candidates.values().fold(N::zero(), |acc, cc| { + total_trailing += state.candidates.values().fold(N::new(), |mut acc, cc| { match cc.state { - CandidateState::Elected => { if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() { acc + &cc.votes - state.quota.as_ref().unwrap() } else { acc } } - CandidateState::Hopeful | CandidateState::Guarded | CandidateState::Withdrawn => { acc } - CandidateState::Excluded | CandidateState::Doomed => { acc + &cc.votes } + CandidateState::Elected => { + if !cc.finalised && &cc.votes > state.quota.as_ref().unwrap() { + acc += &cc.votes; + acc -= state.quota.as_ref().unwrap(); + } + } + CandidateState::Hopeful | CandidateState::Guarded | CandidateState::Withdrawn => {} + CandidateState::Excluded | CandidateState::Doomed => { + acc += &cc.votes; + } } + acc }); if num_vacancies - 1 < hopefuls.len() { diff --git a/src/stv/wasm.rs b/src/stv/wasm.rs index c170c51..bfc993d 100644 --- a/src/stv/wasm.rs +++ b/src/stv/wasm.rs @@ -329,7 +329,7 @@ impl STVOptions { pub fn describe_count(filename: String, election: &Election, opts: &stv::STVOptions) -> String { let mut result = String::from("

Count computed by OpenTally (revision "); result.push_str(crate::VERSION); - let total_ballots = election.ballots.iter().fold(N::zero(), |acc, b| { acc + &b.orig_value }); + let total_ballots = election.ballots.iter().fold(N::new(), |mut acc, b| { acc += &b.orig_value; acc }); result.push_str(&format!(r#"). Read {:.0} ballots from ‘{}’ for election ‘{}’. There are {} candidates for {} vacancies. "#, total_ballots, filename, election.name, election.candidates.iter().filter(|c| !c.is_dummy).count(), election.seats)); let opts_str = opts.describe::(); @@ -605,7 +605,7 @@ pub fn update_results_table(stage_num: usize, state: &CountState, } // Calculate total votes - let mut total_vote = state.candidates.iter().filter_map(|(c, cc)| if c.is_dummy { None } else { Some(cc) }).fold(N::zero(), |acc, cc| { acc + &cc.votes }); + let mut total_vote = state.candidates.iter().filter_map(|(c, cc)| if c.is_dummy { None } else { Some(cc) }).fold(N::new(), |mut acc, cc| { acc += &cc.votes; acc }); total_vote += &state.exhausted.votes; total_vote += &state.loss_fraction.votes; @@ -622,7 +622,7 @@ pub fn update_results_table(stage_num: usize, state: &CountState, } "ballots_votes" => { // Calculate total ballots - let mut total_ballots = state.candidates.values().fold(N::zero(), |acc, cc| { acc + cc.num_ballots() }); + let mut total_ballots = state.candidates.values().fold(N::new(), |mut acc, cc| { acc += cc.num_ballots(); acc }); total_ballots += state.exhausted.num_ballots(); result.push(&format!(r#"{}{}"#, classes_i, pp(&total_ballots, 0), pp(&total_vote, opts.pp_decimals)).into()); }