# ARX.SDK on Amazon DynamoDB: benchmarks

ARX.SDK's DynamoDB provider against the hand-written low-level client code a developer would write (`PutItemRequest`,
`QueryRequest`, `TransactWriteItemsRequest`: the baseline) and the AWS SDK's object persistence model
(`DynamoDBContext`). BenchmarkDotNet, Release, on the `arx-dynamodb` container (DynamoDB Local 2.5.2, Docker Desktop on
Windows), tables `arx_bench_product`, `arx_bench_crm` (with GSI1) and `arx_bench_events` (write-sharded 4 ways). Reports
in `results/`. Ratios are against the raw client; numbers from one machine, so read the ratios, not the times.

| Operation | ARX against the raw client | Notes |
|---|---|---|
| Create, one item (a conditional put) | 1.00× | `DynamoDBContext`'s save (no condition: it overwrites) 1.01× |
| CreateMultiple, 25 and 100 items, one `TransactWriteItems` | 0.98×, 1.14× (spread ±0.13) | `BatchWriteItem` (not atomic): 1.28× and 3.99× |
| UpdateMultiple, 1, 25 and 100 items, conditional updates in one transaction | 0.97×, 0.97×, 1.01× | – |
| Update through an index (25 orders found by status, then updated by key) | 1.03× | against a Query of GSI1 and a transaction of conditional updates |
| Delete, 1 item; DeleteMultiple, 25 and 100 | 0.95×, 1.00×, 1.07× | before tuning 1.48×, 1.13×, 1.74× |
| Retrieve by key (`GetItem`) | 1.16× (spread ±0.10) | the same code read 0.90× to 1.39× in five runs; `DynamoDBContext` 1.01× to 1.48× |
| RetrieveMultiple, 100 and 1,000 items of one partition | 1.01×, 1.18× | the raw baseline only counts the items; `DynamoDBContext`, which builds objects, 0.94× and 1.16× |
| RetrieveMultiple through GSI1 (200 orders) | 0.98× | – |
| Scan of 1,000 items with a filter (`AllowScan`) | 1.03× | – |
| A write-sharded partition, 100 and 1,000 items over 4 shards, in order | 1.10×, 1.12× | against four parallel queries merged by hand; before tuning 2.26×, 1.88× |
| Create and Retrieve against the Document model (`Table.PutItemAsync` with the same condition, `Table.GetItemAsync`) | 1.01×, 1.11× | the Document model 1.05×, 1.15× (one run, 2026-11-09) |
| Retrieve by many keys, 25 and 100: ARX has no single call, so one RetrieveAsync per key in parallel | 1.42×, 0.69× against `BatchGetItem` | the raw client's parallel GetItem 1.88×, 0.56×; `DynamoDBContext` batch get 1.11×, 1.00×. On DynamoDB Local a `BatchGetItem` of 100 is slower than 100 parallel GetItems, which says nothing about AWS; allocations 8× to 10× (one request per key) |
| A move (`AllowKeyChange`): the consistent read, then a delete and a put in one transaction | 1.14× | against the same GetItem and TransactWriteItems by hand |

What the numbers say: writes are level with the raw client, deletes since the tuning; reads cost the dictionary
entity, level with the AWS SDK's own mapper. ARX allocates more: 1.1× to 1.5× the raw client on reads, 2× to 4× on
writes of 25 to 100 items (each item's key, condition and description kept for the error a failed transaction
reports). Single-item timings swing by ±25% between runs on this machine. DynamoDB Local is not DynamoDB: its
transactions take 65 to 90 ms whatever their size, so absolute times say nothing about AWS.

## Reports

| Class | Report |
|---|---|
| `CreateBenchmarks` | [results/…CreateBenchmarks-report-github.md](#report-createbenchmarks) |
| `CreateMultipleBenchmarks` | [results/…CreateMultipleBenchmarks-report-github.md](#report-createmultiplebenchmarks) |
| `UpdateMultipleBenchmarks` | [results/…UpdateMultipleBenchmarks-report-github.md](#report-updatemultiplebenchmarks) |
| `UpdateByIndexBenchmarks` | [results/…UpdateByIndexBenchmarks-report-github.md](#report-updatebyindexbenchmarks) |
| `DeleteMultipleBenchmarks` | [results/…DeleteMultipleBenchmarks-report-github.md](#report-deletemultiplebenchmarks) |
| `RetrieveBenchmarks` | [results/…RetrieveBenchmarks-report-github.md](#report-retrievebenchmarks) |
| `IndexAndScanBenchmarks` | [results/…IndexAndScanBenchmarks-report-github.md](#report-indexandscanbenchmarks) |
| `ShardedReadBenchmarks` | [results/…ShardedReadBenchmarks-report-github.md](#report-shardedreadbenchmarks) |
| `DocumentModelBenchmarks` | [results/…DocumentModelBenchmarks-report-github.md](#report-documentmodelbenchmarks) |
| `BatchGetBenchmarks` | [results/…BatchGetBenchmarks-report-github.md](#report-batchgetbenchmarks) |
| `MoveBenchmarks` | [results/…MoveBenchmarks-report-github.md](#report-movebenchmarks) |

## Not measured yet

Against AWS itself, nothing: these numbers are DynamoDB Local's. A read by many keys in one call has no ARX API yet (a
condition `id In (…)` gives no partition, ARX8001): the `BatchGetBenchmarks` row is what an application does today.

A caution about this machine: the client runs on Windows and reaches DynamoDB Local through Docker Desktop's port
forwarding, which stalls requests of more than about 8 KB by ~50 ms.
The small move transaction above takes 6 ms; the 65 to 90 ms quoted for larger transactions may be that stall rather
than DynamoDB Local. Not re-measured from inside the Docker network yet.
