Rust on AWS Lambda: jitter-entropy & opt-level
Two Rust-specific build settings (opt-level and the aws-lc-rs jitter-entropy flag), measured per memory tier.
The aws-lc-rs jitter-entropy cold-start tax
aws-lc-rs (the default crypto backend for rustls and the AWS SDK for Rust) collects CPU jitter entropy once per process, on the first TLS handshake. That is the same one-time CPU work whichever scenario triggers it, but because the Lambda Init phase appears to run on boosted CPU while the Invoke phase runs on the configured tier's fraction, the wall-clock cost depends on which phase the first TLS call lands in. On the two scenarios below that produces two different shapes:
oneclient(1 AWS client, DDB), first TLS in the Invoke phase (the cliff). The SDK client is built in the Init phase, but the first TLS handshake the SDK makes is the per-invoke DDBGetItemcall, in the Invoke phase under the configured tier's fractional vCPU. So the jitter tax lands in the cold invoke's reportedDurationand the on-vs-off gap grows steeply as memory shrinks.lettercount(letter count, CPU), first TLS in the Init phase (the flat bump). The handler does ans3.get_object().send()in the Init phase, where the measured init bump is consistent with Init-phase code running on more CPU than the configured tier alone would provide. The same tax lands inInit Durationand stays roughly flat across tiers.
The chart shows the measured consequence (cliff vs flat bump). Which phase each .send() runs in is fixed by the handler shape (the two bullets above), and the measured phase deltas are consistent with the first TLS work landing in the Invoke phase for oneclient and the Init phase for lettercount; the Init-phase CPU boost behind the two shapes is inferred, an observed behavior AWS does not formally document. The benchmark records the REPORT line, not TLS timing or CPU allocation. Cold Start Anatomy covers the mechanism, the source, the caveats, and why the same effect shapes cold starts across runtimes, not just this Rust A/B.
Why setting AWS_LC_SYS_NO_JITTER_ENTROPY=1 is a trade-off, not a default
AWS-LC seeds its RNG by hedging across two entropy sources as a defense-in-depth measure, with CPU jitter as the default seed root (see entropy_sources.c in aws-lc, the C library that the aws-lc-sys crate vendors). With AWS_LC_SYS_NO_JITTER_ENTROPY=1 set, AWS-LC seeds from the OS instead and uses the CPU's hardware RNG (RDRAND / RNDR) as the second source; on a CPU without a hardware RNG it falls back to the OS source for the second slot too, so the two sources are no longer independent. AWS-LC's own build option warns that with jitter disabled "randomness generation might not use two independent entropy sources," so it should be evaluated per workload. AWS's own Rust SDK team documents this flag as the cold-start mitigation for Lambda and frames it as a trade-off, not a blanket recommendation to disable (smithy-rs announcement). Every other Rust chart on this site is built with the flag set, so its latency numbers reflect the optimized configuration; this page isolates what setting the flag actually changes on Lambda.
AWS-LC auto-opts-out inside a snapshot-restore environment. A runtime check detects a VM uniqueness-breaking event (UBE), the resume of a snapshotted/cloned VM, and switches to the same OS + RDRAND configuration on its own (entropy_sources.c). Plain Lambda does not signal a VM UBE, so this does not fire for a plain Rust function; setting the build flag is the manual equivalent.
Two ways to avoid the Invoke-phase cliff for a handler shaped like oneclient:
- Force a TLS handshake at init. Constructing the SDK client is not enough, since
aws-lc-rsis lazy; a small real call on the client the handler uses (for the DynamoDB-backedoneclient, a cheap call such aslist_tables, though this variant is illustrative and not one of the measured builds) is required. This is cheap under the Init-phase CPU behavior noted above; if that behavior changes, the cost moves to the first request. - Build with
AWS_LC_SYS_NO_JITTER_ENTROPY=1. Removes the cost outright (in either phase) but drops one of AWS-LC's defense-in-depth entropy sources, so it is a workload-specific decision.
Opt-level: speed vs size
opt-level=3 optimizes for runtime speed, usually at the cost of a bigger binary; opt-level=z optimizes for size, which can load faster. Neither outcome is guaranteed: rustc's optimizer is not fully predictable, so z is not always the smaller binary and 3 is not always the faster code (cargo profiles reference). Which wins for cold start is scenario-dependent, so both are measured.
One scope caveat: a smaller artifact also downloads and unpacks faster, but that phase runs before the Init phase and no REPORT metric isolates it (it is only visible from the caller's wall-clock, which Cold Start Anatomy measures separately, and where the download term is small below a few MB). So this A/B captures the loaded-code trade-off (init link/load + warm execution speed), not total cold start including download. The chart below plots cold init P50 for exactly that reason.