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

Replace push loops with extend() where possible #52738

Merged
merged 1 commit into from
Jul 29, 2018

Conversation

ljedrz
Copy link
Contributor

@ljedrz ljedrz commented Jul 26, 2018

Or set the vector capacity where I couldn't do it.

According to my simple benchmark extending a vector can be over 10 times faster than pushing to it in a loop:

10 elements (6.1 times faster):

test bench_extension ... bench:          75 ns/iter (+/- 23)
test bench_push_loop ... bench:         458 ns/iter (+/- 142)

100 elements (11.12 times faster):

test bench_extension ... bench:          87 ns/iter (+/- 26)
test bench_push_loop ... bench:         968 ns/iter (+/- 3,528)

1000 elements (11.04 times faster):

test bench_extension ... bench:         311 ns/iter (+/- 9)
test bench_push_loop ... bench:       3,436 ns/iter (+/- 233)

Seems like a good idea to use extend as much as possible.

@rust-highfive
Copy link
Collaborator

r? @eddyb

(rust_highfive has picked a reviewer for you, use r? to override)

@rust-highfive rust-highfive added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 26, 2018
@Eh2406
Copy link
Contributor

Eh2406 commented Jul 27, 2018

Drive by comment, All these lovely clean up prs you have submitted... maybe we should suggest that clippy lint for them? So they can spread to the ecosystem?

@ljedrz
Copy link
Contributor Author

ljedrz commented Jul 28, 2018

@Eh2406 clippy has an impressive set of working lints and some of them were a direct inspiration for some of my cleanups. I checked the ones that I haven't seen in clippy and it appears that they have already been proposed 😃:

@@ -949,7 +949,7 @@ impl Expr {
ExprKind::Array(exprs) if exprs.len() == 1 =>
exprs[0].to_ty().map(TyKind::Slice)?,
ExprKind::Tup(exprs) => {
let mut tys = Vec::new();
let mut tys = Vec::with_capacity(exprs.len());
for expr in exprs {
tys.push(expr.to_ty()?);
Copy link
Member

Choose a reason for hiding this comment

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

You can do this sort of thing by using .collect::<Result<Vec<_>, _>>() (for Result element types).

for &LoopScope { loop_id: id, .. } in self.loop_scopes.iter().rev() {
data.exiting_scopes.push(id);
}
data.exiting_scopes.extend(
Copy link
Member

Choose a reason for hiding this comment

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

Can be collect instead.

for (_, obligation) in r_o.drain_filter(|(ro_body_id, _)| *ro_body_id == body_id) {
my_region_obligations.push(obligation);
}
my_region_obligations.extend(
Copy link
Member

Choose a reason for hiding this comment

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

Can be collect instead.

}
signature_metadata.extend(
signature.inputs().iter().map(|argument_type| type_metadata(cx, argument_type, span))
);
Copy link
Member

Choose a reason for hiding this comment

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

Can be collect instead (using iter::once for the first element above).

for n in glob_map.get(&id).unwrap() {
names.push(n.to_string());
}
names.extend(glob_map.get(&id).unwrap().iter().map(|n| n.to_string()))
Copy link
Member

Choose a reason for hiding this comment

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

Can be collect instead.

for arg in self.args.iter() {
args.push([arg.as_ptr() as usize, arg.len()]);
}
args.extend(self.args.iter().map(|arg| [arg.as_ptr() as usize, arg.len()]));
Copy link
Member

Choose a reason for hiding this comment

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

Can be collect instead (with iter::once).

for declared_bound in &param.bounds {
bounds.push((*declared_bound).clone());
}
bounds.extend(param.bounds.iter().cloned());
Copy link
Member

Choose a reason for hiding this comment

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

Can be collect instead.

@ljedrz
Copy link
Contributor Author

ljedrz commented Jul 28, 2018

@eddyb great suggestions, applied. thanks!

@ljedrz
Copy link
Contributor Author

ljedrz commented Jul 29, 2018

Rebased after 1bc49a9.

}
let mut r_o = self.region_obligations.borrow_mut();
let my_region_obligations = r_o.drain_filter(|(ro_body_id, _)| *ro_body_id == body_id)
.map(|(_, obligation)| obligation);
Copy link
Member

Choose a reason for hiding this comment

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

Pretty sure you need to collect this, and not hold that borrow_mut for that long, because the loop calls methods on self which could try to borrow again self.region_obligations. cc @nikomatsakis

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wanted to collect this, but the borrowed value didn't live long enough.

Copy link
Member

Choose a reason for hiding this comment

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

What did you write? I'm guessing it's just some scope thing.

Copy link
Member

Choose a reason for hiding this comment

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

In general, let v = { let mut x = y.borrow_mut(); x.foo().collect() }; doesn't work, you need another variable inside the block, e.g. let v = { let mut x = y.borrow_mut(); let v = x.foo().collect(); v };

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah yeah, that should work; I'll fix this shortly.

@@ -500,7 +500,7 @@ impl Pat {
PatKind::Slice(pats, None, _) if pats.len() == 1 =>
pats[0].to_ty().map(TyKind::Slice)?,
PatKind::Tuple(pats, None) => {
let mut tys = Vec::new();
let mut tys = Vec::with_capacity(pats.len());
for pat in pats {
tys.push(pat.to_ty()?);
Copy link
Member

Choose a reason for hiding this comment

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

Pretty sure this can also use collect (it's a lot like the code below).

@eddyb
Copy link
Member

eddyb commented Jul 29, 2018

@bors r+

@bors
Copy link
Contributor

bors commented Jul 29, 2018

📌 Commit 6071fb762a3add82a54307fbe79df83c5c9bdb85 has been approved by eddyb

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 29, 2018
@bors
Copy link
Contributor

bors commented Jul 29, 2018

⌛ Testing commit 6071fb762a3add82a54307fbe79df83c5c9bdb85 with merge e7f363a2f3b01512e6ca1cdd569db506711f38a5...

@bors
Copy link
Contributor

bors commented Jul 29, 2018

💔 Test failed - status-travis

@bors bors removed the S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. label Jul 29, 2018
@bors bors added the S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. label Jul 29, 2018
@rust-highfive
Copy link
Collaborator

The job dist-various-1 of your PR failed on Travis (raw log). Through arcane magic we have determined that the following fragments from the build log may contain information about the problem.

Click to expand the log.
[01:03:11] 
[01:03:11] error[E0433]: failed to resolve. Use of undeclared type or module `iter`
[01:03:11]    --> libstd/sys/redox/process.rs:299:37
[01:03:11]     |
[01:03:11] 299 |         let args: Vec<[usize; 2]> = iter::once(
[01:03:11]     |                                     ^^^^ Use of undeclared type or module `iter`
[01:03:14] error: aborting due to previous error
[01:03:14] 
[01:03:14] For more information about this error, try `rustc --explain E0433`.
[01:03:14] [RUSTC-TIMING] std test:false 3.442
[01:03:14] [RUSTC-TIMING] std test:false 3.442
[01:03:14] error: Could not compile `std`.
[01:03:14] 
[01:03:14] Caused by:
[01:03:14]   process didn't exit successfully: `/checkout/obj/build/bootstrap/debug/rustc --crate-name std libstd/lib.rs --color always --error-format json --crate-type dylib --crate-type rlib --emit=dep-info,link -C prefer-dynamic -C opt-level=2 --cfg feature="alloc_jemalloc" --cfg feature="backtrace" --cfg feature="jemalloc" --cfg feature="panic-unwind" --cfg feature="panic_unwind" -C metadata=5acb4a772c226432 -C extra-filename=-5acb4a772c226432 --out-dir /checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/deps --target x86_64-unknown-redox -L dependency=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/deps -L dependency=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/release/deps --extern alloc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/deps/liballoc-54de1e6e18170c67.rlib --extern alloc_jemalloc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/deps/liballoc_jemalloc-53666a930e3ad04f.rlib --extern alloc_system=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/deps/liballoc_system-42b58af3303ea433.rlib --extern compiler_builtins=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/deps/libcompiler_builtins-77c89b8f8fd767a0.rlib --extern core=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/deps/libcore-2706c4a39e374c53.rlib --extern libc=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/deps/liblibc-c218be5828e2f028.rlib --extern panic_abort=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/deps/libpanic_abort-4a8dc0e01b2e80eb.rlib --extern panic_unwind=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/deps/libpanic_unwind-ec538ede862f0199.rlib --extern std_unicode=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/deps/libstd_unicode-ee9c3372b170ad3f.rlib --extern unwind=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/deps/libunwind-7481e04d1a7a227f.rlib -L native=/checkout/obj/build/x86_64-unknown-redox/native/libbacktrace/ -L native=/checkout/obj/build/x86_64-unknown-redox/native/libbacktrace -l static=backtrace -l static=backtrace -L native=/checkout/obj/build/x86_64-unknown-linux-gnu/stage2-std/x86_64-unknown-redox/release/build/compiler_builtins-3dbe2456c9d3a72d/out` (exit code: 1)
[01:03:14] command did not execute successfully: "/checkout/obj/build/x86_64-unknown-linux-gnu/stage0/bin/cargo" "build" "--target" "x86_64-unknown-redox" "-j" "4" "--release" "--locked" "--color" "always" "--features" "panic-unwind jemalloc backtrace" "--manifest-path" "/checkout/src/libstd/Cargo.toml" "--message-format" "json"
[01:03:14] expected success, got: exit code: 101
[01:03:14] thread 'main' panicked at 'cargo must succeed', bootstrap/compile.rs:1119:9
[01:03:14] travis_fold:end:stage2-std

[01:03:14] travis_time:end:stage2-std:start=1532881947584942148,finish=1532881987197051641,duration=39612109493

---
travis_time:end:0237a2ce:start=1532881989526454383,finish=1532881989534908820,duration=8454437
travis_fold:end:after_failure.3
travis_fold:start:after_failure.4
travis_time:start:26c40e98
$ ln -s . checkout && for CORE in obj/cores/core.*; do EXE=$(echo $CORE | sed 's|obj/cores/core\.[0-9]*\.!checkout!\(.*\)|\1|;y|!|/|'); if [ -f "$EXE" ]; then printf travis_fold":start:crashlog\n\033[31;1m%s\033[0m\n" "$CORE"; gdb -q -c "$CORE" "$EXE" -iex 'set auto-load off' -iex 'dir src/' -iex 'set sysroot .' -ex bt -ex q; echo travis_fold":"end:crashlog; fi; done || true
travis_fold:end:after_failure.4
travis_fold:start:after_failure.5
travis_time:start:35feb0b4
travis_time:start:35feb0b4
$ cat ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers || true
cat: ./obj/build/x86_64-unknown-linux-gnu/native/asan/build/lib/asan/clang_rt.asan-dynamic-i386.vers: No such file or directory
travis_fold:end:after_failure.5
travis_fold:start:after_failure.6
travis_time:start:03a8a830
$ dmesg | grep -i kill

I'm a bot! I can only do what humans tell me to, so if this was not helpful or you have suggestions for improvements, please ping or otherwise contact @TimNN. (Feature Requests)

@ljedrz
Copy link
Contributor Author

ljedrz commented Jul 29, 2018

Ah yes, Redox isn't checked on a non-Redox machine 😜. It should work now.

@ljedrz
Copy link
Contributor Author

ljedrz commented Jul 29, 2018

@eddyb can you ask bors to retry?

@eddyb
Copy link
Member

eddyb commented Jul 29, 2018

@bors r+

@bors
Copy link
Contributor

bors commented Jul 29, 2018

📌 Commit 59c8a27 has been approved by eddyb

@bors bors added S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Jul 29, 2018
@bors
Copy link
Contributor

bors commented Jul 29, 2018

⌛ Testing commit 59c8a27 with merge 866a713...

bors added a commit that referenced this pull request Jul 29, 2018
Replace push loops with extend() where possible

Or set the vector capacity where I couldn't do it.

According to my [simple benchmark](https://gist.github.com/ljedrz/568e97621b749849684c1da71c27dceb) `extend`ing a vector can be over **10 times** faster than `push`ing to it in a loop:

10 elements (6.1 times faster):
```
test bench_extension ... bench:          75 ns/iter (+/- 23)
test bench_push_loop ... bench:         458 ns/iter (+/- 142)
```

100 elements (11.12 times faster):
```
test bench_extension ... bench:          87 ns/iter (+/- 26)
test bench_push_loop ... bench:         968 ns/iter (+/- 3,528)
```

1000 elements (11.04 times faster):
```
test bench_extension ... bench:         311 ns/iter (+/- 9)
test bench_push_loop ... bench:       3,436 ns/iter (+/- 233)
```

Seems like a good idea to use `extend` as much as possible.
@bors
Copy link
Contributor

bors commented Jul 29, 2018

☀️ Test successful - status-appveyor, status-travis
Approved by: eddyb
Pushing 866a713 to master...

@bors bors merged commit 59c8a27 into rust-lang:master Jul 29, 2018
@ljedrz ljedrz deleted the push_to_extend branch July 30, 2018 07:07
Mark-Simulacrum added a commit to Mark-Simulacrum/rust that referenced this pull request Aug 1, 2018
…-Simulacrum

Use Vec::extend in SmallVec::extend when applicable

As calculated in rust-lang#52738, `Vec::extend` is much faster than `push`ing to it in a loop. We can take advantage of this method in `SmallVec` too - at least in cases when its underlying object is an `AccumulateVec::Heap`.

~~This approach also accidentally improves the `push` loop of the `AccumulateVec::Array` variant, because it doesn't utilize `SmallVec::push` which performs `self.reserve(1)` with every iteration; this is unnecessary, because we're already reserving the whole space we will be needing by performing `self.reserve(iter.size_hint().0)` at the beginning.~~
pietroalbini added a commit to pietroalbini/rust that referenced this pull request Aug 1, 2018
…-Simulacrum

Use Vec::extend in SmallVec::extend when applicable

As calculated in rust-lang#52738, `Vec::extend` is much faster than `push`ing to it in a loop. We can take advantage of this method in `SmallVec` too - at least in cases when its underlying object is an `AccumulateVec::Heap`.

~~This approach also accidentally improves the `push` loop of the `AccumulateVec::Array` variant, because it doesn't utilize `SmallVec::push` which performs `self.reserve(1)` with every iteration; this is unnecessary, because we're already reserving the whole space we will be needing by performing `self.reserve(iter.size_hint().0)` at the beginning.~~
kennytm added a commit to kennytm/rust that referenced this pull request Aug 10, 2018
Don't collect() when size_hint is useless

This adjusts PRs rust-lang#52738 and rust-lang#52697 by falling back to calculating capacity and extending or pushing in a loop where `collect()` can't be trusted to calculate the right capacity.

It is a performance win.
GuillaumeGomez added a commit to GuillaumeGomez/rust that referenced this pull request Aug 12, 2018
Don't collect() when size_hint is useless

This adjusts PRs rust-lang#52738 and rust-lang#52697 by falling back to calculating capacity and extending or pushing in a loop where `collect()` can't be trusted to calculate the right capacity.

It is a performance win.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-bors Status: Waiting on bors to run and complete tests. Bors will change the label on completion.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants