///|
/// A repeatable benchmark case with a deterministic workload definition.
pub struct BenchmarkCase {
name : String
rounds : Int
payload_size : Int
target : String
} derive(Eq, Debug)
///|
pub fn BenchmarkCase::new(
name : String,
rounds : Int,
payload_size : Int,
target : String,
) -> Result[BenchmarkCase, String] {
if name == "" || target == "" {
Err("benchmark name and target must not be empty")
} else if rounds < 1 || payload_size < 1 || payload_size > 249 {
Err("benchmark workload is outside supported limits")
} else {
Ok({ name, rounds, payload_size, target })
}
}
///|
pub fn BenchmarkCase::name(self : BenchmarkCase) -> String {
self.name
}
///|
pub fn BenchmarkCase::rounds(self : BenchmarkCase) -> Int {
self.rounds
}
///|
pub fn BenchmarkCase::payload_size(self : BenchmarkCase) -> Int {
self.payload_size
}
///|
pub fn BenchmarkCase::target(self : BenchmarkCase) -> String {
self.target
}
///|
pub struct BenchmarkResult {
case : BenchmarkCase
encoded_frames : Int
encoded_bytes : Int
checksum : UInt
elapsed_micros : Int
} derive(Eq, Debug)
///|
pub fn BenchmarkResult::new(
case : BenchmarkCase,
workload : BenchmarkWorkload,
elapsed_micros : Int,
) -> Result[BenchmarkResult, String] {
if elapsed_micros < 0 {
Err("benchmark duration cannot be negative")
} else {
Ok({
case,
encoded_frames: workload.encoded_frames(),
encoded_bytes: workload.encoded_bytes(),
checksum: workload.checksum(),
elapsed_micros,
})
}
}
///|
pub fn BenchmarkResult::case(self : BenchmarkResult) -> BenchmarkCase {
self.case
}
///|
pub fn BenchmarkResult::encoded_frames(self : BenchmarkResult) -> Int {
self.encoded_frames
}
///|
pub fn BenchmarkResult::encoded_bytes(self : BenchmarkResult) -> Int {
self.encoded_bytes
}
///|
pub fn BenchmarkResult::checksum(self : BenchmarkResult) -> UInt {
self.checksum
}
///|
pub fn BenchmarkResult::elapsed_micros(self : BenchmarkResult) -> Int {
self.elapsed_micros
}
///|
pub fn BenchmarkResult::frames_per_second(self : BenchmarkResult) -> Float {
if self.elapsed_micros == 0 {
0.0
} else {
Float::from_int(self.encoded_frames) *
1000000.0 /
Float::from_int(self.elapsed_micros)
}
}
///|
pub fn BenchmarkResult::megabytes_per_second(self : BenchmarkResult) -> Float {
if self.elapsed_micros == 0 {
0.0
} else {
Float::from_int(self.encoded_bytes) *
1000000.0 /
Float::from_int(self.elapsed_micros) /
1000000.0
}
}
///|
pub struct BenchmarkSuite {
cases : Array[BenchmarkCase]
} derive(Debug)
///|
pub fn BenchmarkSuite::new() -> BenchmarkSuite {
{ cases: [] }
}
///|
pub fn BenchmarkSuite::add(
self : BenchmarkSuite,
case : BenchmarkCase,
) -> Result[Unit, String] {
for existing in self.cases {
if existing.name() == case.name() {
return Err("benchmark case name already exists")
}
}
self.cases.push(case)
Ok(())
}
///|
pub fn BenchmarkSuite::len(self : BenchmarkSuite) -> Int {
self.cases.length()
}
///|
pub fn BenchmarkSuite::cases(self : BenchmarkSuite) -> Array[BenchmarkCase] {
self.cases.copy()
}
///|
/// Run a deterministic benchmark workload; elapsed time is supplied by the host.
pub fn BenchmarkSuite::run(
self : BenchmarkSuite,
elapsed_micros : Array[Int],
) -> Result[Array[BenchmarkResult], String] {
if elapsed_micros.length() != self.cases.length() {
Err("benchmark timing count does not match case count")
} else {
let result : Array[BenchmarkResult] = []
for index in 0.. return Err(error)
Ok(workload) =>
match BenchmarkResult::new(case, workload, elapsed_micros[index]) {
Err(error) => return Err(error)
Ok(value) => result.push(value)
}
}
}
Ok(result)
}
}
///|
/// Produce the standard local suite used by README benchmark commands.
pub fn default_benchmark_suite() -> BenchmarkSuite {
let suite = BenchmarkSuite::new()
ignore(
suite.add(BenchmarkCase::new("encode-small", 10000, 16, "native").unwrap()),
)
ignore(
suite.add(BenchmarkCase::new("encode-medium", 5000, 128, "native").unwrap()),
)
ignore(
suite.add(
BenchmarkCase::new("encode-maximum", 1000, 249, "native").unwrap(),
),
)
suite
}
///|
pub fn benchmark_result_table(results : Array[BenchmarkResult]) -> String {
let out = StringBuilder()
out <+ "case,frames,bytes,elapsed_us,frames_per_second,checksum\n"
for result in results {
out <+
"\{result.case().name()},\{result.encoded_frames()},\{result.encoded_bytes()},\{result.elapsed_micros()},\{result.frames_per_second()},\{result.checksum()}\n"
}
out.to_string()
}
///|
pub fn benchmark_fixture_checksums() -> Array[UInt] {
let result : Array[UInt] = []
for case in default_benchmark_suite().cases() {
result.push(
run_benchmark_workload(case.rounds(), case.payload_size())
.unwrap()
.checksum(),
)
}
result
}