/ benchmarks / plot.jl
plot.jl
 1  #!/usr/bin/env julia
 2  
 3  using Pkg
 4  Pkg.activate(@__DIR__)
 5  
 6  using JSON
 7  using StatsPlots
 8  using Printf
 9  using Dates
10  
11  function latest_results()
12      files = filter(f -> occursin(r"^results_\d{4}-\d{2}-\d{2}_\d{6}\.json$", f), readdir(@__DIR__))
13      isempty(files) && error("No results_*.json files found in benchmarks/")
14      sort(files)[end]
15  end
16  
17  function load_results(path::String)
18      open(joinpath(@__DIR__, path)) do io
19          return JSON.parse(io)
20      end
21  end
22  
23  median_ms(entry) = entry["median_seconds"] * 1e3
24  
25  function plot_group(results::Dict, key::Symbol, title::String, labels::Vector{String}, outfile::String)
26      group = get(results, String(key), nothing)
27      group === nothing && return
28      Ns = sort(parse.(Int, collect(keys(group))))
29      data = [median_ms(group[string(N)][labels[j]]) for j in eachindex(labels), N in Ns]
30      groupedbar(string.(Ns), permutedims(data), bar_position=:dodge,
31          legend=:topleft, title=title, xlabel="N", ylabel="median time (ms)")
32      png(joinpath(@__DIR__, outfile))
33  end
34  
35  function plot_single_ops(results::Dict, key::Symbol, order::Vector{String}, title::String, outfile::String)
36      group = get(results, String(key), nothing)
37      group === nothing && return
38      labels = [label for label in order if haskey(group, label)]
39      values = [median_ms(group[label]) for label in labels]
40      pretty = [replace(label, "_" => " ") for label in labels]
41      bar(pretty, values; legend=false, title=title, ylabel="median time (ms)", xticks=:auto, xrotation=30)
42      png(joinpath(@__DIR__, outfile))
43  end
44  
45  function main()
46      file = length(ARGS) > 0 ? ARGS[1] : latest_results()
47      println("Loading ", file)
48      res = load_results(file)
49  
50      # Fixed-base / MSM / normalization
51      plot_group(res, :fixed_g1, "Fixed-base G1 (median)", ["naive", "batch"], "fixed_g1.png")
52      plot_group(res, :fixed_g2, "Fixed-base G2 (median)", ["naive", "batch"], "fixed_g2.png")
53      plot_group(res, :msm_g1, "MSM G1 (median)", ["naive", "msm"], "msm_g1.png")
54      plot_group(res, :msm_g2, "MSM G2 (median)", ["naive", "msm"], "msm_g2.png")
55      plot_group(res, :norm_g1, "Batch norm G1 (median)", ["each", "batch"], "norm_g1.png")
56      plot_group(res, :norm_g2, "Batch norm G2 (median)", ["each", "batch"], "norm_g2.png")
57  
58      # Pairing summaries
59      plot_group(res, :pairing, "Pairing sequential vs batch", ["sequential", "batch"], "pairing.png")
60      plot_single_ops(res, :pairing_single,
61          ["pairing", "miller_loop", "final_exponentiation"],
62          "Pairing micro-operations", "pairing_ops.png")
63  
64      # Groth16 end-to-end
65      plot_single_ops(res, :groth16,
66          ["r1cs_to_qap", "setup", "prove", "verify_full", "prepare_vk", "prepare_inputs", "verify_prepared"],
67          "Groth16 pipeline", "groth16.png")
68  
69      println("Saved plots to benchmarks/*.png")
70  end
71  
72  main()