///|
/// Deterministic STEP workload generator used by the local benchmark command.
/// It creates real IFC wall products, placements, representations and
/// extrusions instead of timing a synthetic string operation.
pub fn benchmark_fixture(product_count : Int) -> String {
let count = if product_count < 0 { 0 } else { product_count }
let output = StringBuilder()
output <+
#|ISO-10303-21;
output <+
#|HEADER;
output <+
#|FILE_DESCRIPTION(('ViewDefinition [CoordinationView]'),'2;1');
output <+
#|FILE_NAME('benchmark.ifc','2026-08-19T00:00:00',('moonbit-ifc'),(''),'moonbit-ifc','moonbit-ifc','');
output <+
#|FILE_SCHEMA(('IFC4'));
output <+
#|ENDSEC;
output <+
#|DATA;
output <+
#|#1=IFCPROJECT('0000000000000000000000',#2,'Benchmark project',$,$,$,$,(#10),#11);
output <+
#|#2=IFCOWNERHISTORY($,$,$,.ADDED.,$,$,$,0);
output <+
#|#10=IFCUNITASSIGNMENT((#12));
output <+
#|#11=IFCCONTEXT($,'Model','ViewDefinition [CoordinationView]','',#13,$,$,(#14),$);
output <+
#|#12=IFCSIUNIT(*,.LENGTHUNIT.,$,.METRE.);
output <+
#|#13=IFCAXIS2PLACEMENT3D(#15,$,$);
output <+
#|#14=IFCGEOMETRICREPRESENTATIONCONTEXT($,'Model',3,1.0E-05,#13,$,$,(#14),$);
output <+
#|#15=IFCCARTESIANPOINT((0.0,0.0,0.0));
for ordinal in 0.. BenchmarkCase raise ParseError {
let source = benchmark_fixture(product_count)
{
name,
iterations: if iterations < 1 {
1
} else {
iterations
},
source_length: source.length(),
entity_count: parse(source).entities.length(),
}
}
///|
fn elapsed_millis(start : UInt64, finish : UInt64) -> UInt64 {
if finish >= start {
finish - start
} else {
0UL
}
}
///|
fn operations_per_second(iterations : Int, elapsed : UInt64) -> Double {
if elapsed == 0UL {
0.0
} else {
iterations.to_double() * 1000.0 / elapsed.to_double()
}
}
///|
fn benchmark_product_count(case : BenchmarkCase) -> Int {
if case.entity_count <= 8 {
0
} else {
(case.entity_count - 8) / 6
}
}
///|
pub fn benchmark_parse_case(
case : BenchmarkCase,
) -> BenchmarkResult raise ParseError {
let source = if case.name == "smoke" {
minimal_fixture()
} else {
benchmark_fixture(benchmark_product_count(case))
}
let start = @env.now()
let mut allocated = 0
for _ in 0.. BenchmarkResult raise ParseError {
let source = benchmark_fixture(benchmark_product_count(case))
let start = @env.now()
let mut allocated = 0
for _ in 0.. BenchmarkResult {
let source = benchmark_fixture(benchmark_product_count(case))
let start = @env.now()
let mut allocated = 0
for _ in 0.. Array[BenchmarkCase] raise ParseError {
[
benchmark_case("small-8-walls", 8, 8),
benchmark_case("medium-64-walls", 64, 3),
benchmark_case("large-256-walls", 256, 1),
]
}
///|
pub fn run_benchmark_report() -> BenchmarkReport raise ParseError {
let cases : Array[BenchmarkResult] = []
for case in benchmark_cases() {
cases.push(benchmark_parse_case(case))
cases.push(benchmark_stream_case(case))
if case.entity_count <= 500 {
cases.push(benchmark_pipeline_case(case))
}
}
{
toolchain: "MoonBit stable toolchain; timing source: @env.now()",
cases,
notes: [
"Each case parses the generated ISO 10303-21 source before measuring.", "Elapsed time is wall-clock milliseconds on the local machine.",
"Run the command again on the target machine before publishing numbers.",
],
}
}
///|
fn benchmark_result_json(result : BenchmarkResult) -> String {
"{\"name\":" +
json_string(result.name) +
",\"iterations\":" +
result.iterations.to_string() +
",\"elapsed_millis\":" +
result.elapsed_millis.to_string() +
",\"operations_per_second\":" +
result.operations_per_second.to_string() +
",\"source_length\":" +
result.source_length.to_string() +
",\"entity_count\":" +
result.entity_count.to_string() +
",\"allocated_entities\":" +
result.allocated_entities.to_string() +
"}"
}
///|
pub fn BenchmarkReport::to_json(self : BenchmarkReport) -> String {
"{\"toolchain\":" +
json_string(self.toolchain) +
",\"cases\":[" +
self.cases.map(benchmark_result_json).join(",") +
"],\"notes\":[" +
self.notes.map(json_string).join(",") +
"]}"
}
///|
pub fn BenchmarkReport::to_markdown(self : BenchmarkReport) -> String {
let rows = self.cases
.map(result => {
"| " +
result.name +
" | " +
result.iterations.to_string() +
" | " +
result.source_length.to_string() +
" | " +
result.entity_count.to_string() +
" | " +
result.elapsed_millis.to_string() +
" | " +
result.operations_per_second.to_string() +
" |"
})
.join("\n")
"| Case | Iterations | Source chars | Entities | Elapsed ms | Ops/s |\n" +
"|---|---:|---:|---:|---:|---:|\n" +
rows
}
///|
pub fn BenchmarkReport::headline(self : BenchmarkReport) -> String {
"benchmark cases=" +
self.cases.length().to_string() +
", toolchain=" +
self.toolchain
}
///|
pub fn BenchmarkResult::is_measured(self : BenchmarkResult) -> Bool {
self.elapsed_millis > 0UL && self.operations_per_second > 0.0
}
///|
pub fn BenchmarkResult::bytes_per_operation(self : BenchmarkResult) -> Double {
if self.iterations == 0 {
0.0
} else {
self.source_length.to_double() / self.iterations.to_double()
}
}
///|
pub fn benchmark_result_summary(result : BenchmarkResult) -> String {
result.name +
": " +
result.elapsed_millis.to_string() +
" ms, " +
result.operations_per_second.to_string() +
" ops/s, " +
result.source_length.to_string() +
" chars, " +
result.entity_count.to_string() +
" entities"
}
///|
pub fn benchmark_smoke(source : String) -> BenchmarkResult raise ParseError {
benchmark_parse_case({
name: "smoke",
iterations: 1,
source_length: source.length(),
entity_count: parse(source).entities.length(),
})
}