Summary
ExecutionAttributes wraps a new HashMap<>() with default initial capacity (16 buckets). By the time a DynamoDB request finishes executing through all pipeline stages and interceptors, approximately 30–40 attributes have been inserted. This causes 2–3 resize+rehash cycles on every single request, which shows up as measurable CPU overhead in production profiling.
Evidence
Using async-profiler on a JanusGraph server with a DynamoDB-heavy read workload (Query-dominated, ~thousands of DynamoDB calls/sec, SDK version 2.42.28):
AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext: 6.44% cumulative CPU of total JVM CPU
HashMap.resize() appears in the hot path within that subtree
ExecutionAttributes.getAttribute: 1.62% cumulative CPU — repeated map lookups against a recently-resized table
Relevant code
ExecutionAttributes.java constructs new HashMap<>() with no initial capacity hint.
Proposed fix
Pre-size the HashMap to avoid resizes. Based on the number of attributes inserted during a typical DynamoDB request (~30–40), an initial capacity of 64 would prevent all resize cycles:
// Before
private final Map<ExecutionAttribute<?>, Object> attributes = new HashMap<>();
// After
private final Map<ExecutionAttribute<?>, Object> attributes = new HashMap<>(64);
Alternatively, a custom open-addressing map keyed on ExecutionAttribute identity (they are singletons) could be even more efficient.
Impact
This is a zero-risk, one-line change that eliminates repeated memory allocation and rehashing on every SDK request. On high-throughput DynamoDB workloads the savings are proportional to request rate.
Summary
ExecutionAttributeswraps anew HashMap<>()with default initial capacity (16 buckets). By the time a DynamoDB request finishes executing through all pipeline stages and interceptors, approximately 30–40 attributes have been inserted. This causes 2–3 resize+rehash cycles on every single request, which shows up as measurable CPU overhead in production profiling.Evidence
Using async-profiler on a JanusGraph server with a DynamoDB-heavy read workload (Query-dominated, ~thousands of DynamoDB calls/sec, SDK version 2.42.28):
AwsExecutionContextBuilder.invokeInterceptorsAndCreateExecutionContext: 6.44% cumulative CPU of total JVM CPUHashMap.resize()appears in the hot path within that subtreeExecutionAttributes.getAttribute: 1.62% cumulative CPU — repeated map lookups against a recently-resized tableRelevant code
ExecutionAttributes.javaconstructsnew HashMap<>()with no initial capacity hint.Proposed fix
Pre-size the HashMap to avoid resizes. Based on the number of attributes inserted during a typical DynamoDB request (~30–40), an initial capacity of 64 would prevent all resize cycles:
Alternatively, a custom open-addressing map keyed on
ExecutionAttributeidentity (they are singletons) could be even more efficient.Impact
This is a zero-risk, one-line change that eliminates repeated memory allocation and rehashing on every SDK request. On high-throughput DynamoDB workloads the savings are proportional to request rate.