Commit 649ba85
authored
Optimize interceptor code paths (#4597)
## Motivation and Context
Addresses #4114
Every SDK operation invokes ~16 interceptor hooks via dyn dispatch. Most
interceptors only override 1–2 hooks, meaning the majority of calls
dispatch into no-op defaults. Additionally, every interceptor checks
`DisableInterceptor<T>` in the config bag on every invocation, even for
SDK-internal interceptors that are never disabled.
### Benchmark summary
The existing
[`previous-release-comparison`](https://github.com/smithy-lang/smithy-rs/tree/main/aws/sdk/benchmarks/previous-release-comparison)
benchmark crate (updated in this PR) was used to measure the impact of
each change. S3 `ListObjectsV2` with mock HTTP client (no network). The
S3 crate was generated locally with `endpointRuleSet` trait (not BDD)
for apples-to-apples comparison against the published release.
| Step | Commit | Description | Avg | Δ from prev step | Cumulative Δ |
|------|--------|-------------|-----|-------------------|--------------|
| Baseline | — | Previous release (1.129.0) | 66.64 µs | — | — |
| 1 |
[`7d8a725`](7d8a725)
| Skip disable check for permanent interceptors | 55.22 µs ±0.50 |
−11.42 µs (−17.1%) | −11.42 µs (−17.1%) |
| 2 |
[`cb490c0`](cb490c0)
| Skip dyn dispatch for unoverridden hooks | 53.46 µs ±0.52 | −1.76 µs
(−3.2%) | −13.18 µs (−19.8%) |
| 3 |
[`62a725b`](62a725b)
| Reuse client builder for merged RuntimeComponents | 50.79 µs ±0.12 |
−2.67 µs (−5.0%) | −15.85 µs (−23.8%) |
The description below breaks down each optimization in the order listed
above.
## Description
### Skip disable check for permanent interceptors (−17.1%)
Introduces `SharedInterceptor::permanent()`, which skips the
per-invocation `DisableInterceptor<T>` config bag lookup. SDK-internal
interceptors that are never disabled (content length enforcement,
metrics, token bucket, checksum, idempotency token, request compression,
feature trackers) are now registered via this path.
Interceptors that *can* be disabled — `InvocationIdInterceptor`,
`UserAgentInterceptor`, `RequestInfoInterceptor` (disabled during
presigning) — remain registered via `SharedInterceptor::new()`.
In debug builds, a `debug_assert!` fires if someone attempts to call
`disable_interceptor` on a permanent interceptor, catching the
misconfiguration early with a clear error message. This uses a
type-erased `fn(&ConfigBag) -> bool` pointer captured at construction
time (when the concrete type `T` is still known), gated behind
`#[cfg(debug_assertions)]` so there is zero cost in release builds.
### Skip dyn dispatch for unoverridden hooks (−3.2%)
Adds a new proc-macro crate `aws-smithy-runtime-api-macros` with a
`#[dyn_dispatch_hint]` attribute macro. Placed on an `impl Intercept`
block, it inspects which hook methods are overridden and auto-generates
an `overridden_hooks()` method returning the correct `OverriddenHooks`
bitmask. The interceptor dispatch loop checks this bitmask before
calling each hook, skipping dyn dispatch into no-op defaults.
A new crate was necessary because manually specifying bitflags at the
`SharedInterceptor` construction site would be error-prone and drift
over time. For example, without the macro, callers would need to write
something like:
```rust
SharedInterceptor::permanent(MyInterceptor::new())
.with_override_hint(OverriddenHooks::MODIFY_BEFORE_SIGNING | OverriddenHooks::MODIFY_BEFORE_TRANSMIT)
```
This creates a maintenance hazard: if someone adds or removes a hook
override in the `impl Intercept` block but forgets to update the
construction site, the flags silently go out of sync. The macro derives
the flags directly from the impl block, so they can never drift.
Interceptors not annotated with `#[dyn_dispatch_hint]` default to
`overridden_hooks() -> all()`, preserving existing behavior.
All internal interceptors are annotated with `#[dyn_dispatch_hint]`. The
trait method, `OverriddenHooks` type, and macro re-export are all
`#[doc(hidden)]`.
### Reuse client builder for merged RuntimeComponents (−5.0%)
Eliminates an extra `RuntimeComponents::builder()` + `merge_from` in
`apply_configuration` by merging the operation builder directly into the
existing client builder.
## Testing
- CI
- New integration tests in
`aws-smithy-runtime-api/tests/permanent_interceptor.rs`: permanent vs
disableable behavior, debug assertion on misuse
- New integration tests in
`aws-smithy-runtime-api/tests/dyn_dispatch_hint.rs`: macro generates
correct `OverriddenHooks` flags for single hook, multiple hooks, no
hooks, and without-macro cases
### Compile time impact
The new proc-macro crate (`aws-smithy-runtime-api-macros`) adds `syn`,
`quote`, and `proc-macro2` as dependencies. No measurable compile time
regression on `aws-sdk-s3` release builds (hyperfine, 10 runs with
`cargo clean` between each on `m7i.xlarge`):
| Branch | Mean | ±σ | Range | Instance |
|--------|------|----|-------|----------|
| `main` | 125.367s | ±2.538s | 123.2–132.0s | m7i.xlarge, Amazon Linux
2023 |
| This PR | 124.770s | ±1.216s | 122.3–127.0s | m7i.xlarge, Amazon Linux
2023 |1 parent 3b65879 commit 649ba85
69 files changed
Lines changed: 1467 additions & 615 deletions
File tree
- aws
- codegen-aws-sdk/src
- main/kotlin/software/amazon/smithy/rustsdk
- customize
- apigateway
- glacier
- route53
- s3
- test/kotlin/software/amazon/smithy/rustsdk
- rust-runtime
- aws-inlineable/src
- aws-runtime/src
- user_agent
- sdk/benchmarks/previous-release-comparison
- benches
- buildSrc/src/main/kotlin
- codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy
- customizations
- endpoint
- generators
- generators
- config
- codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/smithy
- rust-runtime
- aws-smithy-runtime-api-macros
- src
- aws-smithy-runtime-api
- src/client
- tests
- aws-smithy-runtime
- src/client
- http
- body
- retries/strategy
- inlineable/src
- tools/ci-build/sdk-lints/src
Some content is hidden
Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 3 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
253 | 253 | | |
254 | 254 | | |
255 | 255 | | |
| 256 | + | |
| 257 | + | |
256 | 258 | | |
257 | 259 | | |
258 | 260 | | |
259 | | - | |
| 261 | + | |
260 | 262 | | |
261 | 263 | | |
262 | 264 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
159 | 159 | | |
160 | 160 | | |
161 | 161 | | |
162 | | - | |
| 162 | + | |
163 | 163 | | |
164 | 164 | | |
165 | 165 | | |
| |||
Lines changed: 3 additions & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
41 | 41 | | |
42 | 42 | | |
43 | 43 | | |
| 44 | + | |
44 | 45 | | |
45 | 46 | | |
46 | 47 | | |
| |||
60 | 61 | | |
61 | 62 | | |
62 | 63 | | |
| 64 | + | |
63 | 65 | | |
64 | 66 | | |
65 | 67 | | |
| |||
84 | 86 | | |
85 | 87 | | |
86 | 88 | | |
87 | | - | |
| 89 | + | |
88 | 90 | | |
89 | 91 | | |
90 | 92 | | |
| |||
aws/codegen-aws-sdk/src/main/kotlin/software/amazon/smithy/rustsdk/HttpRequestChecksumDecorator.kt
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
174 | 174 | | |
175 | 175 | | |
176 | 176 | | |
177 | | - | |
| 177 | + | |
178 | 178 | | |
179 | 179 | | |
180 | 180 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
131 | 131 | | |
132 | 132 | | |
133 | 133 | | |
134 | | - | |
| 134 | + | |
135 | 135 | | |
136 | 136 | | |
137 | 137 | | |
| |||
aws/codegen-aws-sdk/src/main/kotlin/software/amazon/smithy/rustsdk/ObservabilityMetricDecorator.kt
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
37 | | - | |
| 37 | + | |
38 | 38 | | |
39 | 39 | | |
40 | 40 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
30 | 30 | | |
31 | 31 | | |
32 | 32 | | |
33 | | - | |
| 33 | + | |
34 | 34 | | |
35 | 35 | | |
36 | 36 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
33 | 33 | | |
34 | 34 | | |
35 | 35 | | |
36 | | - | |
| 36 | + | |
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
| |||
Lines changed: 1 addition & 1 deletion
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
32 | 32 | | |
33 | 33 | | |
34 | 34 | | |
35 | | - | |
| 35 | + | |
36 | 36 | | |
37 | 37 | | |
38 | 38 | | |
| |||
Lines changed: 3 additions & 3 deletions
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
89 | 89 | | |
90 | 90 | | |
91 | 91 | | |
92 | | - | |
| 92 | + | |
93 | 93 | | |
94 | 94 | | |
95 | 95 | | |
| |||
113 | 113 | | |
114 | 114 | | |
115 | 115 | | |
116 | | - | |
| 116 | + | |
117 | 117 | | |
118 | 118 | | |
119 | 119 | | |
| |||
122 | 122 | | |
123 | 123 | | |
124 | 124 | | |
125 | | - | |
| 125 | + | |
126 | 126 | | |
127 | 127 | | |
128 | 128 | | |
| |||
0 commit comments