Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

perf: Use SmallVec in the Type::App variant #129

Merged
merged 7 commits into from
Nov 11, 2016
Merged

perf: Use SmallVec in the Type::App variant #129

merged 7 commits into from
Nov 11, 2016

Conversation

Marwes
Copy link
Member

@Marwes Marwes commented Aug 24, 2016

Since applications are usually done on just a few argument it is usually unnecessary to allocate a Vec. Using SmallVec this heap allocation can be avoided for applications with only a few arguments which should cover most of the cases.
#50

@Marwes
Copy link
Member Author

Marwes commented Aug 24, 2016

Not as happy as I expected with this PR as SmallVec currently has two drop flags which causes Type to grow by 8 bytes. Once rust-lang/rust#35764 gets merged it should be possible to actually increase the number of arguments to 3 (from the current 2) while still keeping the same size for Type (thought naturally this would be worse for anything else than nightly until the trains catch up).

Atm I am only seeing a minor improvement in the typecheck_prelude benchmark so maybe hold of on merging this until it can be tested with the aforementioned rustc PR.

@lambda-fairy
Copy link

Heads up, rust-lang/rust#35764 was merged a while ago. Might like to revisit this change.

@Marwes
Copy link
Member Author

Marwes commented Oct 3, 2016

I think I redid the benchmarks after rust-lang/rust#35764 was merged but didn't see any significant improvements (allocation does not seem to be a large part of the runtime currently, but admittedly it might be shadowed by other things). Going to redo it anyway to verify (and remember to update this PR).

I believe drop-flags will only be removed when 1.13 is released however so In any case it might be best waiting for 1.13 before merging.

@Marwes
Copy link
Member Author

Marwes commented Oct 4, 2016

Rebased and removed the intermediate Vecs used when constructing AppVecs (which gave an additional ~5% speedup). Used the master branch of https://github.com/servo/rust-smallvec so we should wait with merging this until at least a new version on cargo is released. May also want to wait for rust 1.13 as this will likely be a regression in performance on stable otherwise.

SmallVec

test typecheck_prelude ... bench:  13,559,631 ns/iter (+/- 569,445)

Vec

test typecheck_prelude ... bench:  15,035,660 ns/iter (+/- 570,014)

unifier.try_match(l, &Type::app(r.clone(), r_args[..offset].into()));

let reduced_r = Type::app(r.clone(),
r_args[..offset].iter().cloned().collect());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you make this an intermediate? like:

let offset = r_args.len() - l_args.len();

let reduced_r_args = r_args[..offset].iter().cloned();
let reduced_r = Type::app(r.clone(), reduced_r_args.collect());

Just find the weird indentation kinda ugly.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I find it hard to read with to many superfluous intermediates as well (neither of the names reduced_r or reduced_r_args says anything). I will see what I can do, may be able to simply the code overall.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed... no real 'best way' here. Not a big deal - I agree it might be a tad superfluous here.

shakes fist at Rust's ugly syntax and rightward drift

😆

@@ -346,10 +348,10 @@ test
let result = support::typecheck(text);
assert!(result.is_ok(), "{}", result.unwrap_err());

let variants = Type::variants(vec![(support::intern_unscoped("T"),
let variants = Type::variants(collect![(support::intern_unscoped("T"),
Type::function(vec![typ("a")],
support::typ_a("Test", vec![typ("a")])))]);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Indentation is now out of alignment.

Copy link
Member Author

@Marwes Marwes Oct 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I have got rustfmt set to format automatically but I think vec! is special cased, other macros do not get formatted...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, that's right :(

@@ -1212,7 +1225,7 @@ fn walk_move_types2<'a, I, F, T>(mut types: I, replaced: bool, output: &mut Vec<
Some(typ) => {
output.push(typ);
}
None if replaced || !output.is_empty() => {
None if replaced || output.len() != 0 => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reasoning?

Copy link
Member Author

@Marwes Marwes Oct 5, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

VecLike from smallvec does not have an is_empty method but it appears this is only because the VecLike trait does not have the Deref bound specified correctly. Will submit a PR.

Could also just specify Deref<Target=[T]>myself which I am not sure why I did not do...

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Dang - yeah, a PR would probably be useful there.

walk_move_types2(types.into_iter(), false, &mut out, &mut f);
if out.is_empty() {
if out.len() == 0 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto

Since applications are usually done on just a few argument it is usually unnecessary to allocate a `Vec`. Using `SmallVec` this heap allocation can be avoided for applications with only a few arguments which should cover most of the cases.
Stable and beta cannot handle dev-dependencies in sub packages (cargo test -p gluon_parser)
Disabling since travis takes forever to start the osx builds
@Marwes
Copy link
Member Author

Marwes commented Nov 10, 2016

Rebase. Since rust 1.13 is release there is no reason to delay merging this anymore I think. Disabled osx builds since they take forever to start.

@@ -1077,12 +1085,14 @@ pub fn walk_move_type_opt<F: ?Sized, I, T>(typ: &Type<I, T>, f: &mut F) -> Optio
}
}

pub fn walk_move_types<'a, I, F, T>(types: I, mut f: F) -> Option<Vec<T>>
// FIXME Add R: Default and remove the `out` parameter
pub fn walk_move_types<'a, I, F, T, R>(types: I, mut f: F) -> Option<R>
Copy link
Member

@brendanzab brendanzab Nov 10, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you elaborate on this comment?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just forgot to remove that comment when removing the out parameter, fixed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

:D

Ok(merge(r, new_type, r_args, new_args, Type::app))
}
}
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Love this - great idea breaking it out into another function ❤️

@brendanzab brendanzab merged commit 0b29da5 into master Nov 11, 2016
@brendanzab brendanzab deleted the smallvec branch November 11, 2016 02:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants