Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Add PerformanceTests #1910

Merged
merged 11 commits into from
Mar 28, 2025
19 changes: 19 additions & 0 deletions PerformanceTests/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
// swift-tools-version: 5.9

import PackageDescription

let package = Package(
name: "SDKWorkbench",
platforms: [.macOS(.v12), .iOS(.v15)],
dependencies: [
.package(name: "aws-sdk-swift", path: "../../aws-sdk-swift"),
],
targets: [
.executableTarget(
name: "PerformanceTestRunner",
dependencies: [
.product(name: "AWSSTS", package: "aws-sdk-swift"),
]
),
]
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

struct Dimension: Codable {
let name: String
let value: String
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

protocol PerformanceTest {
var name: String { get }
var description: String { get }
var unit: Unit { get }
var dimensions: [Dimension] { get }
var test: () async throws -> Double { get }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation

struct PerformanceTestReport: Codable {
var productId: String = "AWS SDK for Swift"
let sdkVersion: String?
let commitId: String
let results: [PerformanceTestResult]
}

extension PerformanceTestReport {
func printFormatted() {
let jsonEncoder = JSONEncoder()
jsonEncoder.outputFormatting = .prettyPrinted

if let jsonData = try? jsonEncoder.encode(self),
let jsonString = String(data: jsonData, encoding: .utf8) {
print(jsonString)
} else {
print("Error encoding JSON")
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

struct PerformanceTestResult: Codable {
let name: String
let description: String
let unit: String
let date: Int
let measurements: [Double]
let dimensions: [Dimension]?
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

enum Unit: String {
case operationsPerSecond = "Operations/Second"
case bitsPerSecond = "Bits/Second"
case megabitsPerSecond = "Megabits/Second"
case gigabitsPerSecond = "Gigabits/Second"
case seconds = "Seconds"
case milliseconds = "Milliseconds"
case microseconds = "Microseconds"
case nanoseconds = "Nanoseconds"
case bytes = "Bytes"
case megabytes = "Megabytes"
case gigabytes = "Gigabytes"
case percentage = "Percentage"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation

struct PerformanceTestRunner {

var iterations: Int

/// Executes the given test multiple times
func runTest(
_ perfTest: PerformanceTest
) async throws -> PerformanceTestResult {
var measurements: [Double] = []

for _ in 0..<iterations {
measurements.append(try await perfTest.test())
}

let timestamp = Int(Date().timeIntervalSince1970)
return PerformanceTestResult(
name: perfTest.name,
description: perfTest.description,
unit: perfTest.unit.rawValue,
date: timestamp,
measurements: measurements,
dimensions: perfTest.dimensions
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation

struct ProcessRunner {
static func runProcess(
executable: String = "/usr/bin/env",
arguments: [String]
) -> String {
let process = Process()
process.executableURL = URL(fileURLWithPath: executable)
process.arguments = arguments

let outputPipe = Pipe()
process.standardOutput = outputPipe
process.standardError = Pipe()

do {
try process.run()
process.waitUntilExit()
let outputData = outputPipe.fileHandleForReading.readDataToEndOfFile()
return String(data: outputData, encoding: .utf8)?.trimmingCharacters(in: .whitespacesAndNewlines) ?? ""
} catch {
print("ProcessRunner Error: \(error.localizedDescription)")
return ""
}
}

/// Retrieves the current Git commit ID.
static func getGitCommitId() -> String {
return ProcessRunner.runProcess(arguments: ["git", "rev-parse", "HEAD"])
}

/// Retrieves the SDK version from a file.
static func getSdkVersion() -> String {
return ProcessRunner.runProcess(arguments: ["cat", "../../aws-sdk-swift/Package.version"])
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we do it elsewhere, but for safety maybe trim leading & trailing whitespace before returning version string

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

enum OperatingSystem: String {
case macOS = "macOS"
case Linux = "Linux"
case Unknown = "Unknown"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: cases of a Swift enum should start with lower case (macOS, linux, unknown)


static var current: OperatingSystem {
#if os(macOS)
return .macOS
#elseif os(Linux)
return .Linux
#else
return .Unknown
#endif
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import Foundation
import ClientRuntime

// MARK: - Main Entry Point
@main
struct PerformanceMain {
static func main() async throws {
let runner = PerformanceTestRunner(iterations: 5)
let commitId = ProcessRunner.getGitCommitId()
let sdkVersion = ProcessRunner.getSdkVersion()

// Add more tests here
let performanceTests = [
AWSSTSGetCallerIdentity()
]

var results: [PerformanceTestResult] = []
for test in performanceTests {
let result = try await runner.runTest(test)
results.append(result)
}

let report = PerformanceTestReport(
sdkVersion: sdkVersion,
commitId: commitId,
results: results
)
report.printFormatted()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
//
// Copyright Amazon.com Inc. or its affiliates.
// All Rights Reserved.
//
// SPDX-License-Identifier: Apache-2.0
//

import AWSSTS
import Foundation

struct AWSSTSGetCallerIdentity: PerformanceTest {
let name = "sts.getcalleridentity.latency"

let description = "The total time between initiating a GetCallerIdentity and reading the last byte of the object."

let unit = Unit.milliseconds

let dimensions = [
Dimension(name: "OS", value: OperatingSystem.current.rawValue),
]

let test = getCallerIdentity

static func getCallerIdentity() async throws -> Double {
let start = Date()
let client = try await STSClient()
_ = try await client.getCallerIdentity(input: .init())
return Date().timeIntervalSince(start) * 1000 // Convert seconds to milliseconds
}
}
Loading