diff --git a/Iterators and Closures.md b/Iterators and Closures.md index e92a203..2ae8e6c 100644 --- a/Iterators and Closures.md +++ b/Iterators and Closures.md @@ -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. \ No newline at end of file +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. \ No newline at end of file diff --git a/The preformance Closures and Iterators.md b/The preformance Closures and Iterators.md index e69de29..b99c16d 100644 --- a/The preformance Closures and Iterators.md +++ b/The preformance Closures and Iterators.md @@ -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::() >> 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. +