mirror of
https://github.com/darkicewolf50/RustBrock.git
synced 2025-06-15 13:04:18 -06:00
finished ch13.4 and finished ch13
All checks were successful
Test Gitea Actions / first (push) Successful in 13s
All checks were successful
Test Gitea Actions / first (push) Successful in 13s
This commit is contained in:
parent
d961dd4ee9
commit
c47bd9cfae
@ -17,4 +17,15 @@ It will cover:
|
||||
|
||||
We have already covered some other Rust freatures, such as pattern matchin and enums, that are also influenced by the functional style.
|
||||
|
||||
Mastering closures and iterators is an improtant part of wiritng idiomatic, fast Rust code.
|
||||
Mastering closures and iterators is an improtant part of wiritng idiomatic, fast Rust code.
|
||||
|
||||
|
||||
## Summary
|
||||
|
||||
Closures and iterators are Rust features inspired by functional programming language ideas.
|
||||
|
||||
They contribute to the capability to express high-level ideas at low-level preformance.
|
||||
|
||||
The implementations of closures and iterators are such that runtime performance is not affected.
|
||||
|
||||
This is part of Rust's goal to strive to provide zero-cost abstractions.
|
@ -0,0 +1,84 @@
|
||||
# Comparing Performance: Loops vs. Iterators
|
||||
|
||||
To determince whether to use loops or iterators, you need to know which implementation is faster
|
||||
|
||||
Is it the version with an explicit `for` loop?
|
||||
|
||||
Is it the version with iterators?
|
||||
|
||||
Here is a benchmark where we load the entire contnets of *The Adventures of Sherlock Holmes* by Sir Arthur Conan Doyle into a `String` and looking ofr the word *the* in the contents
|
||||
|
||||
Here is the results of the benchmark on the version of `search` using the `for` loop and the version using iterators:
|
||||
```
|
||||
test bench_search_for ... bench: 19,620,300 ns/iter (+/- 915,700)
|
||||
test bench_search_iter ... bench: 19,234,900 ns/iter (+/- 657,200)
|
||||
```
|
||||
The two have similar performace!
|
||||
|
||||
We won't explain the benchmark code here, because the poin is not to prive that the two versions are equivalent but to get a general sense of how these two compare performance-wise.
|
||||
|
||||
For a more comprehensive benchmark, you should check using various texts of various sizes as the `contents`, different words and words of different lenghts as the `query` and all kinds of other variations.
|
||||
|
||||
The point is this, iterators, although a high-level abstraction, get compiled down to roughly the same code as if you wrote the lower-level code yourself.
|
||||
|
||||
Iterators are one of Rust's *zero-cost abstractions`. This means using the abstraction imposes no addtional runtime overhead.
|
||||
|
||||
This is analogous to how Bjarne Stroustrup, the original designer and implementor of C++ defines *zero-verhead* in "Fondations of C++" (2012)
|
||||
```
|
||||
In general, C++ implementations obey the zero-overhead principle: What you don’t use, you don’t pay for. And further: What you do use, you couldn’t hand code any better.
|
||||
```
|
||||
|
||||
Here is another example, the code is taken from an audio decoder.
|
||||
|
||||
The decoding algorithm uses the linear prediction mathematical operation to estimate future values based on a linear function of the previous samples.
|
||||
|
||||
This code uses an iterator chain to do some math on three variables in scope:
|
||||
- a `buffer` slice of data
|
||||
- an array of `coefficients`
|
||||
- an amount ot which shift data in `qlp_shift`
|
||||
|
||||
We declared the variables within this example but not given them any values.
|
||||
|
||||
Even though this code doesnt have muc meaning outside of its context, it is still a concise, real-world example of how Rust tranlate high-level ideas to low-level code.
|
||||
```rust
|
||||
let buffer: &mut [i32];
|
||||
let coefficients: [i64; 12];
|
||||
let qlp_shift: i16;
|
||||
|
||||
for i in 12..buffer.len() {
|
||||
let prediction = coefficients.iter()
|
||||
.zip(&buffer[i - 12..i])
|
||||
.map(|(&c, &s)| c * s as i64)
|
||||
.sum::<i64>() >> qlp_shift;
|
||||
let delta = buffer[i];
|
||||
buffer[i] = prediction as i32 + delta;
|
||||
}
|
||||
```
|
||||
To calcuate the value of `prediction`, the code iterates though each of he 12 values in `coefficients` and ses the `zip` method to pair the coefficient values with the previous 12 values in `buffer`
|
||||
|
||||
For each pair then we multiply the values together, sum all the results and shift the bits in the sum `qlp_shift` bits to the right.
|
||||
|
||||
Calculations in applications like audio decoders often prioritize perfomance.
|
||||
|
||||
Here we are creating an iterator, using two adapters and then consuming the value.
|
||||
|
||||
What assembly would this Rust code compile into.
|
||||
|
||||
As of writing this in the book, it compiles down to the same assbly you would write by hand.
|
||||
|
||||
There is no loop at all corresponding to the iteration over the values in `coefficients`.
|
||||
|
||||
Rust knows that there are 12 iterations, so it "unrolls" the loop.
|
||||
|
||||
*Unrolling* is an optimization that removes the overhead of the loop controlling code and instead generates repetitive code for each iteration fo the loop.
|
||||
|
||||
All of the coefficients ge stored in registers which means accessing the values is very fast.
|
||||
|
||||
There are no bounds checks on the array access at runtime.
|
||||
|
||||
All of these optimizations that Rust is able to apply make the resulting code extremely efficient.
|
||||
|
||||
Now you know that you can use iterators and closures without fear of performance hits.
|
||||
|
||||
They make code seem like its highe level but again dont impose a performance hit.
|
||||
|
Loading…
x
Reference in New Issue
Block a user