JuliaMSI/test/benchmark_v3.jl
Pixelguy14 89de5a56e0
Some checks failed
CI / Julia 1.10 - macOS-latest - x64 - (pull_request) Has been cancelled
CI / Julia 1.10 - ubuntu-latest - x64 - (pull_request) Has been cancelled
CI / Julia 1.10 - windows-latest - x64 - (pull_request) Has been cancelled
Fixed small issues within the file load and the precompiler script, added a final test benchmark suit to compare old Julia MSI library with current
2026-04-16 16:39:31 -06:00

216 lines
8.4 KiB
Julia

# test/benchmark_v3.jl
# ===================================================================
# High-Precision Performance Benchmark Suite (v3)
# ===================================================================
# This script integrates the robust statistical sampling of `BenchmarkTools`
# with the visual comparative mechanics against the legacy library.
# It captures the paradigm shift from "Time per slice" to "Pipeline Throughput".
# ===================================================================
using Pkg
Pkg.activate(joinpath(@__DIR__, ".."))
using BenchmarkTools
using DataFrames
using CSV
using CairoMakie
using Statistics
using MSI_src
using julia_mzML_imzML
struct BenchmarkCase
filepath::String
mz_value::Float64
mz_tolerance::Float64
name::String
end
const RESULTS_DIR = joinpath(@__DIR__, "results")
# List to benchmark
const BENCHMARK_CASES = [
BenchmarkCase("/home/pixel/Documents/Cinvestav_2025/Analisis/Chilli/ltpmsi-chilli.imzML", 420.0, 0.1, "Chilli Pepper"),
BenchmarkCase("/home/pixel/Documents/Cinvestav_2025/Analisis/imzML_DESI/40TopL,10TopR,30BottomL,20BottomR/40TopL,10TopR,30BottomL,20BottomR-centroid.imzML", 885.5, 0.1, "Colon Cancer Human"),
BenchmarkCase("/home/pixel/Documents/Cinvestav_2025/Analisis/imzML_AP_SMALDI/HR2MSImouseurinarybladderS096.imzML", 716.053, 0.1, "Mouse Urinary Bladder"),
BenchmarkCase("/home/pixel/Documents/Cinvestav_2025/Analisis/Leafs/CE1_Leaf_R3.imzML", 306.1, 0.1, "Leaf"),
BenchmarkCase("/home/pixel/Documents/Cinvestav_2025/Analisis/Liv2_imzML_TIMSConvert-selected/Liv2.imzML", 796.18, 0.1, "Liver Cut"),
BenchmarkCase("/home/pixel/Documents/Cinvestav_2025/Analisis/salida_Tims/Stomach_DHB.imzML", 804.3, 0.1, "Mouse Stomach 4GB")
]
function get_total_file_size_mb(filepath::String)
imzml_size = isfile(filepath) ? filesize(filepath) : 0
ibd_path = replace(filepath, r"\.(imzML|imzml)$" => ".ibd")
ibd_size = isfile(ibd_path) ? filesize(ibd_path) : 0
return round((imzml_size + ibd_size) / 1024^2, digits=2)
end
function flush_memory()
GC.gc(true)
if Sys.islinux()
# Force the OS to reclaim memory from the glibc allocator
ccall(:malloc_trim, Int32, (Int32,), 0)
end
end
function run_v3_benchmarks()
mkpath(RESULTS_DIR)
results = DataFrame()
println("="^60)
println("STARTING ENTERPRISE BENCHMARK SUITE (v3)")
println("="^60)
for case in BENCHMARK_CASES
if !isfile(case.filepath)
@warn "File not found: $(case.filepath). Skipping."
continue
end
println("\n--- Target: $(case.name) ---")
file_size = get_total_file_size_mb(case.filepath)
# ------------------------------------------------------------
# 1. JuliaMSI (New Architecture)
# ------------------------------------------------------------
println("[JuliaMSI - New Engine]")
flush_memory()
# Load Phase (Metadata Only / Memory Mapping)
load_stats_new = @timed OpenMSIData(case.filepath)
msi_data = load_stats_new.value
load_time_new_s = load_stats_new.time
mem_load_new_mb = load_stats_new.bytes / 1024^2
# Slicing Phase (High Precision)
b_slice_new = @benchmark get_mz_slice($msi_data, $(case.mz_value), $(case.mz_tolerance)) samples=10 seconds=5
mean_time_new_s = mean(b_slice_new.times) / 1e9
# Calculate Amortized Throughput (10 slices)
# Includes the 'Loading Wall' penalty
total_time_10_new = load_time_new_s + (10 * mean_time_new_s)
amortized_sps_new = 10.0 / total_time_10_new
close(msi_data)
flush_memory()
# ------------------------------------------------------------
# 2. julia_mzML_imzML (Legacy Architecture)
# ------------------------------------------------------------
println("[julia_mzML_imzML - Legacy]")
load_stats_old = try
@timed LoadImzml(case.filepath)
catch e
@warn "Legacy load failed: $e"
(time=Inf, bytes=Inf, value=nothing)
end
old_data = load_stats_old.value
mem_load_old_mb = load_stats_old.bytes / 1024^2
load_time_old_s = Inf
mean_time_old_s = Inf
amortized_sps_old = 0.0
if old_data !== nothing
b_slice_old = try
# Legacy can be extremely slow, constrain it heavily
@benchmark GetSlice($old_data, $(case.mz_value), $(case.mz_tolerance)) samples=3 seconds=10
catch e
@warn "Legacy slice failed: $e"
nothing
end
if b_slice_old !== nothing
mean_time_old_s = mean(b_slice_old.times) / 1e9
# Accurately reflect the massive load time in the throughput
load_time_old_s = load_stats_old.time
total_time_10_old = load_time_old_s + (10 * mean_time_old_s)
amortized_sps_old = 10.0 / total_time_10_old
end
end
flush_memory()
# ------------------------------------------------------------
# Record
# ------------------------------------------------------------
push!(results, (
Dataset = case.name,
FileSize_MB = file_size,
# Legacy Metrics
Legacy_LoadMem_MB = mem_load_old_mb,
Legacy_LoadTime_s = load_stats_old.time,
Legacy_Amortized_SPS_10 = amortized_sps_old,
# New Metrics
New_LoadMem_MB = mem_load_new_mb,
New_LoadTime_s = load_time_new_s,
New_Amortized_SPS_10 = amortized_sps_new,
# Competitive Deltas
RAM_Reduction_Pct = isfinite(mem_load_old_mb) ? ((mem_load_old_mb - mem_load_new_mb) / mem_load_old_mb)*100 : NaN,
UX_Speedup_Factor = isfinite(amortized_sps_old) ? (amortized_sps_new / amortized_sps_old) : NaN
))
println(" > RAM Reduction: $(round(results[end, :RAM_Reduction_Pct], digits=2))%")
println(" > Amortized Throughput (10 Slices): $(round(amortized_sps_old, digits=2)) -> $(round(amortized_sps_new, digits=2)) slices/sec")
end
csv_path = joinpath(RESULTS_DIR, "v3_enterprise_benchmarks.csv")
CSV.write(csv_path, results)
println("\nData saved to $csv_path")
plot_v3_results(results)
return results
end
function plot_v3_results(df::DataFrame)
if isempty(df) return end
fig = Figure(size=(1800, 1200), fontsize=24)
x_pos = 1:nrow(df)
labels = df.Dataset
# ---------- Plot 1: The RAM Revolution ----------
ax1 = Axis(fig[1, 1],
title="Initial RAM Cost (Loading & Mmap)",
ylabel="Memory (MB)",
xticks=(x_pos, labels), xticklabelrotation=π/8)
barplot!(ax1, x_pos .- 0.2, df.New_LoadMem_MB, color="#10b981", width=0.4, label="JuliaMSI (Mmap Lazy)")
barplot!(ax1, x_pos .+ 0.2, df.Legacy_LoadMem_MB, color="#ef4444", width=0.4, label="Legacy (Dense Matrix)")
axislegend(ax1, position=:lt)
# ---------- Plot 2: Throughput Leap ----------
ax2 = Axis(fig[1, 2],
title="Amortized Throughput (10 Slices - Higher is Better)",
ylabel="Slices / Second (Inc. Load Time)",
xticks=(x_pos, labels), xticklabelrotation=π/8)
barplot!(ax2, x_pos .- 0.2, df.New_Amortized_SPS_10, color="#10b981", width=0.4, label="JuliaMSI")
barplot!(ax2, x_pos .+ 0.2, df.Legacy_Amortized_SPS_10, color="#ef4444", width=0.4, label="Legacy")
axislegend(ax2, position=:lt)
# ---------- Plot 3: The Scaling Wall (File Size vs Throughput) ----------
ax3 = Axis(fig[2, 1:2],
title="Performance Scaling: UX Throughput vs Dataset Size",
xlabel="Dataset File Size (MB)",
ylabel="Amortized Throughput (Slices/sec)")
scatterlines!(ax3, df.FileSize_MB, df.New_Amortized_SPS_10, color="#10b981", markersize=15, linewidth=4, label="JuliaMSI Engine")
scatterlines!(ax3, df.FileSize_MB, df.Legacy_Amortized_SPS_10, color="#ef4444", markersize=15, linewidth=4, label="Legacy Engine")
axislegend(ax3, position=:rt)
save(joinpath(RESULTS_DIR, "v3_enterprise_dashboard.png"), fig)
println("\nDashboards saved successfully.")
end
# Ensure we process if run as the main script
if abspath(PROGRAM_FILE) == @__FILE__
run_v3_benchmarks()
end