Skip to content
New issue

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

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

Already on GitHub? # 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
# for free to join this conversation on GitHub. Already have an account? # to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants