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

Doc BTree better, and add some iteration benches #17801

Merged
merged 3 commits into from
Oct 11, 2014

Conversation

Gankra
Copy link
Contributor

@Gankra Gankra commented Oct 5, 2014

I previously avoided #[inline]ing anything assuming someone would come in and explain to me where this would be appropriate. Apparently no one really knows, so I'll just go the opposite way an inline everything assuming someone will come in and yell at me that such-and-such shouldn't be #[inline].

For posterity, iteration comparisons:

test btree::map::bench::iter_20                            ... bench:       971 ns/iter (+/- 30)
test btree::map::bench::iter_1000                          ... bench:     29445 ns/iter (+/- 480)
test btree::map::bench::iter_100000                        ... bench:   2929035 ns/iter (+/- 21551)

test treemap::bench::iter_20                               ... bench:       530 ns/iter (+/- 66)
test treemap::bench::iter_1000                             ... bench:     26287 ns/iter (+/- 825)
test treemap::bench::iter_100000                           ... bench:   7650084 ns/iter (+/- 356711)

test trie::bench_map::iter_20                              ... bench:       646 ns/iter (+/- 265)
test trie::bench_map::iter_1000                            ... bench:     43556 ns/iter (+/- 5014)
test trie::bench_map::iter_100000                          ... bench:  12988002 ns/iter (+/- 139676)

As you can see btree "scales" much better than treemap. triemap scales quite poorly.

Note that completely different results are given if the elements are inserted in order from the range [0, size]. In particular, TrieMap completely dominates in the sorted case. This suggests adding benches for both might be worthwhile. However unsorted is probably the more "normal" case, so I consider this "good enough" for now.

@thestinger
Copy link
Contributor

Since it's all generic code, inlining across crates is already possible. That may change in the future if Rust ever supports re-exporting instantiations of generics like C++11.

@alexcrichton
Copy link
Member

Yes, as @thestinger mentioned all of these methods are already candidates for inlining across crates due to generics (with today's compiler), and otherwise the #[inline] annotation will give a strong hint to LLVM to inline a function. Let's avoid spraying #[inline] for now as doing so on all methods can generally have a negative impact on code size and sometimes performance. We can always selectively #[inline] things later on if need be (adding/removing it is backwards compatible)

@Gankra
Copy link
Contributor Author

Gankra commented Oct 5, 2014

The generic thing makes total sense (although I don't think it's really documented anywhere?). This did improve the benchmark performance though, so it seems reasonable to do? I could remove it from the bigger public functions, so that only the internal method separating gets reliably "undone"?

@thestinger
Copy link
Contributor

@gankro: #[inline] allows cross-crate inlining for non-generic code and also raises the inline threshold above the default. It's not usually a good idea to raise the inline threshold. It may improve the results on a micro-benchmark but it's likely a bad thing in real-world programs. Try using --opt-level=3 when testing rather than --opt-level=2.

@Gankra
Copy link
Contributor Author

Gankra commented Oct 5, 2014

A lot of generic collection code is #[inline], should we be evaluating undoing that?

@thestinger
Copy link
Contributor

It's not good that Rust makes the programmer do this by hand. Ideally it would be checking a threshold after optimizing the function and automatically exporting it. If that was implemented, all of the current #[inline] annotations could be removed and we could start over.

@thestinger
Copy link
Contributor

I don't think the current inline annotations on generics make sense in most cases. It would be useful if we could export generic instantiations but I'd like to think that we'll fix cross-crate inlining at some point.

@Gankra
Copy link
Contributor Author

Gankra commented Oct 5, 2014

@thestinger So that's a yes? We should seriously consider removing all the generic #[inline]'s? I doubt any of it was hand-picked for optimization (maybe some of your Vec stuff?).

@thestinger
Copy link
Contributor

There are a few bits of generic code like the Vec<T> push method where the hint is actually a good thing. It has been very overused though.

@thestinger
Copy link
Contributor

I think it would be a waste of time to go through them and remove the overuse. The time would be better spent on improving the compiler's implementation of inlining so nearly all of it is unnecessary, generic code or not. It would be pretty easy to improve it and I don't think it would be a huge challenge to make it smart enough to avoid the need for hints in 99% of the cases they're used today.

@Gankra Gankra force-pushed the collections-stuff branch from 394167a to 7c04b3c Compare October 5, 2014 23:59
@Gankra
Copy link
Contributor Author

Gankra commented Oct 6, 2014

Alright, good to know (I'd really appreciate if some official info on this could be provided for contributors!). I've removed the inline stuff. The rest seems perfectly good and worthwhile to include.

/// searches. However, this does mean that searches will have to do *more* comparisons on average.
/// The precise number of comparisons depends on the node search strategy used. For optimal cache
/// effeciency, one could search the nodes linearly. For optimal comparisons, one could search
/// search the node using binary search. As a compromise, one could also perform a linear search
Copy link
Member

Choose a reason for hiding this comment

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

"one could search search the node"

@Gankra
Copy link
Contributor Author

Gankra commented Oct 7, 2014

@killercup Thanks! Fixed.

@Gankra Gankra changed the title Doc BTree better, #[inline] everything, and add some iteration benches Doc BTree better, and add some iteration benches Oct 8, 2014
let mut rng = weak_rng();

for _ in range(0, size) {
map.swap(rng.gen(), rng.gen());
Copy link
Member

Choose a reason for hiding this comment

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

Any reason to go for swap instead of insert?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

No joke, there was actually a thought-process here: Because I expect insert to be deprecated in the future. If the changes are performed mechanically, this code could get transformed into something weird (since we don't care about the return value). It's a bit silly, honestly, but swap and insert are functionally identical so it doesn't really matter and I figured I'd play it safe.

Copy link
Member

Choose a reason for hiding this comment

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

Uh, I'm not sure I'm on board with deprecating the name insert, but okay.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

The proposal in the collections reform RFC is swap would be renamed insert. In my head all inserts then gets translated to insert().is_some()

Copy link
Member

Choose a reason for hiding this comment

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

Okay, cool, that's what I was hoping.

bors added a commit that referenced this pull request Oct 11, 2014
I previously avoided `#[inline]`ing anything assuming someone would come in and explain to me where this would be appropriate. Apparently no one *really* knows, so I'll just go the opposite way an inline everything assuming someone will come in and yell at me that such-and-such shouldn't be `#[inline]`.

==================

For posterity, iteration comparisons:

```
test btree::map::bench::iter_20                            ... bench:       971 ns/iter (+/- 30)
test btree::map::bench::iter_1000                          ... bench:     29445 ns/iter (+/- 480)
test btree::map::bench::iter_100000                        ... bench:   2929035 ns/iter (+/- 21551)

test treemap::bench::iter_20                               ... bench:       530 ns/iter (+/- 66)
test treemap::bench::iter_1000                             ... bench:     26287 ns/iter (+/- 825)
test treemap::bench::iter_100000                           ... bench:   7650084 ns/iter (+/- 356711)

test trie::bench_map::iter_20                              ... bench:       646 ns/iter (+/- 265)
test trie::bench_map::iter_1000                            ... bench:     43556 ns/iter (+/- 5014)
test trie::bench_map::iter_100000                          ... bench:  12988002 ns/iter (+/- 139676)
```

As you can see `btree` "scales" much better than `treemap`. `triemap` scales quite poorly.

Note that *completely* different results are given if the elements are inserted in order from the range [0, size]. In particular, TrieMap *completely* dominates in the sorted case. This suggests adding benches for both might be worthwhile. However unsorted is *probably* the more "normal" case, so I consider this "good enough" for now.
@bors bors closed this Oct 11, 2014
@bors bors merged commit f91c680 into rust-lang:master Oct 11, 2014
lnicola pushed a commit to lnicola/rust that referenced this pull request Aug 13, 2024
minor: Fix metrics not running

`@bors` r+
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.

6 participants