Skip to content

Commit c25f465

Browse files
committed
---
yaml --- r: 195580 b: refs/heads/master c: fd182f4 h: refs/heads/master v: v3
1 parent 3a49ca0 commit c25f465

File tree

360 files changed

+7576
-7288
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

360 files changed

+7576
-7288
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
refs/heads/master: 343c110e76ddb673ed8bfc22ea917c3146c7b6e3
2+
refs/heads/master: fd182f41dc5e0c90b491c416b60458e105e96659
33
refs/heads/snap-stage1: e33de59e47c5076a89eadeb38f4934f58a3618a6
44
refs/heads/snap-stage3: b3317d68910900f135f9f38e43a7a699bc736b4a
55
refs/heads/try: 961e0358e1a5c0faaef606e31e9965742c1643bf

trunk/src/compiletest/compiletest.rs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@
1818
#![feature(std_misc)]
1919
#![feature(test)]
2020
#![feature(path_ext)]
21-
#![feature(convert)]
2221
#![feature(str_char)]
2322

2423
#![deny(warnings)]

trunk/src/doc/reference.md

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -977,17 +977,13 @@ An example of `use` declarations:
977977

978978
```
979979
# #![feature(core)]
980-
use std::iter::range_step;
981980
use std::option::Option::{Some, None};
982981
use std::collections::hash_map::{self, HashMap};
983982
984983
fn foo<T>(_: T){}
985984
fn bar(map1: HashMap<String, usize>, map2: hash_map::HashMap<String, usize>){}
986985
987986
fn main() {
988-
// Equivalent to 'std::iter::range_step(0, 10, 2);'
989-
range_step(0, 10, 2);
990-
991987
// Equivalent to 'foo(vec![std::option::Option::Some(1.0f64),
992988
// std::option::Option::None]);'
993989
foo(vec![Some(1.0f64), None]);

trunk/src/doc/trpl/SUMMARY.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,6 @@
4242
* [Intrinsics](intrinsics.md)
4343
* [Lang items](lang-items.md)
4444
* [Link args](link-args.md)
45+
* [Benchmark Tests](benchmark-tests.md)
4546
* [Conclusion](conclusion.md)
4647
* [Glossary](glossary.md)

trunk/src/doc/trpl/benchmark-tests.md

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
% Benchmark tests
2+
3+
Rust supports benchmark tests, which can test the performance of your
4+
code. Let's make our `src/lib.rs` look like this (comments elided):
5+
6+
```{rust,ignore}
7+
#![feature(test)]
8+
9+
extern crate test;
10+
11+
pub fn add_two(a: i32) -> i32 {
12+
a + 2
13+
}
14+
15+
#[cfg(test)]
16+
mod tests {
17+
use super::*;
18+
use test::Bencher;
19+
20+
#[test]
21+
fn it_works() {
22+
assert_eq!(4, add_two(2));
23+
}
24+
25+
#[bench]
26+
fn bench_add_two(b: &mut Bencher) {
27+
b.iter(|| add_two(2));
28+
}
29+
}
30+
```
31+
32+
Note the `test` feature gate, which enables this unstable feature.
33+
34+
We've imported the `test` crate, which contains our benchmarking support.
35+
We have a new function as well, with the `bench` attribute. Unlike regular
36+
tests, which take no arguments, benchmark tests take a `&mut Bencher`. This
37+
`Bencher` provides an `iter` method, which takes a closure. This closure
38+
contains the code we'd like to benchmark.
39+
40+
We can run benchmark tests with `cargo bench`:
41+
42+
```bash
43+
$ cargo bench
44+
Compiling adder v0.0.1 (file:///home/steve/tmp/adder)
45+
Running target/release/adder-91b3e234d4ed382a
46+
47+
running 2 tests
48+
test tests::it_works ... ignored
49+
test tests::bench_add_two ... bench: 1 ns/iter (+/- 0)
50+
51+
test result: ok. 0 passed; 0 failed; 1 ignored; 1 measured
52+
```
53+
54+
Our non-benchmark test was ignored. You may have noticed that `cargo bench`
55+
takes a bit longer than `cargo test`. This is because Rust runs our benchmark
56+
a number of times, and then takes the average. Because we're doing so little
57+
work in this example, we have a `1 ns/iter (+/- 0)`, but this would show
58+
the variance if there was one.
59+
60+
Advice on writing benchmarks:
61+
62+
63+
* Move setup code outside the `iter` loop; only put the part you want to measure inside
64+
* Make the code do "the same thing" on each iteration; do not accumulate or change state
65+
* Make the outer function idempotent too; the benchmark runner is likely to run
66+
it many times
67+
* Make the inner `iter` loop short and fast so benchmark runs are fast and the
68+
calibrator can adjust the run-length at fine resolution
69+
* Make the code in the `iter` loop do something simple, to assist in pinpointing
70+
performance improvements (or regressions)
71+
72+
## Gotcha: optimizations
73+
74+
There's another tricky part to writing benchmarks: benchmarks compiled with
75+
optimizations activated can be dramatically changed by the optimizer so that
76+
the benchmark is no longer benchmarking what one expects. For example, the
77+
compiler might recognize that some calculation has no external effects and
78+
remove it entirely.
79+
80+
```{rust,ignore}
81+
#![feature(test)]
82+
83+
extern crate test;
84+
use test::Bencher;
85+
86+
#[bench]
87+
fn bench_xor_1000_ints(b: &mut Bencher) {
88+
b.iter(|| {
89+
(0..1000).fold(0, |old, new| old ^ new);
90+
});
91+
}
92+
```
93+
94+
gives the following results
95+
96+
```text
97+
running 1 test
98+
test bench_xor_1000_ints ... bench: 0 ns/iter (+/- 0)
99+
100+
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
101+
```
102+
103+
The benchmarking runner offers two ways to avoid this. Either, the closure that
104+
the `iter` method receives can return an arbitrary value which forces the
105+
optimizer to consider the result used and ensures it cannot remove the
106+
computation entirely. This could be done for the example above by adjusting the
107+
`b.iter` call to
108+
109+
```rust
110+
# struct X;
111+
# impl X { fn iter<T, F>(&self, _: F) where F: FnMut() -> T {} } let b = X;
112+
b.iter(|| {
113+
// note lack of `;` (could also use an explicit `return`).
114+
(0..1000).fold(0, |old, new| old ^ new)
115+
});
116+
```
117+
118+
Or, the other option is to call the generic `test::black_box` function, which
119+
is an opaque "black box" to the optimizer and so forces it to consider any
120+
argument as used.
121+
122+
```rust
123+
#![feature(test)]
124+
125+
extern crate test;
126+
127+
# fn main() {
128+
# struct X;
129+
# impl X { fn iter<T, F>(&self, _: F) where F: FnMut() -> T {} } let b = X;
130+
b.iter(|| {
131+
let n = test::black_box(1000);
132+
133+
(0..n).fold(0, |a, b| a ^ b)
134+
})
135+
# }
136+
```
137+
138+
Neither of these read or modify the value, and are very cheap for small values.
139+
Larger values can be passed indirectly to reduce overhead (e.g.
140+
`black_box(&huge_struct)`).
141+
142+
Performing either of the above changes gives the following benchmarking results
143+
144+
```text
145+
running 1 test
146+
test bench_xor_1000_ints ... bench: 131 ns/iter (+/- 3)
147+
148+
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured
149+
```
150+
151+
However, the optimizer can still modify a testcase in an undesirable manner
152+
even when using either of the above.

0 commit comments

Comments
 (0)