aws-sdk-java-v2 icon indicating copy to clipboard operation
aws-sdk-java-v2 copied to clipboard

Eliminate lambda indirection overhead in DynamoDB Enhanced Client

Open RanVaknin opened this issue 2 months ago • 1 comments

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 avatar Nov 26 '25 19:11 RanVaknin

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

sonarqubecloud[bot] avatar Nov 26 '25 20:11 sonarqubecloud[bot]