Commit be554d2
authored
Performance Improvements for BDD endpoint resolution (#4595)
## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here -->
Benchmarks introduced in
#4579 revealed that our BDD
endpoint resolution showed a performance regression compared to the
older tree-based resolution. The initial tests used during BDD
development created an endpoint resolver for each test. Creating the
resolver for BDD is much faster since it uses a static partition map
instead of cloning the map. But this covered up the fact that the actual
resolution was much slower. This PR improves the performance of BDD
based endpoint resolution so it is now faster than tree based
resolution.
## Description
<!--- Describe your changes in detail -->
### 1. `EndpointAuthScheme` typed struct (~25-30% improvement)
Replaced `HashMap<String, Document>` auth scheme construction with a new
`EndpointAuthScheme` struct that uses a flat `Vec<(Cow<'static, str>,
Document)>` with linear-scan lookup. This eliminates:
- HashMap allocation, hashing (SipHash), and rehashing
- String key allocation for static keys like `"signingName"`,
`"signingRegion"`
- HashMap drop overhead
For 4 entries (the typical auth scheme size), linear scan is faster than
HashMap lookup.
**Files:** `aws-smithy-types/src/endpoint.rs`,
`aws-smithy-runtime-api/src/client/auth.rs`,
`aws-smithy-runtime/src/client/orchestrator/auth.rs`,
`aws-runtime/src/auth.rs`, `aws-inlineable/src/endpoint_auth.rs`,
`aws-inlineable/src/s3_express.rs`
### 2. Per-service inlined BDD loop (~5-6% improvement)
Replaced the generic `evaluate_bdd` function + `ConditionFn` enum +
closure-based condition evaluation with a per-service generated BDD
loop. Conditions and results are evaluated directly in the
`resolve_endpoint` function, eliminating:
- The `ConditionFn` enum and `CONDITIONS` array
- Closure call overhead for condition evaluation
- The `ResultEndpoint` enum and `RESULTS` array
- Intermediate result dispatch
**Files:** `EndpointBddGenerator.kt`, `bdd_interpreter.rs`
### 3. Fast-path trivial conditions (~2-3% improvement)
Trivial conditions (`isSet` on params, `booleanEquals` comparing params
to literals) are emitted as direct expressions instead of
closure-wrapped match arms. These are the most frequently evaluated
conditions since they're near the BDD root.
9 out of 76 S3 conditions are fast-pathed: `region.is_some()`,
`bucket.is_some()`, `endpoint.is_some()`, `(use_fips) == (&true)`, etc.
**Files:** `EndpointBddGenerator.kt`
### 4. `HashMap::with_capacity` for remaining records (~8-12%
improvement)
Pre-sized HashMap allocations with exact capacity for record literals,
eliminating rehashing during construction.
**Files:** `LiteralGenerator.kt`
### 5. Borrow instead of clone in result bindings (~5-8% improvement)
Changed result variable bindings from `.as_ref().map(|s|
s.clone()).expect(...)` to `.as_ref().expect(...)` and from
`.as_ref().map(|s| s.clone()).unwrap_or_default()` to
`.as_deref().unwrap_or_default()`. Values are only cloned when actually
needed (e.g., for HashMap insertion), not eagerly at binding time.
**Files:** `EndpointBddGenerator.kt`
### 6. Borrowed string literals for auth scheme values (~1-2%
improvement)
Used `Ownership::Borrowed` expression generator for auth scheme property
values, avoiding unnecessary `.to_string()` on static string literals
that are passed to `Into<Document>`.
**Files:** `EndpointBddGenerator.kt`
### 7. Optimized `is_valid_host_label` (~5-8% improvement)
Replaced Unicode-aware `chars().enumerate().all()` with byte-level ASCII
validation. DNS host labels are ASCII-only (RFC 952/1123), so:
- `b.is_ascii_alphanumeric()` instead of `ch.is_alphanumeric()` (avoids
Unicode tables)
- Direct byte iteration instead of UTF-8 decoding via `chars()`
- Manual dot-splitting instead of `str::split('.')` iterator
Added test coverage for non-ASCII and emoji inputs to document the
correct rejection behavior.
**Files:** `inlineable/src/endpoint_lib/host.rs`
### 8. Single-entry endpoint cache (~75-85% improvement for repeated
params)
Added a `RwLock<Option<(Params, Endpoint)>>` single-entry cache to
`DefaultResolver`. On cache hit (same params as last call), returns a
clone of the cached endpoint without re-evaluating the BDD. This is safe
because endpoint resolution is a pure function of params + static
partition data.
The caching logic is on the `ResolveEndpoint` trait impl, so unit tests
that call the inherent `resolve_endpoint` method bypass the cache and
test actual resolution logic.
**Files:** `EndpointBddGenerator.kt`
## Benchmark Results
All benchmarks run on the S3 endpoint resolver with 10,000 iterations
per scenario using the endpoint benchmarks introduced in
#4579.
### Uncached (algorithmic improvements only)
```
Benchmark Tree (ns) BDD orig (ns) BDD opt (ns) vs Tree vs Orig
-------------------------------------------------------------------------------------
s3_outposts 2083 3207 1685 -19% -47%
s3_accesspoint 1334 2083 1230 -8% -41%
s3express 1150 2227 1271 +11% -43%
s3_path_style 978 1572 827 -15% -47%
s3_virtual_addressing 1018 1517 780 -23% -49%
```
The BDD resolver now beats the tree-based resolver on 4 out of 5
benchmarks. Only `s3express` remains slower (+11%), which has the most
complex condition chain.
### Cached (repeated identical params)
```
Benchmark BDD opt (ns) Cached (ns) Improvement
---------------------------------------------------------------
s3_outposts 1685 432 -74%
s3_accesspoint 1230 260 -79%
s3express 1271 324 -74%
s3_path_style 827 259 -69%
s3_virtual_addressing 780 259 -67%
```
## Testing
- 433 S3 endpoint tests pass (BDD resolver)
- 367 DynamoDB, 77 Lambda, 75 STS, 46 SSO, 46 SSOOIDC endpoint tests
pass (tree-based resolvers)
- 1,044 total endpoint tests across all service crates
- New non-ASCII and emoji test cases for `is_valid_host_label`
## Backward Compatibility
- `Endpoint` struct gains a new `auth_schemes` field alongside existing
`properties` — additive, non-breaking
- `AuthSchemeEndpointConfig` now supports both typed
`EndpointAuthScheme` and `Document` variants
- All consumers check typed `auth_schemes()` first, falling back to
`properties["authSchemes"]` for tree-based resolvers
- Test generator conditionally uses typed auth schemes only for
BDD-based services
## Checklist
<!--- If a checkbox below is not applicable, then please DELETE it
rather than leaving it unchecked -->
- [x] For changes to the smithy-rs codegen or runtime crates, I have
created a changelog entry Markdown file in the `.changelog` directory,
specifying "client," "server," or both in the `applies_to` key.
- [x] For changes to the AWS SDK, generated SDK code, or SDK runtime
crates, I have created a changelog entry Markdown file in the
`.changelog` directory, specifying "aws-sdk-rust" in the `applies_to`
key.
----
_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._1 parent 062b5f9 commit be554d2
25 files changed
Lines changed: 936 additions & 175 deletions
File tree
- .changelog
- aws
- rust-runtime
- aws-inlineable/src
- aws-runtime
- src
- sdk/benchmarks/standardized-benches
- src
- bin
- codegen-client/src/main/kotlin/software/amazon/smithy/rust/codegen/client/smithy/endpoint
- generators
- rulesgen
- codegen-core/src/main/kotlin/software/amazon/smithy/rust/codegen/core/rustlang
- rust-runtime
- aws-smithy-runtime-api
- src/client
- aws-smithy-runtime
- src/client/orchestrator
- aws-smithy-types
- src
- inlineable/src/endpoint_lib
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
| 1 | + | |
| 2 | + | |
| 3 | + | |
| 4 | + | |
| 5 | + | |
| 6 | + | |
| 7 | + | |
| 8 | + | |
| 9 | + | |
| 10 | + | |
| 11 | + | |
| 12 | + | |
| 13 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
60 | 60 | | |
61 | 61 | | |
62 | 62 | | |
| 63 | + | |
| 64 | + | |
| 65 | + | |
| 66 | + | |
| 67 | + | |
| 68 | + | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
37 | 37 | | |
38 | 38 | | |
39 | 39 | | |
40 | | - | |
| 40 | + | |
| 41 | + | |
| 42 | + | |
| 43 | + | |
| 44 | + | |
| 45 | + | |
41 | 46 | | |
42 | 47 | | |
43 | 48 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1093 | 1093 | | |
1094 | 1094 | | |
1095 | 1095 | | |
| 1096 | + | |
| 1097 | + | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
1096 | 1103 | | |
1097 | 1104 | | |
1098 | 1105 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
1 | 1 | | |
2 | 2 | | |
3 | | - | |
| 3 | + | |
4 | 4 | | |
5 | 5 | | |
6 | 6 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
284 | 284 | | |
285 | 285 | | |
286 | 286 | | |
287 | | - | |
288 | | - | |
289 | | - | |
290 | | - | |
| 287 | + | |
291 | 288 | | |
292 | 289 | | |
293 | 290 | | |
| |||
0 commit comments