Unverified Commit 1f3e82bd authored by Landon James's avatar Landon James Committed by GitHub
Browse files

Add MVP of automated performance tests (#3953)

## Motivation and Context
<!--- Why is this change required? What problem does it solve? -->
<!--- If it fixes an open issue, please link to the issue here -->
We have an EOY goal to to integrate with our internal automated
performance monitoring solution.

## Description
<!--- Describe your changes in detail -->
Add a new crate at `aws/sdk/benchmarks/sdk-perf/` that runs some very
basic performance tests (currently just serializing and deserializing
DDB request/responses). Also add a new ci-script that generates the
correct SDK crates, compiles the `sdk-perf` crate, and creates a shell
script that the automated performance tests invoke to kick off our
tests.

## Testing
<!--- Please describe in detail how you tested your changes -->
<!--- Include details of your testing environment, and the tests you ran
to -->
<!--- see how your change affects other areas of the code, etc. -->
I deployed this code to my personal Catapult instance (along with the
changes to our internal CDK that will be CRed separately) and
successfully ran an `e2e-test`.

## Checklist
<!--- If a checkbox below is not applicable, then please DELETE it
rather than leaving it unchecked -->
No customer visible changes, this is strictly for our CI.

----

_By submitting this pull request, I confirm that you can use, modify,
copy, and redistribute this contribution, under the terms of your
choice._
parent e2d9fe8d
Loading
Loading
Loading
Loading
+1342 −0

File added.

Preview size limit exceeded, changes collapsed.

+18 −0
Original line number Diff line number Diff line
[package]
name = "sdk-perf"
version = "0.1.0"
edition = "2021"

[dependencies]
# SDK deps
# Note if you need more than dynamodb you will need to add the new services to the
# assemble command in the tools/ci-scripts/generate-sdk-perf-bin script
aws-sdk-dynamodb = {path = "../../build/aws-sdk/sdk/dynamodb"}
aws-smithy-runtime-api = {path = "../../build/aws-sdk/sdk/aws-smithy-runtime-api", features = ["http-1x"]}
aws-smithy-types = {path = "../../build/aws-sdk/sdk/aws-smithy-types"}
# External deps
bytes = "1.9.0"
clap = { version = "4.5.23", features = ["derive"] }
http = "1.2.0"
serde = {version = "1.0.216", features = ["derive"]}
serde_json = "1.0.133"
+106 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

use aws_sdk_dynamodb::operation::put_item::{PutItem, PutItemInput};
use aws_sdk_dynamodb::operation::query::QueryOutput;
use aws_sdk_dynamodb::types::AttributeValue;
use aws_smithy_runtime_api::client::interceptors::context::Input;
use aws_smithy_runtime_api::client::orchestrator::HttpResponse;
use aws_smithy_runtime_api::client::runtime_plugin::RuntimePlugin;
use aws_smithy_runtime_api::client::ser_de::{DeserializeResponse, SharedResponseDeserializer};
use aws_smithy_runtime_api::client::ser_de::{SerializeRequest, SharedRequestSerializer};
use aws_smithy_types::body::SdkBody;
use aws_smithy_types::config_bag::ConfigBag;

pub(crate) fn deserialize() {
    use aws_sdk_dynamodb::operation::query::Query;
    use bytes::Bytes;

    let response = HttpResponse::try_from(http::Response::builder()
         .header("server", "Server")
         .header("date", "Mon, 08 Mar 2021 15:51:23 GMT")
         .header("content-type", "application/x-amz-json-1.0")
         .header("content-length", "1231")
         .header("connection", "keep-alive")
         .header("x-amzn-requestid", "A5FGSJ9ET4OKB8183S9M47RQQBVV4KQNSO5AEMVJF66Q9ASUAAJG")
         .header("x-amz-crc32", "624725176")
         .status(http::StatusCode::from_u16(200).unwrap())
         .body(SdkBody::from(Bytes::copy_from_slice(br#"{"Count":2,"Items":[{"year":{"N":"2013"},"info":{"M":{"actors":{"L":[{"S":"Daniel Bruhl"},{"S":"Chris Hemsworth"},{"S":"Olivia Wilde"}]},"plot":{"S":"A re-creation of the merciless 1970s rivalry between Formula One rivals James Hunt and Niki Lauda."},"release_date":{"S":"2013-09-02T00:00:00Z"},"image_url":{"S":"http://ia.media-imdb.com/images/M/MV5BMTQyMDE0MTY0OV5BMl5BanBnXkFtZTcwMjI2OTI0OQ@@._V1_SX400_.jpg"},"genres":{"L":[{"S":"Action"},{"S":"Biography"},{"S":"Drama"},{"S":"Sport"}]},"directors":{"L":[{"S":"Ron Howard"}]},"rating":{"N":"8.3"},"rank":{"N":"2"},"running_time_secs":{"N":"7380"}}},"title":{"S":"Rush"}},{"year":{"N":"2013"},"info":{"M":{"actors":{"L":[{"S":"David Matthewman"},{"S":"Ann Thomas"},{"S":"Jonathan G. Neff"}]},"release_date":{"S":"2013-01-18T00:00:00Z"},"plot":{"S":"A rock band plays their music at high volumes, annoying the neighbors."},"genres":{"L":[{"S":"Comedy"},{"S":"Drama"}]},"image_url":{"S":"http://ia.media-imdb.com/images/N/O9ERWAU7FS797AJ7LU8HN09AMUP908RLlo5JF90EWR7LJKQ7@@._V1_SX400_.jpg"},"directors":{"L":[{"S":"Alice Smith"},{"S":"Bob Jones"}]},"rating":{"N":"6.2"},"rank":{"N":"11"},"running_time_secs":{"N":"5215"}}},"title":{"S":"Turn It Down, Or Else!"}}],"ScannedCount":2}"#)))
         .unwrap()).unwrap();

    let operation = Query::new();
    let config = operation.config().expect("operation should have config");
    let deserializer = config
        .load::<SharedResponseDeserializer>()
        .expect("operation should set a deserializer");

    let output = deserializer
        .deserialize_nonstreaming(&response)
        .expect("success");
    let output = output.downcast::<QueryOutput>().expect("correct type");
    assert_eq!(2, output.count);
}

macro_rules! attr_s {
    ($str_val:expr) => {
        AttributeValue::S($str_val.into())
    };
}
macro_rules! attr_n {
    ($str_val:expr) => {
        AttributeValue::N($str_val.into())
    };
}
macro_rules! attr_list {
    ( $($attr_val:expr),* ) => {
        AttributeValue::L(vec![$($attr_val),*])
    }
}
macro_rules! attr_obj {
    { $($str_val:expr => $attr_val:expr),* } => {
        AttributeValue::M(
            vec![
                $(($str_val.to_string(), $attr_val)),*
            ].into_iter().collect()
        )
    };
}

pub(crate) fn serialize() {
    let input = PutItemInput::builder()
            .table_name("Movies-5")
            .set_item(Some(
                attr_obj! {
                "year" => attr_n!("2013"),
                "title" => attr_s!("Turn It Down, Or Else!"),
                "info" => attr_obj! {
                    "directors" => attr_list![attr_s!("Alice Smith"), attr_s!("Bob Jones")],
                    "release_date" => attr_s!("2013-01-18T00:00:00Z"),
                    "rating" => attr_n!("6.2"),
                    "genres" => attr_list!(attr_s!("Comedy"), attr_s!("Drama")),
                    "image_url" => attr_s!("http://ia.media-imdb.com/images/N/O9ERWAU7FS797AJ7LU8HN09AMUP908RLlo5JF90EWR7LJKQ7@@._V1_SX400_.jpg"),
                    "plot" => attr_s!("A rock band plays their music at high volumes, annoying the neighbors."),
                    "rank" => attr_n!("11"),
                    "running_time_secs" => attr_n!("5215"),
                    "actors" => attr_list!(attr_s!("David Matthewman"), attr_s!("Ann Thomas"), attr_s!("Jonathan G. Neff"))
                }
            }.as_m().unwrap().clone(),
            ))
            .build()
            .expect("valid input");
    let operation = PutItem::new();
    let config = operation.config().expect("operation should have config");
    let serializer = config
        .load::<SharedRequestSerializer>()
        .expect("operation should set a serializer");
    let mut config_bag = ConfigBag::base();
    let input = Input::erase(input.clone());

    let request = serializer
        .serialize_input(input, &mut config_bag)
        .expect("success");
    let body = request.body().bytes().unwrap();
    assert_eq!(body[0], b'{');
}
+55 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

mod ddb_serde;
mod results;
mod test_util;

use clap::Parser;
use ddb_serde::{deserialize, serialize};
use results::Results;
use test_util::{run_test, TestConfig};

#[derive(Parser, Debug)]
#[command(version, about, long_about = None)]
struct Args {
    /// Name of the person to greet
    #[arg(short, long)]
    commit_id: String,
}

fn main() {
    let args = Args::parse();

    let mut results = Results {
        product_id: "aws-sdk-rust".into(),
        sdk_version: None,
        commit_id: args.commit_id,
        results: Vec::new(),
    };

    // Note: in the below microseconds should be case insensitive, but due to a
    // bug in the perf test evaluation it is currently not. Can get rid of the
    // capitalization when that is fixed.
    let deserialize_config = TestConfig {
        name: "deserialize.ddb".into(),
        description: "Deserializing a DDB response.".into(),
        unit: "Microseconds".into(),
        runs: 10,
    };

    let serialize_config = TestConfig {
        name: "serialize.ddb".into(),
        description: "Serializing a DDB request.".into(),
        unit: "Microseconds".into(),
        runs: 10,
    };

    run_test(&deserialize_config, &mut results, deserialize);
    run_test(&serialize_config, &mut results, serialize);

    let output = serde_json::to_string(&results).unwrap();
    println!("{output:#}");
}
+37 −0
Original line number Diff line number Diff line
/*
 * Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
 * SPDX-License-Identifier: Apache-2.0
 */

use serde::Serialize;

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Results {
    pub(crate) product_id: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub(crate) sdk_version: Option<String>,
    pub(crate) commit_id: String,
    pub(crate) results: Vec<Result>,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Result {
    pub(crate) name: String,
    pub(crate) description: String,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub(crate) publish_to_cloudwatch: Option<bool>,
    #[serde(skip_serializing_if = "Option::is_none")]
    pub(crate) dimensions: Option<Vec<Dimension>>,
    pub(crate) date: u64,
    pub(crate) measurements: Vec<f64>,
    pub(crate) unit: String,
}

#[derive(Serialize)]
#[serde(rename_all = "camelCase")]
pub(crate) struct Dimension {
    pub(crate) name: String,
    pub(crate) value: String,
}
Loading