Skip to content

Commit 0b540a0

Browse files
author
Shubham Chaturvedi
committed
PerfTest: rust
1 parent 678ca04 commit 0b540a0

File tree

10 files changed

+4618
-0
lines changed

10 files changed

+4618
-0
lines changed

esdk-performance-testing/benchmarks/rust/Cargo.lock

Lines changed: 3469 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
[package]
2+
name = "esdk-benchmark"
3+
version = "0.1.0"
4+
edition = "2021"
5+
6+
[[bin]]
7+
name = "esdk_benchmark"
8+
path = "src/main.rs"
9+
10+
[dependencies]
11+
# AWS Encryption SDK
12+
aws-esdk = { path = "../../../AwsEncryptionSDK/runtimes/rust" }
13+
14+
# Async runtime
15+
tokio = { version = "1.0", features = ["full"] }
16+
17+
# Serialization
18+
serde = { version = "1.0", features = ["derive"] }
19+
serde_json = "1.0"
20+
serde_yaml = "0.9"
21+
22+
# CLI and progress
23+
clap = { version = "4.0", features = ["derive"] }
24+
indicatif = "0.17"
25+
26+
# Statistics and benchmarking
27+
hdrhistogram = "7.5"
28+
rand = "0.8"
29+
30+
# System info
31+
sysinfo = "0.30"
32+
33+
# Error handling
34+
anyhow = "1.0"
35+
thiserror = "1.0"
36+
37+
# Logging
38+
log = "0.4"
39+
env_logger = "0.10"
40+
41+
# Time
42+
chrono = { version = "0.4", features = ["serde"] }
43+
44+
# Memory profiling
45+
memory-stats = "1.0"
46+
stats_alloc = "0.1"
47+
48+
# Async utilities
49+
futures = "0.3"
50+
51+
# Override AWS SDK versions to be compatible with Rust 1.85.0
52+
aws-config = "1.8.6"
53+
aws-sdk-dynamodb = "1.73.0"
54+
aws-sdk-kms = "1.67.0"
55+
aws-sdk-sso = "1.69.0"
56+
aws-sdk-ssooidc = "1.69.0"
57+
aws-sdk-sts = "1.69.0"
58+
aws-smithy-types = "1.2"
59+
60+
[dev-dependencies]
61+
criterion = { version = "0.5", features = ["html_reports"] }
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
# ESDK Rust Performance Benchmark
2+
3+
Rust implementation of the AWS Encryption SDK performance benchmark suite.
4+
5+
## Features
6+
7+
- **Throughput**: Operations per second and latency analysis (P50, P95, P99)
8+
- **Memory**: Peak memory consumption monitoring
9+
- **Concurrency**: Multi-threaded performance testing
10+
- **Raw AES Keyring**: Local 256-bit AES keys (no KMS dependency)
11+
12+
## Prerequisites
13+
14+
- Rust 1.70.0+ ([rustup.rs](https://rustup.rs/))
15+
- Build tools (Xcode CLI Tools on macOS, build-essential on Linux)
16+
17+
## Quick Start
18+
19+
```bash
20+
# Build and run
21+
cargo build --release
22+
./target/release/esdk_benchmark --config ../../config/test-scenarios.yaml
23+
24+
# Quick test (requires quick_config in YAML)
25+
./target/release/esdk_benchmark --quick
26+
```
27+
28+
## Configuration
29+
30+
### Command Line
31+
- `--config`: Path to YAML config file (required)
32+
- `--output`: Results output path (default: `../../results/raw-data/rust_results.json`)
33+
- `--quick`: Quick test mode (requires `quick_config` section in YAML)
34+
35+
## Logging
36+
37+
Default: info level. Override with `RUST_LOG`:
38+
```bash
39+
RUST_LOG=debug ./target/release/esdk_benchmark --config config.yaml
40+
```
41+
42+
## Development
43+
44+
```bash
45+
# Format and lint
46+
cargo fmt
47+
cargo clippy -- -D warnings
48+
49+
# Test
50+
cargo test
51+
52+
# Debug build
53+
cargo build
54+
RUST_LOG=debug ./target/debug/esdk_benchmark --config config.yaml
55+
```
56+
57+
## Troubleshooting
58+
59+
- **Build issues**: `rustup update && cargo clean && cargo build --release`
60+
- **Config issues**: Validate YAML syntax and check file permissions
61+
- **Memory issues**: Monitor with `htop` or Activity Monitor
62+
63+
## License
64+
65+
Apache License 2.0
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use anyhow::{Context, Result};
5+
use aws_esdk::client as esdk_client;
6+
use aws_esdk::material_providers::client as mpl_client;
7+
use aws_esdk::material_providers::types::material_providers_config::MaterialProvidersConfig;
8+
use aws_esdk::material_providers::types::AesWrappingAlg;
9+
use aws_esdk::material_providers::types::EsdkCommitmentPolicy;
10+
use aws_esdk::types::aws_encryption_sdk_config::AwsEncryptionSdkConfig;
11+
use log::info;
12+
use rand::Rng;
13+
use sysinfo::System;
14+
15+
use crate::config::{load_config, TestConfig};
16+
use crate::results::BenchmarkResult;
17+
18+
// Constants for memory testing
19+
pub const MEMORY_TEST_ITERATIONS: usize = 5;
20+
21+
pub struct EsdkBenchmark {
22+
pub esdk_client: esdk_client::Client,
23+
pub raw_keyring: aws_esdk::material_providers::types::keyring::KeyringRef,
24+
pub config: TestConfig,
25+
pub results: Vec<BenchmarkResult>,
26+
pub cpu_count: usize,
27+
pub total_memory_gb: f64,
28+
}
29+
30+
impl EsdkBenchmark {
31+
pub async fn new(config_path: &str) -> Result<Self> {
32+
// Get system info
33+
let mut system = System::new_all();
34+
system.refresh_all();
35+
let cpu_count = system.cpus().len();
36+
let total_memory_gb = system.total_memory() as f64 / (1024.0 * 1024.0 * 1024.0);
37+
38+
// Load configuration
39+
let config = load_config(config_path)?;
40+
41+
// Setup ESDK
42+
let (esdk_client, raw_keyring) = Self::setup_esdk().await?;
43+
44+
info!(
45+
"Initialized ESDK Benchmark - CPU cores: {}, Memory: {:.1}GB",
46+
cpu_count, total_memory_gb
47+
);
48+
49+
Ok(Self {
50+
esdk_client,
51+
raw_keyring,
52+
config,
53+
results: Vec::new(),
54+
cpu_count,
55+
total_memory_gb,
56+
})
57+
}
58+
59+
async fn setup_esdk() -> Result<(
60+
esdk_client::Client,
61+
aws_esdk::material_providers::types::keyring::KeyringRef,
62+
)> {
63+
// Initialize ESDK client with commitment policy
64+
let esdk_config = AwsEncryptionSdkConfig::builder()
65+
.commitment_policy(EsdkCommitmentPolicy::RequireEncryptRequireDecrypt)
66+
.build()?;
67+
let esdk_client = esdk_client::Client::from_conf(esdk_config)?;
68+
69+
// Initialize material providers client
70+
let mpl_config = MaterialProvidersConfig::builder().build()?;
71+
let mpl_client = mpl_client::Client::from_conf(mpl_config)?;
72+
73+
// Create default AES-256 keyring
74+
let mut key = [0u8; 32]; // 256-bit key
75+
rand::thread_rng().fill(&mut key);
76+
77+
let raw_keyring = mpl_client
78+
.create_raw_aes_keyring()
79+
.key_name("test-aes-256-key")
80+
.key_namespace("esdk-performance-test")
81+
.wrapping_key(key.to_vec())
82+
.wrapping_alg(AesWrappingAlg::AlgAes256GcmIv12Tag16)
83+
.send()
84+
.await
85+
.context("Failed to create keyring")?;
86+
87+
info!("ESDK client initialized successfully");
88+
Ok((esdk_client, raw_keyring))
89+
}
90+
91+
pub fn save_results(&self, output_path: &str) -> Result<()> {
92+
crate::results::save_results(
93+
&self.results,
94+
output_path,
95+
self.cpu_count,
96+
self.total_memory_gb,
97+
)
98+
}
99+
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
use anyhow::{Context, Result};
5+
use serde::{Deserialize, Serialize};
6+
use std::fs;
7+
8+
#[derive(Debug, Clone, Serialize, Deserialize)]
9+
pub struct TestConfig {
10+
pub data_sizes: DataSizes,
11+
pub iterations: IterationConfig,
12+
pub concurrency_levels: Vec<usize>,
13+
pub quick_config: Option<QuickConfig>,
14+
}
15+
16+
#[derive(Debug, Clone, Serialize, Deserialize)]
17+
pub struct DataSizes {
18+
pub small: Vec<usize>,
19+
pub medium: Vec<usize>,
20+
pub large: Vec<usize>,
21+
}
22+
23+
#[derive(Debug, Clone, Serialize, Deserialize)]
24+
pub struct IterationConfig {
25+
pub warmup: usize,
26+
pub measurement: usize,
27+
}
28+
29+
#[derive(Debug, Clone, Serialize, Deserialize)]
30+
pub struct QuickConfig {
31+
pub data_sizes: QuickDataSizes,
32+
pub iterations: QuickIterationConfig,
33+
pub concurrency_levels: Vec<usize>,
34+
pub test_types: Vec<String>,
35+
}
36+
37+
#[derive(Debug, Clone, Serialize, Deserialize)]
38+
pub struct QuickDataSizes {
39+
pub small: Vec<usize>,
40+
}
41+
42+
#[derive(Debug, Clone, Serialize, Deserialize)]
43+
pub struct QuickIterationConfig {
44+
pub warmup: usize,
45+
pub measurement: usize,
46+
}
47+
48+
pub fn load_config(config_path: &str) -> Result<TestConfig> {
49+
if !std::path::Path::new(config_path).exists() {
50+
return Err(anyhow::anyhow!("Config file not found: {}", config_path));
51+
}
52+
53+
let config_content = fs::read_to_string(config_path)
54+
.with_context(|| format!("Failed to read config file: {}", config_path))?;
55+
56+
let config: TestConfig =
57+
serde_yaml::from_str(&config_content).with_context(|| "Failed to parse config file")?;
58+
59+
Ok(config)
60+
}
Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
// Copyright Amazon.com Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
4+
mod benchmark;
5+
mod config;
6+
mod results;
7+
mod tests;
8+
9+
use anyhow::Result;
10+
use clap::{Arg, Command};
11+
12+
use benchmark::EsdkBenchmark;
13+
14+
#[tokio::main]
15+
async fn main() -> Result<()> {
16+
env_logger::Builder::from_default_env()
17+
.filter_level(log::LevelFilter::Info)
18+
.init();
19+
20+
let matches = Command::new("ESDK Rust Benchmark")
21+
.version("1.0")
22+
.about("AWS Encryption SDK Performance Benchmark Suite - Rust Implementation")
23+
.arg(
24+
Arg::new("config")
25+
.long("config")
26+
.value_name("FILE")
27+
.help("Path to test configuration file")
28+
.default_value("../../config/test-scenarios.yaml"),
29+
)
30+
.arg(
31+
Arg::new("output")
32+
.long("output")
33+
.value_name("FILE")
34+
.help("Path to output results file")
35+
.default_value("../../results/raw-data/rust_results.json"),
36+
)
37+
.arg(
38+
Arg::new("quick")
39+
.long("quick")
40+
.help("Run quick test with reduced iterations")
41+
.action(clap::ArgAction::SetTrue),
42+
)
43+
.get_matches();
44+
45+
let config_path = matches.get_one::<String>("config").unwrap();
46+
let output_path = matches.get_one::<String>("output").unwrap();
47+
let quick = matches.get_flag("quick");
48+
49+
// Initialize benchmark
50+
let mut bench = EsdkBenchmark::new(config_path).await?;
51+
52+
// Adjust config for quick test
53+
if quick {
54+
let quick_config = bench.config.quick_config.as_ref()
55+
.ok_or_else(|| anyhow::anyhow!("Quick mode requested but no quick_config found in config file"))?;
56+
57+
bench.config.iterations.measurement = quick_config.iterations.measurement;
58+
bench.config.iterations.warmup = quick_config.iterations.warmup;
59+
bench.config.data_sizes.small = quick_config.data_sizes.small.clone();
60+
bench.config.data_sizes.medium = Vec::new();
61+
bench.config.data_sizes.large = Vec::new();
62+
bench.config.concurrency_levels = quick_config.concurrency_levels.clone();
63+
}
64+
65+
// Run benchmarks
66+
bench.run_all_benchmarks().await?;
67+
68+
// Save results
69+
bench.save_results(output_path)?;
70+
71+
// Print summary
72+
println!("\n=== ESDK Rust Benchmark Summary ===");
73+
println!("Total tests completed: {}", bench.results.len());
74+
println!("Results saved to: {}", output_path);
75+
76+
if !bench.results.is_empty() {
77+
let mut max_throughput = 0.0;
78+
for result in &bench.results {
79+
if result.test_name == "throughput" && result.ops_per_second > max_throughput {
80+
max_throughput = result.ops_per_second;
81+
}
82+
}
83+
if max_throughput > 0.0 {
84+
println!("Maximum throughput: {:.2} ops/sec", max_throughput);
85+
}
86+
}
87+
88+
Ok(())
89+
}

0 commit comments

Comments
 (0)