Skip to content

Commit be554d2

Browse files
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/bdd-endpoint-perf.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
---
2+
applies_to:
3+
- aws-sdk-rust
4+
- client
5+
authors:
6+
- lnj
7+
references: []
8+
breaking: false
9+
new_feature: false
10+
bug_fix: false
11+
---
12+
13+
Optimized BDD endpoint resolution performance by replacing HashMap-based auth schemes with a typed `EndpointAuthScheme` struct, inlining the BDD evaluation loop, and adding a single-entry endpoint cache. The BDD resolver is now up to 49% faster than the original implementation and outperforms the tree-based resolver on most benchmarks.

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,9 @@ target/
6060

6161
# python
6262
__pycache__
63+
64+
65+
# AI ccontext
66+
.kiro/
67+
# Convenient naming convention I have been using to manage context between different workflows
68+
.kiro-*/

aws/rust-runtime/aws-inlineable/src/endpoint_auth.rs

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,12 @@ pub(crate) async fn resolve_endpoint_based_auth_scheme_options<'a>(
3737
// auth scheme IDs but not properties. This is because, at this stage, we're only determining which auth schemes will be candidates.
3838
// Any `authSchemes` list properties that influence the signing context will be extracted later
3939
// in `AuthSchemeEndpointConfig`, and passed by the orchestrator to the signer's `sign_http_request` method.
40-
if let Some(aws_smithy_types::Document::Array(endpoint_auth_schemes)) =
40+
let typed_schemes = endpoint.auth_schemes();
41+
if !typed_schemes.is_empty() {
42+
for scheme in typed_schemes {
43+
endpoint_auth_scheme_ids.push(AuthSchemeId::from(Cow::Owned(scheme.name().to_owned())));
44+
}
45+
} else if let Some(aws_smithy_types::Document::Array(endpoint_auth_schemes)) =
4146
endpoint.properties().get("authSchemes")
4247
{
4348
for endpoint_auth_scheme in endpoint_auth_schemes {

aws/rust-runtime/aws-inlineable/src/s3_express.rs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1093,6 +1093,13 @@ pub(crate) mod utils {
10931093
.load::<crate::config::endpoint::Endpoint>()
10941094
.expect("endpoint added to config bag by endpoint orchestrator");
10951095

1096+
let typed_schemes = endpoint.auth_schemes();
1097+
if !typed_schemes.is_empty() {
1098+
return typed_schemes
1099+
.iter()
1100+
.any(|scheme| scheme.name() == crate::s3_express::auth::SCHEME_ID.as_str());
1101+
}
1102+
10961103
let auth_schemes = match endpoint.properties().get("authSchemes") {
10971104
Some(Document::Array(schemes)) => schemes,
10981105
_ => return false,

aws/rust-runtime/aws-runtime/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "aws-runtime"
3-
version = "1.7.3"
3+
version = "1.7.4"
44
authors = ["AWS Rust SDK Team <aws-sdk-rust@amazon.com>"]
55
description = "Runtime support code for the AWS SDK. This crate isn't intended to be used directly."
66
edition = "2021"

aws/rust-runtime/aws-runtime/src/auth.rs

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -284,10 +284,7 @@ fn extract_field_from_endpoint_config<'a>(
284284
field_name: &'static str,
285285
endpoint_config: &'a AuthSchemeEndpointConfig<'_>,
286286
) -> Option<&'a Document> {
287-
endpoint_config
288-
.as_document()
289-
.and_then(Document::as_object)
290-
.and_then(|config| config.get(field_name))
287+
endpoint_config.get(field_name)
291288
}
292289

293290
fn apply_signing_instructions(

0 commit comments

Comments
 (0)