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

Use a union to reduce the size of SmallVec [v2] #94

Merged
merged 4 commits into from
Jun 6, 2018

Conversation

arthurprs
Copy link
Contributor

@arthurprs arthurprs commented May 10, 2018

Building on top of #92 by @Amanieu

I introduced triple() and triple_mut() which removed almost all of the runtime overhead. Performance is very comparable.

 name                     master:: ns/iter  union:: ns/iter  diff ns/iter   diff %  speedup 
 bench_extend             45                47                          2    4.44%   x 0.96 
 bench_extend_from_slice  45                43                         -2   -4.44%   x 1.05 
 bench_from_slice         45                44                         -1   -2.22%   x 1.02 
 bench_insert             615               622                         7    1.14%   x 0.99 
 bench_insert_from_slice  101               99                         -2   -1.98%   x 1.02 
 bench_insert_many        309               266                       -43  -13.92%   x 1.16 
 bench_macro_from_elem    41                37                         -4   -9.76%   x 1.11 
 bench_macro_from_list    40                42                          2    5.00%   x 0.95 
 bench_push               381               370                       -11   -2.89%   x 1.03 
 bench_pushpop            404               420                        16    3.96%   x 0.96 
 bench_remove             458               436                       -22   -4.80%   x 1.05 

This change is Reviewable

@eira-fransham
Copy link
Contributor

eira-fransham commented May 22, 2018

Your benchmark changes will definitely cause conflicts with #97, I don't know if you want to add your bench changes as a seperate PR to avoid that (I don't mind rolling them into #97 instead if you like).

@arthurprs
Copy link
Contributor Author

@Vurich fell free to put them into #97 and I'll rebase this.

eira-fransham added a commit to eira-fransham/rust-smallvec that referenced this pull request May 22, 2018
@bors-servo
Copy link
Contributor

☔ The latest upstream changes (presumably #97) made this pull request unmergeable. Please resolve the merge conflicts.

@arthurprs
Copy link
Contributor Author

I rebased on master now, the only change I kept was to add the return type to remove_no_inline (to prevent over optimization, specially for Vec).

lib.rs Outdated
//! Note that `smallvec` can still be larger than `Vec` if the inline buffer is larger than two
//! machine words.
//!
//! To use this feature add `features = ["uuid"]` in the `smallvec` section of Cargo.toml.
Copy link

Choose a reason for hiding this comment

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

Should be union instead of uuid, right?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ops, will fix.

@arthurprs
Copy link
Contributor Author

It'd be nice to get this moving. It may be interesting for rustc as well.

@jdm
Copy link
Member

jdm commented May 31, 2018

@mbrubeck Do you have time to review these changes?

Copy link
Collaborator

@mbrubeck mbrubeck left a comment

Choose a reason for hiding this comment

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

This is great, thanks! Some requests/suggestions below.

Cargo.toml Outdated
default = ["std"]

[lib]
name = "smallvec"
path = "lib.rs"

[dependencies]
unreachable = "1.0.0"
debug_unreachable = "0.1"
Copy link
Collaborator

Choose a reason for hiding this comment

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

We may not want to use debug_unreachable, because it is unmaintained and doesn't work correctly in Rust > 1.0: reem/rust-debug-unreachable#6

We could just use unreachable all the time, or write our own debug_unreachable macro, or write a new debug_unreachable-like crate...

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense, I'll remove the dependency and add the equivalent macro.

@@ -350,14 +419,25 @@ impl<A: Array> SmallVec<A> {
/// ```
#[inline]
pub fn from_vec(mut vec: Vec<A::Item>) -> SmallVec<A> {
let (ptr, cap, len) = (vec.as_mut_ptr(), vec.capacity(), vec.len());
mem::forget(vec);
if vec.capacity() <= A::size() {
Copy link
Collaborator

Choose a reason for hiding this comment

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

The documentation says that from_vec does not copy the elements. I think we should keep the current behavior of keeping the items on the heap. Users who want to move them inline if possible can use shrink_to_fit or other methods.

Copy link
Contributor

Choose a reason for hiding this comment

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

This is unfortunately not possible with the new design: there is no way of representing a heap-allocated buffer with a capacity less than A::size(). If self.capacity <= A::size() then the code assumes that the data is in the inline buffer.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Oh, right. In that case the documentation should be updated.

lib.rs Outdated
@@ -301,7 +370,7 @@ impl<A: Array> Drop for SmallVecData<A> {
/// assert!(v.spilled());
/// ```
pub struct SmallVec<A: Array> {
len: usize,
capacity: usize,
Copy link
Collaborator

Choose a reason for hiding this comment

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

Using capacity here is a little confusing to me, but I'm not sure what a better name would be. Maybe something like len_cap? Maybe capacity is okay, but there should be a comment here describing the semantics.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good point.

lib.rs Outdated
}

#[inline]
fn triple(&self) -> (*const A::Item, usize, usize) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please add a comment to say what the return value means.

lib.rs Outdated
while len < *len_ptr {
let last_index = *len_ptr - 1;
*len_ptr = last_index;
ptr::read(ptr.offset(last_index as isize));
Copy link
Collaborator

Choose a reason for hiding this comment

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

This could use ptr::drop_in_place instead of ptr::read.

lib.rs Outdated
} else {
let ptr = self.as_ptr();
for i in 0..self.len() {
ptr::read(ptr.offset(i as isize));
Copy link
Collaborator

Choose a reason for hiding this comment

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

This could use ptr::drop_in_place instead.

@mbrubeck
Copy link
Collaborator

mbrubeck commented Jun 2, 2018

Great, thanks! I think this is ready but I just want to double-check all the unsafe code before merging it (and give a chance for others to do the same).

@mbrubeck
Copy link
Collaborator

mbrubeck commented Jun 6, 2018

@bors-servo r+

@bors-servo
Copy link
Contributor

📌 Commit cfa1f0c has been approved by mbrubeck

@bors-servo
Copy link
Contributor

⌛ Testing commit cfa1f0c with merge 93c530f...

bors-servo pushed a commit that referenced this pull request Jun 6, 2018
Use a union to reduce the size of SmallVec [v2]

Building on top of #92 by @Amanieu

I introduced `triple()` and `triple_mut()` which removed almost all of the runtime overhead. Performance is very comparable.

```
 name                     master:: ns/iter  union:: ns/iter  diff ns/iter   diff %  speedup
 bench_extend             45                47                          2    4.44%   x 0.96
 bench_extend_from_slice  45                43                         -2   -4.44%   x 1.05
 bench_from_slice         45                44                         -1   -2.22%   x 1.02
 bench_insert             615               622                         7    1.14%   x 0.99
 bench_insert_from_slice  101               99                         -2   -1.98%   x 1.02
 bench_insert_many        309               266                       -43  -13.92%   x 1.16
 bench_macro_from_elem    41                37                         -4   -9.76%   x 1.11
 bench_macro_from_list    40                42                          2    5.00%   x 0.95
 bench_push               381               370                       -11   -2.89%   x 1.03
 bench_pushpop            404               420                        16    3.96%   x 0.96
 bench_remove             458               436                       -22   -4.80%   x 1.05
```

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-smallvec/94)
<!-- Reviewable:end -->
@bors-servo
Copy link
Contributor

☀️ Test successful - status-travis
Approved by: mbrubeck
Pushing 93c530f to master...

@bors-servo bors-servo merged commit cfa1f0c into servo:master Jun 6, 2018
mbrubeck added a commit to mbrubeck/rust-smallvec that referenced this pull request Jul 19, 2018
Changes in this release:

* Fix possible double-free in `insert_many` when passed an iterator that
  panics in `next` (servo#103)
* Add a new `union` feature (disabled by default) that reduces the size
  of the SmallVec struct (servo#94)
* Improve performance of `extend` and `from_elem` (servo#93)
* Improve performance of `drop` (servo#100)
* Update dev-dependency on `bincode` (servo#102)
* Update to build without `std` on current Rust nightly (servo#104)
* Additional benchmarks (servo#95, servo#97).
mbrubeck added a commit to mbrubeck/rust-smallvec that referenced this pull request Jul 19, 2018
Changes in this release:

* Fix possible double-free in `insert_many` when passed an iterator that
  panics in `next` (servo#103)
* Add a new `union` feature (disabled by default) that reduces the size
  of the SmallVec struct (servo#94)
* Improve performance of `extend` and `from_elem` (servo#93)
* Improve performance of `drop` (servo#100)
* Update dev-dependency on `bincode` (servo#102)
* Update to build without `std` on current Rust nightly (servo#104)
* Additional benchmarks (servo#95, servo#97).
@mbrubeck mbrubeck mentioned this pull request Jul 19, 2018
bors-servo pushed a commit that referenced this pull request Jul 19, 2018
Version 0.6.3

Changes in this release:

* Fix possible double-free in `insert_many` when passed an iterator that panics in `next` (#103)
* Add a new `union` feature (disabled by default) that reduces the size of the SmallVec struct (#94)
* Improve performance of `extend` and `from_elem` (#93)
* Improve performance of `drop` (#100)
* Update to build without `std` feature on current Rust nightly (#104)
* Additional benchmarks (#95, #97)
* Update dev-dependency on `bincode` (#102)

<!-- Reviewable:start -->
---
This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/rust-smallvec/105)
<!-- Reviewable:end -->
@mbrubeck mbrubeck mentioned this pull request Jul 30, 2018
12 tasks
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.

7 participants