We can't find the internet
Attempting to reconnect
Something went wrong!
Attempting to reconnect
← Performance and Data Layout step 3 of 20
Where the allocations are: the counting allocator
Measure allocator calls for four ways of building vectors and strings. Fix the starter so each construction gets its own measurement, rather than including the counts from earlier constructions.
pub fn alloc_report(n: usize) -> Vec<usize>
What the counter measures
The harness supplies alloc_reset() and alloc_count(). Its global allocator
increments one counter for each call to alloc, alloc_zeroed or realloc, then
delegates the request to the system allocator. Deallocation does not increment
or decrement the counter.
This counts allocation requests, including reallocations. It does not count bytes, live allocations, elapsed time or physical memory copies. A realloc call counts even when the allocator can grow the block at the same address.
The counter is shared by the whole process. Resetting it excludes earlier work such as parsing input, but does not isolate you from unrelated work that happens during a measurement. Keep logging, report allocation and other setup outside the measured regions. Do not spawn background work.
Four separate measurements
Keep the supplied PARTS array and these four constructions unchanged. Each uses the same n. Return their counts in this order:
| Index | Construction |
|---|---|
| 0 | Push n u64 values into a Vec that starts with no capacity. |
| 1 | Push the same values into a Vec created with capacity n. |
| 2 | Rebuild a String with format on every iteration. |
| 3 | Append the same parts into a String created with capacity n * 5. |
The vector values are 0..n, converted to u64. Both string loops append
PARTS[i % 5] for each i in 0..n, where PARTS is
["alpha", "beta", "gamma", "delta", "eps"].
Create the four-entry report buffer before the first reset. For each
construction, reset immediately before it, read the count immediately after
it, then store that count. Keep the supplied black_box(&value) calls so
the completed values are exposed to an optimization barrier.
An empty construction with n = 0 produces zero in each slot in the current fixtures. The report's own storage is outside those measurements. Inputs must be small enough for n * 5 and the requested buffers to fit; allocation failure and arithmetic overflow are outside this exercise's input domain.
Reading the results
The current fixtures expect [9, 1, 1999, 1] for n = 1000. These counts were
reproduced with Rust 1.98.1 in debug and optimized builds. They describe this
implementation and setup, not a portable allocation guarantee from Rust.
- The growing Vec makes an initial request, then additional requests as its capacity fills. In that measured implementation, the u64 vector starts with capacity four and doubles until it reaches 1024. Rust guarantees amortized constant-time push, not that exact growth sequence.
- The preallocated Vec has enough capacity for all n pushes. It avoids growth requests during this loop; the values still have to be written.
format!("{}{}", joined, part)creates a new String containing the old contents followed by the next part. Repeatedly rebuilding an ever-longer prefix does quadratic total copying work for these fixed-size parts. The observed request count also depends on formatting's capacity choices.- The preallocated String has enough space because every part is at most
five bytes.
push_strappends without rebuilding the previous prefix. Both string constructions produce 4400 bytes when n is 1000.
Fewer allocator calls do not translate directly into the same factor of speedup. Counts help explain one source of work; timings and profiling answer different questions.
Measurement limits
Not every Box, Vec or String construction makes a heap allocation. Empty buffers, zero-sized values, existing capacity and compiler optimizations can avoid allocation. A global allocator also does not observe memory managed directly by a separate allocator or foreign library.
black_box is a best-effort optimization barrier, not a guarantee that every
source-level allocation occurs. Compiler, standard-library and target changes
can change the observed counts. This exercise currently compares exact
counts, so it is sensitive to those changes. Measure the supplied operations;
do not replace them with formulas that merely reproduce the fixtures.
The starter resets only once, so later entries accumulate earlier counts. Fix that measurement boundary while preserving the four constructions. Submissions must compile, pass the output checks and pass Clippy. The output checks cannot prove that an implementation actually performed the measurements.
Your code, saved here
Sign in to access your accepted solutions and saved draft history. You can keep reading the lesson without an account.
Sign in to your accountStuck?
Rust reference solution
Sign in to track your attempts toward unlocking the reference solution.