Skip to content

Conversation

@RanVaknin
Copy link
Contributor

Motivation and Context

The DynamoDB Enhanced Client v2 shows significant performance regression compared to v1 DynamoDB Mapper, with 32-98% slower operations across all operations. One theory was that lambda allocation overhead in ResolvedImmutableAttribute.attributeGetterMethod() is a key bottleneck affecting all operations in the serialization hot path.

The change refactors how attribute values are extracted during serialization by replacing an inline lambda expression with a method reference to a dedicated instance method. Profiling shows this reduces allocations of iterator and builder objects, with LinkedHashMap$LinkedEntryIterator allocations dropping 44% and DefaultDynamoDbExtensionContext$Builder allocations dropping 30%. The method reference approach appears to allow the JVM to optimize the call path more effectively, though the exact mechanism linking the code change to these specific allocation reductions is not fully clear from profiling data.

The optimization shows the strongest impact on small objects, with Get TINY operations improving 47% compared to 7% for Get HUGE, suggesting the allocation overhead represents a larger proportion of total processing time for smaller payloads.

Change

Before:

Function<T, AttributeValue> getAttributeValueWithTransform = item -> {
    R value = immutableAttribute.getter().apply(item);  // Lambda indirection
    return value == null ? nullAttributeValue() : attributeType.objectToAttributeValue(value);
};

After:

public Function<T, AttributeValue> attributeGetterMethod() {
    return this::getAttributeValue; 
}

AttributeValue getAttributeValue(T item) {
    R value = getter.apply(item);
    return value == null ? nullAttributeValue() : attributeType.objectToAttributeValue(value);
}

Results

running existing benchmarks for test/sdk-benchmarks/src/main/java/software/amazon/awssdk/benchmark/enhanced/dynamodb

Operation Size V1 (ops/s) Before Fix After Fix Fix Improvement Remaining Gap to V1
Delete TINY 12,149,315 10,699,554 11,865,284 +11% -2%
Delete SMALL 12,198,550 10,826,259 11,094,164 +2% -9%
Delete HUGE 12,247,132 10,704,169 11,379,428 +6% -7%
Get TINY 7,243,909 4,561,782 6,720,756 +47% -7%
Get SMALL 4,920,953 3,015,931 3,583,527 +19% -27%
Get HUGE 554,562 289,412 311,014 +7% -44%
Put TINY 6,824,135 4,672,933 5,870,566 +26% -14%
Put SMALL 4,296,701 2,605,126 2,807,175 +8% -35%
Put HUGE 582,056 225,242 223,333 -1% -62%
Update TINY 4,539,823 2,288,870 2,632,113 +15% -42%
Update SMALL 2,862,813 242,741 242,506 +0% -92%
Update HUGE 551,769 54,629 54,374 +0% -90%

JFR Profiling (Get TINY benchmark):

Allocation Impact:

Object Type Before After Change %
LinkedHashMap$LinkedEntryIterator 1,000 565 -435 -44%
DefaultDynamoDbExtensionContext$Builder 1,390 974 -416 -30%
DefaultDynamoDbExtensionContext 1,431 1,170 -261 -18%
EnhancedAttributeValue$InternalBuilder 871 678 -193 -22%
GetItemEnhancedResponse 320 170 -150 -47%
HashMap$Node[] 1,865 1,627 -238 -13%
LinkedHashMap 1,404 1,211 -193 -14%
HashMap 871 543 -328 -38%
EnhancedAttributeValue 664 836 +172 +26%
LinkedHashMap$Entry 590 466 -124 -21%

CPU Impact:

Metric Before After Change %
Total CPU Samples 2,128 2,037 -91 -4.3%

@RanVaknin RanVaknin requested a review from a team as a code owner November 26, 2025 19:50
@sonarqubecloud
Copy link

Quality Gate Failed Quality Gate failed

Failed conditions
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant