Optimizing Rust Application Performance: For Beginners

Online Python Trainer for Beginners

Learn Python easily without overwhelming theory. Solve practical tasks with automatic checking, get hints in Russian, and write code directly in your browser — no installation required.

Start Course

Introduction: Why Rust is Already Fast, But That's Not the Limit

\n\n

Rust is rightfully considered one of the most performant systems programming languages. Thanks to zero-cost abstractions and a strict ownership system, Rust code often runs as fast as equivalent C or C++ code. However, even in Rust there are "pitfalls" that can slow down an application, especially for beginner developers.

\n\n

In this article, we will break down the key principles and practical techniques for optimizing Rust applications. You will learn how to profile code, avoid unnecessary allocations, work efficiently with collections, and harness the power of the compiler. The material is aimed at beginners, so all examples will be as simple and clear as possible.

\n\n

Ready to make your Rust code even faster? Let's go!

\n\n

1. Profiling: Measure First, Optimize Later

\n\n

Before optimizing anything, you need to understand what is actually slowing things down. Rust has excellent tools for this.

\n\n

1.1. Benchmarks with cargo bench

\n\n

The standard way is to use benchmarks. They allow you to measure the execution time of specific functions.

\n\n
// In file benches/my_bench.rs\nuse criterion::{criterion_group, criterion_main, Criterion};\n\nfn fibonacci(n: u64) -> u64 {\n    match n {\n        0 => 1,\n        1 => 1,\n        n => fibonacci(n-1) + fibonacci(n-2),\n    }\n}\n\nfn bench_fib(c: &mut Criterion) {\n    c.bench_function("fib 20", |b| b.iter(|| fibonacci(20)));\n}\n\ncriterion_group!(benches, bench_fib);\ncriterion_main!(benches);\n
\n\n

To run, add to Cargo.toml:

\n
[dev-dependencies]\ncriterion = "0.5"\n\n[[bench]]\nname = "my_bench"\nharness = false
\n\n

Then execute cargo bench. You will get precise numbers: execution time, standard deviation, and comparison with previous runs.

\n\n

1.2. CPU and Memory Profiling

\n\n

For deeper analysis, use:

\n
    \n
  • perf (Linux) — a kernel-level profiler.
  • \n
  • Valgrind — for finding memory leaks and cache analysis.
  • \n
  • flamegraph — visualization of code "hot spots".
  • \n
\n\n

Example of generating a flamegraph:

\n
cargo install flamegraph\ncargo flamegraph --bin my_app\n# Open flamegraph.svg in a browser\n
\n\n

A flamegraph will show which functions consume the most time. This will help you focus on what matters most.

\n\n

2. Memory Management: Avoiding Unnecessary Allocations

\n\n

Heap allocations are one of the most common causes of slow code. Rust gives you full control over memory, but beginners often overuse Box, Vec, and String.

\n\n

2.1. Use the Stack Instead of the Heap

\n\n

Whenever possible, store data on the stack. For example, instead of a Vec for a small fixed set, use an array:

\n\n
// Slow: heap allocation\nlet data: Vec<i32> = vec![1, 2, 3, 4, 5];\n\n// Fast: stack\nlet data: [i32; 5] = [1, 2, 3, 4, 5];\n
\n\n

2.2. Pre-allocate Memory

\n\n

If the size of a collection is known in advance, use with_capacity:

\n\n
// Bad: many reallocations\nlet mut v = Vec::new();\nfor i in 0..1000 {\n    v.push(i);\n}\n\n// Good: one allocation\nlet mut v = Vec::with_capacity(1000);\nfor i in 0..1000 {\n    v.push(i);\n}\n
\n\n

2.3. Avoid Cloning

\n\n

Cloning strings and large structures is an expensive operation. Use borrowing (&str instead of String):

\n\n
fn process(s: &str) { /* ... */ }\n\n// Bad: cloning\nlet s = String::from("hello");\nprocess(&s.clone());\n\n// Good: borrowing\nprocess(&s);\n
\n\n

3. Using Z

Blogs

Book Recommendations