Improve complexity of CompactAssignments::unique_targets (#8314)

* Improve complexity of CompactAssignments::unique_targets

Original implementation was O(n**2). Current impl is O(n log n).

Avoided the original proposed mitigation because it does not retain
the de-duplicating property present in the original implementation.
This implementation does a little more work, but retains that property.

* Explicitly choose sp_std Vec and BTreeSet

Ensures that the macro still works if someone uses it in a context
in which sp_std is not imported or is renamed.

* explicitly use sp_std vectors throughout compact macro
This commit is contained in:
Peter Goodspeed-Niklaus
2021-03-17 09:13:33 +01:00
committed by GitHub
parent 2586d55754
commit 23b32e7543
4 changed files with 25 additions and 24 deletions
@@ -125,7 +125,7 @@ pub(crate) fn into_impl(count: usize, per_thing: syn::Type) -> TokenStream2 {
let target = target_at(*t_idx).or_invalid_index()?; let target = target_at(*t_idx).or_invalid_index()?;
Ok((target, *p)) Ok((target, *p))
}) })
.collect::<Result<Vec<(A, #per_thing)>, _npos::Error>>()?; .collect::<Result<_npos::sp_std::prelude::Vec<(A, #per_thing)>, _npos::Error>>()?;
if sum >= #per_thing::one() { if sum >= #per_thing::one() {
return Err(_npos::Error::CompactStakeOverflow); return Err(_npos::Error::CompactStakeOverflow);
@@ -49,14 +49,14 @@ fn decode_impl(
quote! { quote! {
let #name = let #name =
< <
Vec<(_npos::codec::Compact<#voter_type>, _npos::codec::Compact<#target_type>)> _npos::sp_std::prelude::Vec<(_npos::codec::Compact<#voter_type>, _npos::codec::Compact<#target_type>)>
as as
_npos::codec::Decode _npos::codec::Decode
>::decode(value)?; >::decode(value)?;
let #name = #name let #name = #name
.into_iter() .into_iter()
.map(|(v, t)| (v.0, t.0)) .map(|(v, t)| (v.0, t.0))
.collect::<Vec<_>>(); .collect::<_npos::sp_std::prelude::Vec<_>>();
} }
}; };
@@ -65,7 +65,7 @@ fn decode_impl(
quote! { quote! {
let #name = let #name =
< <
Vec<( _npos::sp_std::prelude::Vec<(
_npos::codec::Compact<#voter_type>, _npos::codec::Compact<#voter_type>,
(_npos::codec::Compact<#target_type>, _npos::codec::Compact<#weight_type>), (_npos::codec::Compact<#target_type>, _npos::codec::Compact<#weight_type>),
_npos::codec::Compact<#target_type>, _npos::codec::Compact<#target_type>,
@@ -76,7 +76,7 @@ fn decode_impl(
let #name = #name let #name = #name
.into_iter() .into_iter()
.map(|(v, (t1, w), t2)| (v.0, (t1.0, w.0), t2.0)) .map(|(v, (t1, w), t2)| (v.0, (t1.0, w.0), t2.0))
.collect::<Vec<_>>(); .collect::<_npos::sp_std::prelude::Vec<_>>();
} }
}; };
@@ -90,7 +90,7 @@ fn decode_impl(
quote! { quote! {
let #name = let #name =
< <
Vec<( _npos::sp_std::prelude::Vec<(
_npos::codec::Compact<#voter_type>, _npos::codec::Compact<#voter_type>,
[(_npos::codec::Compact<#target_type>, _npos::codec::Compact<#weight_type>); #c-1], [(_npos::codec::Compact<#target_type>, _npos::codec::Compact<#weight_type>); #c-1],
_npos::codec::Compact<#target_type>, _npos::codec::Compact<#target_type>,
@@ -104,7 +104,7 @@ fn decode_impl(
[ #inner_impl ], [ #inner_impl ],
t_last.0, t_last.0,
)) ))
.collect::<Vec<_>>(); .collect::<_npos::sp_std::prelude::Vec<_>>();
} }
}).collect::<TokenStream2>(); }).collect::<TokenStream2>();
@@ -142,7 +142,7 @@ fn encode_impl(ident: syn::Ident, count: usize) -> TokenStream2 {
_npos::codec::Compact(v.clone()), _npos::codec::Compact(v.clone()),
_npos::codec::Compact(t.clone()), _npos::codec::Compact(t.clone()),
)) ))
.collect::<Vec<_>>(); .collect::<_npos::sp_std::prelude::Vec<_>>();
#name.encode_to(&mut r); #name.encode_to(&mut r);
} }
}; };
@@ -160,7 +160,7 @@ fn encode_impl(ident: syn::Ident, count: usize) -> TokenStream2 {
), ),
_npos::codec::Compact(t2.clone()), _npos::codec::Compact(t2.clone()),
)) ))
.collect::<Vec<_>>(); .collect::<_npos::sp_std::prelude::Vec<_>>();
#name.encode_to(&mut r); #name.encode_to(&mut r);
} }
}; };
@@ -184,14 +184,14 @@ fn encode_impl(ident: syn::Ident, count: usize) -> TokenStream2 {
[ #inners_compact_array ], [ #inners_compact_array ],
_npos::codec::Compact(t_last.clone()), _npos::codec::Compact(t_last.clone()),
)) ))
.collect::<Vec<_>>(); .collect::<_npos::sp_std::prelude::Vec<_>>();
#name.encode_to(&mut r); #name.encode_to(&mut r);
} }
}).collect::<TokenStream2>(); }).collect::<TokenStream2>();
quote!( quote!(
impl _npos::codec::Encode for #ident { impl _npos::codec::Encode for #ident {
fn encode(&self) -> Vec<u8> { fn encode(&self) -> _npos::sp_std::prelude::Vec<u8> {
let mut r = vec![]; let mut r = vec![];
#encode_impl_single #encode_impl_single
#encode_impl_double #encode_impl_double
@@ -119,14 +119,14 @@ fn struct_def(
let name = field_name_for(1); let name = field_name_for(1);
// NOTE: we use the visibility of the struct for the fields as well.. could be made better. // NOTE: we use the visibility of the struct for the fields as well.. could be made better.
quote!( quote!(
#vis #name: Vec<(#voter_type, #target_type)>, #vis #name: _npos::sp_std::prelude::Vec<(#voter_type, #target_type)>,
) )
}; };
let doubles = { let doubles = {
let name = field_name_for(2); let name = field_name_for(2);
quote!( quote!(
#vis #name: Vec<(#voter_type, (#target_type, #weight_type), #target_type)>, #vis #name: _npos::sp_std::prelude::Vec<(#voter_type, (#target_type, #weight_type), #target_type)>,
) )
}; };
@@ -135,7 +135,7 @@ fn struct_def(
let field_name = field_name_for(c); let field_name = field_name_for(c);
let array_len = c - 1; let array_len = c - 1;
quote!( quote!(
#vis #field_name: Vec<( #vis #field_name: _npos::sp_std::prelude::Vec<(
#voter_type, #voter_type,
[(#target_type, #weight_type); #array_len], [(#target_type, #weight_type); #array_len],
#target_type #target_type
@@ -194,20 +194,19 @@ fn struct_def(
all_edges all_edges
} }
fn unique_targets(&self) -> Vec<Self::Target> { fn unique_targets(&self) -> _npos::sp_std::prelude::Vec<Self::Target> {
// NOTE: this implementation returns the targets sorted, but we don't use it yet per // NOTE: this implementation returns the targets sorted, but we don't use it yet per
// se, nor is the API enforcing it. // se, nor is the API enforcing it.
let mut all_targets: Vec<Self::Target> = Vec::with_capacity(self.average_edge_count()); use _npos::sp_std::collections::btree_set::BTreeSet;
let mut all_targets: BTreeSet<Self::Target> = BTreeSet::new();
let mut maybe_insert_target = |t: Self::Target| { let mut maybe_insert_target = |t: Self::Target| {
match all_targets.binary_search(&t) { all_targets.insert(t);
Ok(_) => (),
Err(pos) => all_targets.insert(pos, t)
}
}; };
#unique_targets_impl #unique_targets_impl
all_targets all_targets.into_iter().collect()
} }
fn remove_voter(&mut self, to_remove: Self::Voter) -> bool { fn remove_voter(&mut self, to_remove: Self::Voter) -> bool {
@@ -216,7 +215,7 @@ fn struct_def(
} }
fn from_assignment<FV, FT, A>( fn from_assignment<FV, FT, A>(
assignments: Vec<_npos::Assignment<A, #weight_type>>, assignments: _npos::sp_std::prelude::Vec<_npos::Assignment<A, #weight_type>>,
index_of_voter: FV, index_of_voter: FV,
index_of_target: FT, index_of_target: FT,
) -> Result<Self, _npos::Error> ) -> Result<Self, _npos::Error>
@@ -243,8 +242,8 @@ fn struct_def(
self, self,
voter_at: impl Fn(Self::Voter) -> Option<A>, voter_at: impl Fn(Self::Voter) -> Option<A>,
target_at: impl Fn(Self::Target) -> Option<A>, target_at: impl Fn(Self::Target) -> Option<A>,
) -> Result<Vec<_npos::Assignment<A, #weight_type>>, _npos::Error> { ) -> Result<_npos::sp_std::prelude::Vec<_npos::Assignment<A, #weight_type>>, _npos::Error> {
let mut assignments: Vec<_npos::Assignment<A, #weight_type>> = Default::default(); let mut assignments: _npos::sp_std::prelude::Vec<_npos::Assignment<A, #weight_type>> = Default::default();
#into_impl #into_impl
Ok(assignments) Ok(assignments)
} }
@@ -119,6 +119,8 @@ pub use pjr::*;
pub use codec; pub use codec;
#[doc(hidden)] #[doc(hidden)]
pub use sp_arithmetic; pub use sp_arithmetic;
#[doc(hidden)]
pub use sp_std;
/// Simple Extension trait to easily convert `None` from index closures to `Err`. /// Simple Extension trait to easily convert `None` from index closures to `Err`.
/// ///