Skip to content

Commit 16ba590

Browse files
auto commit
1 parent 5286619 commit 16ba590

File tree

19 files changed

+2597
-0
lines changed

19 files changed

+2597
-0
lines changed
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# DB-ESDK Performance Test Scenarios Configuration
2+
3+
# Data sizes to test (in bytes)
4+
# Categories are for organization only - code processes all sizes regardless of category
5+
data_sizes:
6+
small:
7+
- 1024 # 1KB
8+
- 5120 # 5KB
9+
- 10240 # 10KB
10+
medium:
11+
- 102400 # 100KB
12+
- 400000 # 400KB
13+
14+
# Quick test configuration (reduced test set for faster execution)
15+
quick_config:
16+
data_sizes:
17+
small:
18+
- 102400 # 100KB - within DynamoDB's 400KB limit
19+
iterations:
20+
warmup: 3 # Reduced warmup iterations
21+
measurement: 3 # Reduced measurement iterations
22+
concurrency_levels:
23+
- 1
24+
- 2
25+
test_types:
26+
- "throughput"
27+
- "memory"
28+
- "concurrency"
29+
30+
# Test iterations for statistical significance
31+
iterations:
32+
warmup: 5 # Warmup iterations (not counted)
33+
measurement: 10 # Measurement iterations
34+
35+
# Concurrency levels to test
36+
concurrency_levels:
37+
- 1
38+
- 2
39+
- 4
40+
- 8
41+
- 16
42+
43+
# DynamoDB table name
44+
table_name: "dbesdk-performance-testing"
45+
46+
# Keyring
47+
keyring: "raw-aes"
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
# ESDK Go Benchmark
2+
3+
Performance benchmark suite for the AWS Encryption SDK (ESDK) Go implementation.
4+
5+
## Quick Start
6+
7+
```bash
8+
# Run quick benchmark
9+
go run . --config ../../config/test-scenarios.yaml --quick
10+
11+
# Run full benchmark
12+
go run . --config ../../config/test-scenarios.yaml
13+
```
14+
15+
## Build
16+
17+
```bash
18+
# Build release binary
19+
go build -o esdk-benchmark .
20+
21+
# Run built binary
22+
./esdk-benchmark --quick
23+
```
24+
25+
## Configuration
26+
27+
The benchmark uses YAML configuration files. See `../../config/test-scenarios.yaml` for the full configuration format.
28+
29+
### Quick Mode
30+
31+
Quick mode runs a subset of tests with reduced iterations:
32+
33+
- Only runs test types specified in `quick_config.test_types`
34+
- Uses smaller data sizes from `quick_config.data_sizes.small`
35+
- Fewer iterations: `quick_config.iterations.measurement`
36+
37+
## Test Types
38+
39+
- **throughput**: Measures operations per second and latency
40+
- **memory**: Measures peak memory usage during operations
41+
- **concurrency**: Tests performance under concurrent load
42+
43+
## Output
44+
45+
Results are saved to JSON format in `../../results/raw-data/go_results.json` by default.
Lines changed: 172 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,172 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package benchmark
5+
6+
import (
7+
"bytes"
8+
"context"
9+
"fmt"
10+
"runtime/metrics"
11+
"strconv"
12+
"time"
13+
14+
"github.com/aws/aws-sdk-go-v2/aws"
15+
"github.com/aws/aws-sdk-go-v2/service/dynamodb"
16+
"github.com/aws/aws-sdk-go-v2/service/dynamodb/types"
17+
)
18+
19+
// === Helper Functions ===
20+
21+
// runBatchPutGetCycle performs a BatchWriteItem-BatchGetItem cycle with 25 items and measures performance
22+
func (b *DBESDKBenchmark) runBatchPutGetCycle(data []byte) (float64, float64, error) {
23+
ctx := context.Background()
24+
tableName := b.Config.TableName
25+
26+
// Create 25 write requests with same data, different sort_key
27+
var items []map[string]types.AttributeValue
28+
29+
for i := 0; i < 25; i++ {
30+
item := map[string]types.AttributeValue{
31+
"partition_key": &types.AttributeValueMemberS{Value: "benchmark-test"},
32+
"sort_key": &types.AttributeValueMemberN{Value: strconv.Itoa(i)},
33+
"attribute1": &types.AttributeValueMemberM{Value: map[string]types.AttributeValue{
34+
"data": &types.AttributeValueMemberB{Value: data},
35+
}},
36+
"attribute2": &types.AttributeValueMemberS{Value: "sign me!"},
37+
":attribute3": &types.AttributeValueMemberS{Value: "ignore me!"},
38+
}
39+
items = append(items, item)
40+
}
41+
42+
var writeRequests []types.WriteRequest
43+
for _, item := range items {
44+
writeRequests = append(writeRequests, types.WriteRequest{
45+
PutRequest: &types.PutRequest{Item: item},
46+
})
47+
}
48+
49+
// BatchWriteItem
50+
batchWriteStart := time.Now()
51+
_, err := b.DbesdkClient.BatchWriteItem(ctx, &dynamodb.BatchWriteItemInput{
52+
RequestItems: map[string][]types.WriteRequest{tableName: writeRequests},
53+
})
54+
if err != nil {
55+
return 0, 0, fmt.Errorf("BatchWriteItem failed: %w", err)
56+
}
57+
batchWriteDuration := time.Since(batchWriteStart).Seconds() * 1000
58+
59+
// Create 25 keys for BatchGetItem
60+
var keys []map[string]types.AttributeValue
61+
for i := 0; i < 25; i++ {
62+
keys = append(keys, map[string]types.AttributeValue{
63+
"partition_key": &types.AttributeValueMemberS{Value: "benchmark-test"},
64+
"sort_key": &types.AttributeValueMemberN{Value: strconv.Itoa(i)},
65+
})
66+
}
67+
68+
// BatchGetItem
69+
batchGetStart := time.Now()
70+
result, err := b.DbesdkClient.BatchGetItem(ctx, &dynamodb.BatchGetItemInput{
71+
RequestItems: map[string]types.KeysAndAttributes{
72+
tableName: {Keys: keys, ConsistentRead: aws.Bool(true)},
73+
},
74+
})
75+
if err != nil {
76+
return 0, 0, fmt.Errorf("BatchGetItem failed: %w", err)
77+
}
78+
batchGetDuration := time.Since(batchGetStart).Seconds() * 1000
79+
80+
// Verify 25 items retrieved with correct data size
81+
returnedItems := result.Responses[tableName]
82+
if len(returnedItems) != 25 {
83+
return 0, 0, fmt.Errorf("expected 25 items, got %d", len(returnedItems))
84+
}
85+
86+
// Verify each returned item
87+
for i, item := range returnedItems {
88+
if _, ok := item["attribute1"]; !ok {
89+
return 0, 0, fmt.Errorf("item %d missing attribute1", i)
90+
}
91+
92+
// Verify attribute1
93+
if attr1, ok := item["attribute1"].(*types.AttributeValueMemberM); ok {
94+
if dataAttr, ok := attr1.Value["data"].(*types.AttributeValueMemberB); ok {
95+
if !bytes.Equal(dataAttr.Value, data) {
96+
return 0, 0, fmt.Errorf("item %d data mismatch", i)
97+
}
98+
}
99+
}
100+
101+
// Verify attribute2 value
102+
if attr2, ok := item["attribute2"].(*types.AttributeValueMemberS); ok {
103+
if attr2.Value != "sign me!" {
104+
return 0, 0, fmt.Errorf("item %d attribute2 mismatch: got %s", i, attr2.Value)
105+
}
106+
} else {
107+
return 0, 0, fmt.Errorf("item %d attribute2 wrong type", i)
108+
}
109+
110+
// Verify :attribute3 value
111+
if attr3, ok := item[":attribute3"].(*types.AttributeValueMemberS); ok {
112+
if attr3.Value != "ignore me!" {
113+
return 0, 0, fmt.Errorf("item %d :attribute3 mismatch: got %s", i, attr3.Value)
114+
}
115+
} else {
116+
return 0, 0, fmt.Errorf("item %d :attribute3 wrong type", i)
117+
}
118+
}
119+
120+
return batchWriteDuration, batchGetDuration, nil
121+
}
122+
123+
// shouldRunTestType checks if a test type should be run based on quick config
124+
func (b *DBESDKBenchmark) shouldRunTestType(testType string) bool {
125+
if b.Config.QuickConfig == nil || len(b.Config.QuickConfig.TestTypes) == 0 {
126+
return true
127+
}
128+
129+
for _, allowedType := range b.Config.QuickConfig.TestTypes {
130+
if allowedType == testType {
131+
return true
132+
}
133+
}
134+
return false
135+
}
136+
137+
// === Memory Test Implementation ===
138+
139+
// sampleMemoryContinuously runs continuous memory sampling during operation
140+
func (b *DBESDKBenchmark) sampleMemoryContinuously(beforeHeap, beforeAllocs uint64, stopChan chan bool) []MemorySample {
141+
var samples []MemorySample
142+
ticker := time.NewTicker(SamplingIntervalMs * time.Millisecond)
143+
defer ticker.Stop()
144+
145+
for {
146+
select {
147+
case <-stopChan:
148+
return samples
149+
case <-ticker.C:
150+
var currentSamples [2]metrics.Sample
151+
currentSamples[0].Name = "/memory/classes/heap/objects:bytes"
152+
currentSamples[1].Name = "/gc/heap/allocs:bytes"
153+
metrics.Read(currentSamples[:])
154+
155+
var heapDelta, allocsDelta uint64
156+
if currentSamples[0].Value.Uint64() > beforeHeap {
157+
heapDelta = currentSamples[0].Value.Uint64() - beforeHeap
158+
}
159+
if currentSamples[1].Value.Uint64() > beforeAllocs {
160+
allocsDelta = currentSamples[1].Value.Uint64() - beforeAllocs
161+
}
162+
163+
sample := MemorySample{
164+
Timestamp: time.Now(),
165+
HeapMB: float64(heapDelta) / (1024 * 1024),
166+
MetricsAllocsMB: float64(allocsDelta) / (1024 * 1024),
167+
MemStatsAllocsMB: 0,
168+
}
169+
samples = append(samples, sample)
170+
}
171+
}
172+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
package benchmark
5+
6+
import (
7+
"fmt"
8+
"os"
9+
10+
"gopkg.in/yaml.v3"
11+
)
12+
13+
type KeyringType string
14+
15+
const (
16+
RawAESKeying KeyringType = "raw-aes"
17+
)
18+
19+
// TestConfig represents the configuration for benchmark tests
20+
type TestConfig struct {
21+
DataSizes struct {
22+
Small []int `yaml:"small"`
23+
Medium []int `yaml:"medium"`
24+
Large []int `yaml:"large"`
25+
} `yaml:"data_sizes"`
26+
Iterations struct {
27+
Warmup int `yaml:"warmup"`
28+
Measurement int `yaml:"measurement"`
29+
} `yaml:"iterations"`
30+
ConcurrencyLevels []int `yaml:"concurrency_levels"`
31+
QuickConfig *QuickConfig `yaml:"quick_config"`
32+
TableName string `yaml:"table_name"`
33+
Keyring KeyringType `yaml:"keyring"`
34+
}
35+
36+
// QuickConfig represents the quick test configuration
37+
type QuickConfig struct {
38+
DataSizes struct {
39+
Small []int `yaml:"small"`
40+
} `yaml:"data_sizes"`
41+
Iterations struct {
42+
Warmup int `yaml:"warmup"`
43+
Measurement int `yaml:"measurement"`
44+
} `yaml:"iterations"`
45+
ConcurrencyLevels []int `yaml:"concurrency_levels"`
46+
TestTypes []string `yaml:"test_types"`
47+
}
48+
49+
// LoadConfig loads the test configuration from YAML file
50+
func LoadConfig(configPath string) (TestConfig, error) {
51+
var config TestConfig
52+
53+
if _, err := os.Stat(configPath); os.IsNotExist(err) {
54+
return config, fmt.Errorf("config file not found: %s", configPath)
55+
}
56+
57+
data, err := os.ReadFile(configPath)
58+
if err != nil {
59+
return config, fmt.Errorf("failed to read config file: %w", err)
60+
}
61+
62+
if err := yaml.Unmarshal(data, &config); err != nil {
63+
return config, fmt.Errorf("failed to parse config file: %w", err)
64+
}
65+
66+
return config, nil
67+
}

0 commit comments

Comments
 (0)