284 lines
8.6 KiB
GDScript
284 lines
8.6 KiB
GDScript
extends ExampleBase
|
|
|
|
## Number of iterations for each benchmark
|
|
const ITERATIONS: int = 10
|
|
|
|
## Thread counts for multithreaded benchmarks
|
|
const THREAD_COUNTS: Array = [1, 2, 4, 8, 16, 32, 64, 100]
|
|
|
|
## Path to the YAML file to benchmark
|
|
const YAML_PATH: String = "res://addons/yaml/examples/data/supported_syntax.yaml"
|
|
|
|
func _init() -> void:
|
|
icon = "⚡"
|
|
|
|
func run_examples() -> void:
|
|
log_header("YAML Speed Benchmark")
|
|
print(YAML.version())
|
|
|
|
run_example("Load YAML File", load_yaml_file_benchmark)
|
|
run_example("Parse (No Style) Benchmark", parse_benchmark)
|
|
run_example("Parse (With Style) Benchmark", parse_with_style_benchmark)
|
|
run_example("Stringify (No Style) Benchmark", stringify_benchmark)
|
|
run_example("Stringify (With Style) Benchmark", stringify_with_style_benchmark)
|
|
run_example("Compare Results", compare_results)
|
|
run_example("Parse (Threaded) Bechmark", parse_threaded)
|
|
run_example("Stringify (Threaded) Benchmark", stringify_threaded)
|
|
|
|
var yaml_input: String
|
|
var parse_times := []
|
|
var style_parse_times := []
|
|
var stringify_times := []
|
|
var style_stringify_times := []
|
|
var data = null
|
|
var style = null
|
|
|
|
func load_yaml_file_benchmark() -> void:
|
|
log_info("Loading YAML file: " + YAML_PATH)
|
|
|
|
var file := FileAccess.open(YAML_PATH, FileAccess.READ)
|
|
if !file:
|
|
log_error("Could not open file: " + YAML_PATH)
|
|
return
|
|
|
|
yaml_input = file.get_as_text()
|
|
log_success("File loaded, size: " + str(yaml_input.length()) + " characters")
|
|
|
|
var result := YAML.parse(yaml_input)
|
|
data = result.get_data()
|
|
|
|
if LOG_VERBOSE:
|
|
log_result("First 200 characters:\n" + yaml_input.substr(0, 200) + "...")
|
|
|
|
func parse_benchmark() -> void:
|
|
if yaml_input.is_empty():
|
|
log_error("No YAML input loaded")
|
|
return
|
|
|
|
log_info("Running parse benchmark (" + str(ITERATIONS) + " iterations)...")
|
|
|
|
for i in range(ITERATIONS):
|
|
var start := Time.get_ticks_usec()
|
|
var result = YAML.parse(yaml_input)
|
|
var elapsed := Time.get_ticks_usec() - start
|
|
|
|
if result.has_error():
|
|
log_error("Iteration " + str(i + 1) + " failed: " + result.get_error())
|
|
continue
|
|
|
|
parse_times.append(elapsed)
|
|
log_info("Iteration " + str(i + 1) + ": " + str(elapsed) + " µs")
|
|
|
|
if parse_times.size() > 0:
|
|
var avg = float(parse_times.reduce(func(a, b): return a + b)) / parse_times.size()
|
|
log_success("Average parse time: " + str(avg) + " µs")
|
|
else:
|
|
log_error("No successful parse iterations")
|
|
|
|
func parse_with_style_benchmark() -> void:
|
|
if yaml_input.is_empty():
|
|
log_error("No YAML input loaded")
|
|
return
|
|
|
|
log_info("Running parse with style benchmark (" + str(ITERATIONS) + " iterations)...")
|
|
|
|
for i in range(ITERATIONS):
|
|
var start := Time.get_ticks_usec()
|
|
var result = YAML.parse(yaml_input, YAML.create_security(), true)
|
|
var elapsed := Time.get_ticks_usec() - start
|
|
|
|
if result.has_error():
|
|
log_error("Iteration " + str(i + 1) + " failed: " + result.get_error())
|
|
continue
|
|
|
|
if i == 0:
|
|
style = result.get_style()
|
|
|
|
style_parse_times.append(elapsed)
|
|
log_info("Iteration " + str(i + 1) + ": " + str(elapsed) + " µs")
|
|
|
|
if style_parse_times.size() > 0:
|
|
var avg = float(style_parse_times.reduce(func(a, b): return a + b)) / style_parse_times.size()
|
|
log_success("Average parse with style time: " + str(avg) + " µs")
|
|
else:
|
|
log_error("No successful parse with style iterations")
|
|
|
|
func stringify_benchmark() -> void:
|
|
if data == null:
|
|
log_error("No data available for stringify tests")
|
|
return
|
|
|
|
log_info("Running stringify benchmark (" + str(ITERATIONS) + " iterations)...")
|
|
|
|
for i in range(ITERATIONS):
|
|
var start := Time.get_ticks_usec()
|
|
var result = YAML.stringify(data)
|
|
var elapsed := Time.get_ticks_usec() - start
|
|
|
|
if result.has_error():
|
|
log_error("Iteration " + str(i + 1) + " failed: " + result.get_error())
|
|
continue
|
|
|
|
stringify_times.append(elapsed)
|
|
log_info("Iteration " + str(i + 1) + ": " + str(elapsed) + " µs")
|
|
|
|
if stringify_times.size() > 0:
|
|
var avg = float(stringify_times.reduce(func(a, b): return a + b)) / stringify_times.size()
|
|
log_success("Average stringify time: " + str(avg) + " µs")
|
|
else:
|
|
log_error("No successful stringify iterations")
|
|
|
|
func stringify_with_style_benchmark() -> void:
|
|
if data == null or style == null:
|
|
log_error("No data or style available for stringify tests")
|
|
return
|
|
|
|
log_info("Running stringify with style benchmark (" + str(ITERATIONS) + " iterations)...")
|
|
|
|
for i in range(ITERATIONS):
|
|
var start := Time.get_ticks_usec()
|
|
var result = YAML.stringify(data, style)
|
|
var elapsed := Time.get_ticks_usec() - start
|
|
|
|
if result.has_error():
|
|
log_error("Iteration " + str(i + 1) + " failed: " + result.get_error())
|
|
continue
|
|
|
|
style_stringify_times.append(elapsed)
|
|
log_info("Iteration " + str(i + 1) + ": " + str(elapsed) + " µs")
|
|
|
|
if style_stringify_times.size() > 0:
|
|
var avg = float(style_stringify_times.reduce(func(a, b): return a + b)) / style_stringify_times.size()
|
|
log_success("Average stringify with style time: " + str(avg) + " µs")
|
|
else:
|
|
log_error("No successful stringify with style iterations")
|
|
|
|
func compare_results() -> void:
|
|
log_subheader("Performance Comparison")
|
|
|
|
# Collect all test results
|
|
var all_tests = [
|
|
{
|
|
"name": "Parse (no style)",
|
|
"times": parse_times
|
|
},
|
|
{
|
|
"name": "Parse (with style)",
|
|
"times": style_parse_times
|
|
},
|
|
{
|
|
"name": "Stringify (no style)",
|
|
"times": stringify_times
|
|
},
|
|
{
|
|
"name": "Stringify (with style)",
|
|
"times": style_stringify_times
|
|
}
|
|
]
|
|
|
|
# Calculate and print stats
|
|
for test in all_tests:
|
|
var times = test.times
|
|
if times.is_empty():
|
|
log_warning(test.name + ": No valid results")
|
|
continue
|
|
|
|
var avg: float = float(times.reduce(func(a, b): return a + b)) / times.size()
|
|
var min_time: int = times.min()
|
|
var max_time: int = times.max()
|
|
|
|
log_info(test.name + ":")
|
|
log_info(" Average: %.2f µs" % avg)
|
|
log_info(" Min: %d µs" % min_time)
|
|
log_info(" Max: %d µs" % max_time)
|
|
|
|
log_info("\nPerformance Insights:")
|
|
|
|
# Compare parse with and without style
|
|
if !parse_times.is_empty() and !style_parse_times.is_empty():
|
|
var avg_parse = float(parse_times.reduce(func(a, b): return a + b)) / parse_times.size()
|
|
var avg_style_parse = float(style_parse_times.reduce(func(a, b): return a + b)) / style_parse_times.size()
|
|
|
|
var style_overhead = ((avg_style_parse / avg_parse) - 1.0) * 100.0
|
|
log_info("Style detection adds approximately %.1f%% overhead to parsing" % style_overhead)
|
|
|
|
# Compare stringify with and without style
|
|
if !stringify_times.is_empty() and !style_stringify_times.is_empty():
|
|
var avg_stringify = float(stringify_times.reduce(func(a, b): return a + b)) / stringify_times.size()
|
|
var avg_style_stringify = float(style_stringify_times.reduce(func(a, b): return a + b)) / style_stringify_times.size()
|
|
|
|
var style_overhead = ((avg_style_stringify / avg_stringify) - 1.0) * 100.0
|
|
log_info("Using style adds approximately %.1f%% overhead to stringify" % style_overhead)
|
|
|
|
func parse_threaded() -> void:
|
|
if yaml_input.is_empty():
|
|
log_error("No YAML input loaded")
|
|
return
|
|
|
|
log_subheader("Threaded Parse Benchmark")
|
|
|
|
for thread_count in THREAD_COUNTS:
|
|
var threads: Array[Thread] = []
|
|
|
|
var start := Time.get_ticks_usec()
|
|
|
|
for i in range(thread_count):
|
|
var thread := Thread.new()
|
|
thread.start(_parse_thread_func.bind(yaml_input))
|
|
threads.append(thread)
|
|
|
|
var total_parse_time := 0
|
|
for thread in threads:
|
|
total_parse_time += thread.wait_to_finish()
|
|
|
|
var wall_time := Time.get_ticks_usec() - start
|
|
var avg_thread_time := float(total_parse_time / thread_count)
|
|
|
|
log_info("%d thread(s): wall=%d µs | avg thread=%d µs | throughput=%.1f parses/ms" % [
|
|
thread_count,
|
|
wall_time,
|
|
avg_thread_time,
|
|
thread_count / (wall_time / 1000.0)
|
|
])
|
|
|
|
func stringify_threaded() -> void:
|
|
if data == null:
|
|
log_error("No data available for stringify tests")
|
|
return
|
|
|
|
log_subheader("Threaded Stringify Benchmark")
|
|
|
|
for thread_count in THREAD_COUNTS:
|
|
var threads: Array[Thread] = []
|
|
|
|
var start := Time.get_ticks_usec()
|
|
|
|
for i in range(thread_count):
|
|
var thread := Thread.new()
|
|
thread.start(_stringify_thread_func.bind(data))
|
|
threads.append(thread)
|
|
|
|
var total_stringify_time := 0
|
|
for thread in threads:
|
|
total_stringify_time += thread.wait_to_finish()
|
|
|
|
var wall_time := Time.get_ticks_usec() - start
|
|
var avg_thread_time := float(total_stringify_time / thread_count)
|
|
|
|
log_info("%d thread(s): wall=%d µs | avg thread=%d µs | throughput=%.1f stringifies/ms" % [
|
|
thread_count,
|
|
wall_time,
|
|
avg_thread_time,
|
|
thread_count / (wall_time / 1000.0)
|
|
])
|
|
|
|
static func _parse_thread_func(input: String) -> int:
|
|
var start := Time.get_ticks_usec()
|
|
YAML.parse(input)
|
|
return Time.get_ticks_usec() - start
|
|
|
|
static func _stringify_thread_func(input_data: Variant) -> int:
|
|
var start := Time.get_ticks_usec()
|
|
YAML.stringify(input_data)
|
|
return Time.get_ticks_usec() - start
|