From 0556eed2fc04d6dd91b57349bd54d300a71ec3ce Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 5 Mar 2026 14:40:57 -0500 Subject: [PATCH 01/60] add solver benchmarks --- src/JSOBenchmarks.jl | 4 +++- src/solver_benchmarks.jl | 13 +++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) create mode 100644 src/solver_benchmarks.jl diff --git a/src/JSOBenchmarks.jl b/src/JSOBenchmarks.jl index 78c450d..9115e46 100644 --- a/src/JSOBenchmarks.jl +++ b/src/JSOBenchmarks.jl @@ -16,7 +16,9 @@ using Plots # JSO modules using SolverBenchmark -export run_benchmarks +include("solver_benchmarks.jl") + +export run_benchmarks, run_solver_benchmarks export profile_solvers_from_pkgbmark export create_gist_from_json_dict, create_gist_from_json_file export update_gist_from_json_dict, update_gist_from_json_file diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl new file mode 100644 index 0000000..14088e3 --- /dev/null +++ b/src/solver_benchmarks.jl @@ -0,0 +1,13 @@ +function run_solver_benchmarks( + repo_name::AbstractString, + bmark_dir::AbstractString; + reference_branch::AbstractString = "main", + gist_url::Union{AbstractString, Nothing} = nothing, +) + + update_gist = gist_url !== nothing + is_git = isdir(joinpath(bmark_dir, "..", ".git")) + @info "" is_git update_gist + + +end \ No newline at end of file From eb4e67ebaafb1dac5ef661496262d65ac8c2061d Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 5 Mar 2026 15:17:30 -0500 Subject: [PATCH 02/60] run benchmark script on current commit --- src/solver_benchmarks.jl | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 14088e3..c6879bc 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -3,11 +3,32 @@ function run_solver_benchmarks( bmark_dir::AbstractString; reference_branch::AbstractString = "main", gist_url::Union{AbstractString, Nothing} = nothing, + script = "benchmarks.jl", ) update_gist = gist_url !== nothing is_git = isdir(joinpath(bmark_dir, "..", ".git")) @info "" is_git update_gist - + local gist_id + if update_gist + gist_id = split(gist_url, "/")[end] + @info "" gist_id + end + + # if we are running these benchmarks from the git repository + # we want to develop the package instead of using the release + if is_git + Pkg.develop(PackageSpec(path = joinpath(bmark_dir, ".."))) + else + Pkg.activate(bmark_dir) + end + Pkg.instantiate() + + # name the benchmark after the repo or the sha of HEAD + bmarkname = is_git ? readchomp(`$git rev-parse HEAD`) : lowercase(repo_name) + @info "" bmarkname + + # Run the benchmark script on this commit + this_commit = include(joinpath(bmark_dir, script)) end \ No newline at end of file From 2466b3ebc038587b02e41663f45e6cf71eda1798 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 5 Mar 2026 15:37:47 -0500 Subject: [PATCH 03/60] add LibGit2 dep --- Project.toml | 4 +++- src/JSOBenchmarks.jl | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index cc37fac..afa3012 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "JSOBenchmarks" uuid = "9410e2bb-e8e7-4fa0-9768-685b196f59b5" -authors = ["Dominique Orban "] version = "0.1.0" +authors = ["Dominique Orban "] [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" @@ -11,6 +11,7 @@ Git = "d7ba0133-e1db-5d97-8f8c-041e4b3a1eb2" GitHub = "bc5e4493-9b4d-5f90-b8aa-2b2bcaad7a26" JLD2 = "033835bb-8acc-5ee8-8aae-3f567f8a3819" JSON = "682c06a0-de6a-54ab-a142-c8b1cf79cde6" +LibGit2 = "76f85450-5226-5b5a-8eaa-529ad045b433" Pkg = "44cfe95a-1eb2-52ea-b672-e2afdf69b78f" PkgBenchmark = "32113eaa-f34f-5b0d-bd6c-c81e245fc73d" Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80" @@ -24,6 +25,7 @@ Git = "1.3" GitHub = "5.9" JLD2 = "0.4,0.5" JSON = "0.21" +LibGit2 = "1.11.0" Pkg = "1.9" PkgBenchmark = "0.2" Plots = "1.39" diff --git a/src/JSOBenchmarks.jl b/src/JSOBenchmarks.jl index 9115e46..4db2981 100644 --- a/src/JSOBenchmarks.jl +++ b/src/JSOBenchmarks.jl @@ -10,6 +10,7 @@ using Git using GitHub using JLD2 using JSON +using LibGit2 using PkgBenchmark using Plots From abf030cc866bd036e323244a972e6ce391ba6357 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 5 Mar 2026 16:24:59 -0500 Subject: [PATCH 04/60] add _withcommit function --- src/solver_benchmarks.jl | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index c6879bc..f9a041a 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -30,5 +30,39 @@ function run_solver_benchmarks( @info "" bmarkname # Run the benchmark script on this commit - this_commit = include(joinpath(bmark_dir, script)) -end \ No newline at end of file + this_commit = Base.include(Main, joinpath(bmark_dir, script)) + @assert this_commit isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(this_commit)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solver function" + + # Run the benchmark script on the reference branch + local reference + if is_git + #reference = _withcommit(f, repo_name, reference_branch) + end + +end + + +# Runs a script at a commit on a repo and afterwards goes back +# to the original commit / branch. +# This code is based on https://github.com/JuliaCI/PkgBenchmark.jl/blob/master/src/util.jl +function _withcommit(script, repo, commit) + original_commit = _shastring(repo, "HEAD") + LibGit2.transact(repo) do r + branch = try LibGit2.branch(r) catch err; nothing end + try + LibGit2.checkout!(r, _shastring(r, commit)) + f() + catch err + rethrow(err) + finally + if branch !== nothing + LibGit2.branch!(r, branch) + else + LibGit2.checkout!(r, original_commit) + end + end + end +end + +_shastring(r::LibGit2.GitRepo, targetname) = string(LibGit2.revparseid(r, targetname)) +_shastring(dir::AbstractString, targetname) = LibGit2.with(r -> _shastring(r, targetname), LibGit2.GitRepo(dir)) From 74e0fa622ad265f54268400995f5f986b5354781 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 5 Mar 2026 17:40:46 -0500 Subject: [PATCH 05/60] add plotting --- src/solver_benchmarks.jl | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index f9a041a..8fc276f 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -4,6 +4,7 @@ function run_solver_benchmarks( reference_branch::AbstractString = "main", gist_url::Union{AbstractString, Nothing} = nothing, script = "benchmarks.jl", + values = [(:elapsed_time, "CPU Time"), (:neval_obj, "# Objective Evals"), (:neval_grad, "# Gradient Evals")], ) update_gist = gist_url !== nothing @@ -36,9 +37,38 @@ function run_solver_benchmarks( # Run the benchmark script on the reference branch local reference if is_git - #reference = _withcommit(f, repo_name, reference_branch) + repo_dir = joinpath(bmark_dir, "..") + repo = LibGit2.GitRepo(repo_dir) + reference = _withcommit(joinpath(bmark_dir, script), repo, reference_branch) end + #TODO: save in a jld2 file: see run_benchmarks + + # Plotting + if is_git + for key in keys(this_commit) + if haskey(reference, key) + @info "Plotting $key" + stats_subset = Dict(:this_commit => this_commit[key], :reference => reference[key]) + solved(df) = (df.status .== :first_order) + for value in values + @assert hasproperty(df, value[1]) "Expected the stats resulting from the benchmark script to have property $(value[1]), please check the values keyword argument." + end + costs = [df -> .!solved(df) * Inf + getproperty(df, value[1]) for value in values] + costnames = [value[2] for value in values] + + p = profile_solvers(stats_subset, costs, costnames;xlabel = "", ylabel = "") + fname = "this_commit_vs_reference_$(key)" + savefig("$(fname).svg") + else + @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." + end + end + + end + + + end @@ -47,11 +77,13 @@ end # This code is based on https://github.com/JuliaCI/PkgBenchmark.jl/blob/master/src/util.jl function _withcommit(script, repo, commit) original_commit = _shastring(repo, "HEAD") + local result LibGit2.transact(repo) do r branch = try LibGit2.branch(r) catch err; nothing end try LibGit2.checkout!(r, _shastring(r, commit)) - f() + result = Base.include(Main, script) + @assert result isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(this_commit)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solver function" catch err rethrow(err) finally @@ -62,6 +94,7 @@ function _withcommit(script, repo, commit) end end end + return result end _shastring(r::LibGit2.GitRepo, targetname) = string(LibGit2.revparseid(r, targetname)) From 7ca2063e4614a2965a84ade85bed61269ee77d77 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 09:24:27 -0500 Subject: [PATCH 06/60] create gist --- src/solver_benchmarks.jl | 39 ++++++++++++++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 8fc276f..91132ad 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -45,6 +45,8 @@ function run_solver_benchmarks( #TODO: save in a jld2 file: see run_benchmarks # Plotting + files_dict = Dict{String, Any}() + svgs = String[] if is_git for key in keys(this_commit) if haskey(reference, key) @@ -60,15 +62,50 @@ function run_solver_benchmarks( p = profile_solvers(stats_subset, costs, costnames;xlabel = "", ylabel = "") fname = "this_commit_vs_reference_$(key)" savefig("$(fname).svg") + push!(svgs, "$(fname).svg") + content = read(fname, String) + files_dict[fname] = Dict("content" => content) else @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." end end + end + + @info "creating or updating gist" + # json description of gist + json_dict = Dict{String, Any}( + "description" => "$(repo_name) repository benchmark", + "public" => true, + "files" => files_dict, + ) + + if update_gist + json_dict["gist_id"] = gist_id + end + gist_json = "$(bmarkname).json" + open(gist_json, "w") do f + JSON.print(f, json_dict) end + local new_gist_url + if update_gist + update_gist_from_json_dict(gist_id, json_dict) + else + new_gist = create_gist_from_json_dict(json_dict) + new_gist_url = string(new_gist.html_url) + end + + readme = "# $(repo_name) Solver Benchmarks\n\n" + readme *= "Comparison between current commit and $(reference_branch).\n\n" + + for svg in svgs + title = replace(svg, ".svg" => "") + readme *= "## $(title)\n\n" + readme *= "![]($(svg))\n\n" + end - + files_dict["README.md"] = Dict("content" => readme) end From 4d1e7384396fa6ea2480379a94fe1168265c72f0 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 10:03:09 -0500 Subject: [PATCH 07/60] try adding origin to target name --- src/solver_benchmarks.jl | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 91132ad..52162b9 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -134,5 +134,10 @@ function _withcommit(script, repo, commit) return result end -_shastring(r::LibGit2.GitRepo, targetname) = string(LibGit2.revparseid(r, targetname)) -_shastring(dir::AbstractString, targetname) = LibGit2.with(r -> _shastring(r, targetname), LibGit2.GitRepo(dir)) +function _shastring(r::LibGit2.GitRepo, targetname) + try + return string(LibGit2.revparseid(r, targetname)) + catch + return string(LibGit2.revparseid(r, "origin/$targetname")) + end +end \ No newline at end of file From 7da994212bd3c2ddf954e1cd3a9cdb312d1445ee Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 10:12:56 -0500 Subject: [PATCH 08/60] fix error message --- src/solver_benchmarks.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 52162b9..8987445 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -120,7 +120,7 @@ function _withcommit(script, repo, commit) try LibGit2.checkout!(r, _shastring(r, commit)) result = Base.include(Main, script) - @assert result isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(this_commit)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solver function" + @assert result isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(result)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solver function" catch err rethrow(err) finally From 64984a26446a6e80654e268e571e01c6d6e2f81a Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 10:59:14 -0500 Subject: [PATCH 09/60] make _shastring more robust --- src/solver_benchmarks.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 8987445..a29e3e1 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -135,9 +135,7 @@ function _withcommit(script, repo, commit) end function _shastring(r::LibGit2.GitRepo, targetname) - try - return string(LibGit2.revparseid(r, targetname)) - catch - return string(LibGit2.revparseid(r, "origin/$targetname")) - end + branch = LibGit2.lookup_branch(r, targetname) + @assert branch !== nothing "Branch $(targetname) not found in repository." + return LibGit2.GitHash(LibGit2.GitObject(r, LibGit2.name(branch))) end \ No newline at end of file From 502ca449547d92c45913ffb26801e90620545864 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 11:13:46 -0500 Subject: [PATCH 10/60] resolve sha hashes --- src/solver_benchmarks.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index a29e3e1..1b74221 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -113,12 +113,12 @@ end # to the original commit / branch. # This code is based on https://github.com/JuliaCI/PkgBenchmark.jl/blob/master/src/util.jl function _withcommit(script, repo, commit) - original_commit = _shastring(repo, "HEAD") + original_commit = LibGit2.GitHash(LibGit2.GitObject(repo, "HEAD")) local result LibGit2.transact(repo) do r branch = try LibGit2.branch(r) catch err; nothing end try - LibGit2.checkout!(r, _shastring(r, commit)) + LibGit2.checkout!(r, _sha(r, commit)) result = Base.include(Main, script) @assert result isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(result)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solver function" catch err @@ -134,7 +134,7 @@ function _withcommit(script, repo, commit) return result end -function _shastring(r::LibGit2.GitRepo, targetname) +function _sha(r::LibGit2.GitRepo, targetname) branch = LibGit2.lookup_branch(r, targetname) @assert branch !== nothing "Branch $(targetname) not found in repository." return LibGit2.GitHash(LibGit2.GitObject(r, LibGit2.name(branch))) From 7653dd41192de465ecd375157dea69cff95a5cc9 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 11:27:14 -0500 Subject: [PATCH 11/60] convert git hashes to strings --- src/solver_benchmarks.jl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 1b74221..c70a8fb 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -113,7 +113,7 @@ end # to the original commit / branch. # This code is based on https://github.com/JuliaCI/PkgBenchmark.jl/blob/master/src/util.jl function _withcommit(script, repo, commit) - original_commit = LibGit2.GitHash(LibGit2.GitObject(repo, "HEAD")) + original_commit = string(LibGit2.GitHash(LibGit2.GitObject(repo, "HEAD"))) local result LibGit2.transact(repo) do r branch = try LibGit2.branch(r) catch err; nothing end @@ -134,8 +134,8 @@ function _withcommit(script, repo, commit) return result end -function _sha(r::LibGit2.GitRepo, targetname) +function _shastring(r::LibGit2.GitRepo, targetname) branch = LibGit2.lookup_branch(r, targetname) @assert branch !== nothing "Branch $(targetname) not found in repository." - return LibGit2.GitHash(LibGit2.GitObject(r, LibGit2.name(branch))) + return string(LibGit2.GitHash(LibGit2.GitObject(r, LibGit2.name(branch)))) end \ No newline at end of file From d87b0eefa7b3fbdca92da228d9e3ba6dd95fbfdd Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 11:34:41 -0500 Subject: [PATCH 12/60] update function name --- src/solver_benchmarks.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index c70a8fb..1decb93 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -118,7 +118,7 @@ function _withcommit(script, repo, commit) LibGit2.transact(repo) do r branch = try LibGit2.branch(r) catch err; nothing end try - LibGit2.checkout!(r, _sha(r, commit)) + LibGit2.checkout!(r, _shastring(r, commit)) result = Base.include(Main, script) @assert result isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(result)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solver function" catch err From 115938a9a57a02df78a3e91ba2b34f3098e60303 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 11:50:31 -0500 Subject: [PATCH 13/60] debugging: show repo --- src/solver_benchmarks.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 1decb93..f9efee6 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -113,6 +113,7 @@ end # to the original commit / branch. # This code is based on https://github.com/JuliaCI/PkgBenchmark.jl/blob/master/src/util.jl function _withcommit(script, repo, commit) + println(repo) original_commit = string(LibGit2.GitHash(LibGit2.GitObject(repo, "HEAD"))) local result LibGit2.transact(repo) do r From 72e35514ee4903e332d350e4db3e6e844d08a5b3 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 12:01:19 -0500 Subject: [PATCH 14/60] debug show repo_dir and bmark_dir --- src/solver_benchmarks.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index f9efee6..de73507 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -39,6 +39,8 @@ function run_solver_benchmarks( if is_git repo_dir = joinpath(bmark_dir, "..") repo = LibGit2.GitRepo(repo_dir) + println("repo_dir : $repo_dir") + println("bmark_dir : $bmark_dir") reference = _withcommit(joinpath(bmark_dir, script), repo, reference_branch) end From 7c6243398132081cae1fda40317053df30403b62 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 12:16:50 -0500 Subject: [PATCH 15/60] add extra lookup branch step --- src/solver_benchmarks.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index de73507..6f1b59b 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -139,6 +139,7 @@ end function _shastring(r::LibGit2.GitRepo, targetname) branch = LibGit2.lookup_branch(r, targetname) + branch = branch === nothing ? LibGit2.lookup_branch(r, targetname, true) : branch # Search remote as well if not found locally. @assert branch !== nothing "Branch $(targetname) not found in repository." return string(LibGit2.GitHash(LibGit2.GitObject(r, LibGit2.name(branch)))) end \ No newline at end of file From cdb59b8a22ba33197da350a2df1cbfdce90a08d8 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 12:27:39 -0500 Subject: [PATCH 16/60] add bunch of checks --- src/solver_benchmarks.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 6f1b59b..a82d19b 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -140,6 +140,8 @@ end function _shastring(r::LibGit2.GitRepo, targetname) branch = LibGit2.lookup_branch(r, targetname) branch = branch === nothing ? LibGit2.lookup_branch(r, targetname, true) : branch # Search remote as well if not found locally. + branch = branch === nothing ? LibGit2.lookup_branch(r, "origin/$(targetname)") : branch + branch = branch === nothing ? LibGit2.lookup_branch(r, "origin/$(targetname)", true) : branch # Search remote as well if not found locally. @assert branch !== nothing "Branch $(targetname) not found in repository." return string(LibGit2.GitHash(LibGit2.GitObject(r, LibGit2.name(branch)))) end \ No newline at end of file From b1a04d97bf1bc4941ec60080035cdb862f60c852 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 12:58:10 -0500 Subject: [PATCH 17/60] remove hasproperty check --- src/solver_benchmarks.jl | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index a82d19b..e17f4d8 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -55,10 +55,7 @@ function run_solver_benchmarks( @info "Plotting $key" stats_subset = Dict(:this_commit => this_commit[key], :reference => reference[key]) solved(df) = (df.status .== :first_order) - for value in values - @assert hasproperty(df, value[1]) "Expected the stats resulting from the benchmark script to have property $(value[1]), please check the values keyword argument." - end - costs = [df -> .!solved(df) * Inf + getproperty(df, value[1]) for value in values] + costs = [stats_subset -> .!solved(stats_subset) * Inf + getproperty(stats_subset, value[1]) for value in values] costnames = [value[2] for value in values] p = profile_solvers(stats_subset, costs, costnames;xlabel = "", ylabel = "") From fb4418b8336a6af41260003210197bc69deee7b0 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 12:58:34 -0500 Subject: [PATCH 18/60] improve function --- src/solver_benchmarks.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index e17f4d8..1abf34a 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -55,7 +55,7 @@ function run_solver_benchmarks( @info "Plotting $key" stats_subset = Dict(:this_commit => this_commit[key], :reference => reference[key]) solved(df) = (df.status .== :first_order) - costs = [stats_subset -> .!solved(stats_subset) * Inf + getproperty(stats_subset, value[1]) for value in values] + costs = [df -> .!solved(df) * Inf + getproperty(df, value[1]) for value in values] costnames = [value[2] for value in values] p = profile_solvers(stats_subset, costs, costnames;xlabel = "", ylabel = "") From 932b79d4b1c4b0874e7125e34877aa50a989702f Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 6 Mar 2026 14:13:19 -0500 Subject: [PATCH 19/60] fix content --- src/solver_benchmarks.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 1abf34a..622ebcf 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -62,7 +62,7 @@ function run_solver_benchmarks( fname = "this_commit_vs_reference_$(key)" savefig("$(fname).svg") push!(svgs, "$(fname).svg") - content = read(fname, String) + content = read("$(fname).svg", String) files_dict[fname] = Dict("content" => content) else @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." From fe4663f174639b13997f71b07eac6b877baa0fc6 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 08:55:47 -0400 Subject: [PATCH 20/60] add simple_md_report --- src/solver_benchmarks.jl | 30 ++++++++++++++++++++++-------- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 622ebcf..19fda2b 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -87,14 +87,6 @@ function run_solver_benchmarks( JSON.print(f, json_dict) end - local new_gist_url - if update_gist - update_gist_from_json_dict(gist_id, json_dict) - else - new_gist = create_gist_from_json_dict(json_dict) - new_gist_url = string(new_gist.html_url) - end - readme = "# $(repo_name) Solver Benchmarks\n\n" readme *= "Comparison between current commit and $(reference_branch).\n\n" @@ -105,6 +97,28 @@ function run_solver_benchmarks( end files_dict["README.md"] = Dict("content" => readme) + + local new_gist_url + if update_gist + update_gist_from_json_dict(gist_id, json_dict) + else + new_gist = create_gist_from_json_dict(json_dict) + new_gist_url = string(new_gist.html_url) + end + + @info "preparing simple Markdown report" + is_git && + write_simple_md_report( + "bmark_$(bmarkname).md", + this_commit, + reference, + nothing, + new_gist_url, + svgs, + ) + + @info "finished" + return nothing end From 99f10066ff9d5a055ac8ab5505c8f93284e014e0 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 09:08:13 -0400 Subject: [PATCH 21/60] add nothing arguments to write_simple_md_report --- src/JSOBenchmarks.jl | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/JSOBenchmarks.jl b/src/JSOBenchmarks.jl index 4db2981..be0ff95 100644 --- a/src/JSOBenchmarks.jl +++ b/src/JSOBenchmarks.jl @@ -313,12 +313,24 @@ function write_simple_md_report( open(fname, "w") do f println(f, "Gist: $(gist_url)\n") println(f, "Full results stored as artifacts\n") - write_md_svgs(f, "Overview", gist_url, svgs) - write_md(f, "Judgement", judgement) - println(f, "
") - write_md(f, "this_commit", this_commit) - println(f, "
") - write_md(f, "Reference", reference) + + if !isempty(svgs) + write_md_svgs(f, "Overview", gist_url, svgs) + end + + if judgement !== nothing + write_md(f, "Judgement", judgement) + println(f, "
") + end + + if this_commit !== nothing + write_md(f, "this_commit", this_commit) + println(f, "
") + end + + if reference !== nothing + write_md(f, "Reference", reference) + end end end From ad89d3d9dfff029d5b6c78d6166868318f4f4531 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 09:20:58 -0400 Subject: [PATCH 22/60] update write_simple_md_report --- src/solver_benchmarks.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 19fda2b..f537b8b 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -110,8 +110,8 @@ function run_solver_benchmarks( is_git && write_simple_md_report( "bmark_$(bmarkname).md", - this_commit, - reference, + nothing, + nothing, nothing, new_gist_url, svgs, From c792b41cb234ba97e341560a4866ced807d014b2 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 09:33:49 -0400 Subject: [PATCH 23/60] update files_dict uploaded to gist --- src/solver_benchmarks.jl | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index f537b8b..0e975df 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -63,7 +63,7 @@ function run_solver_benchmarks( savefig("$(fname).svg") push!(svgs, "$(fname).svg") content = read("$(fname).svg", String) - files_dict[fname] = Dict("content" => content) + files_dict["$(fname).svg"] = Dict("content" => content) else @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." end @@ -90,12 +90,6 @@ function run_solver_benchmarks( readme = "# $(repo_name) Solver Benchmarks\n\n" readme *= "Comparison between current commit and $(reference_branch).\n\n" - for svg in svgs - title = replace(svg, ".svg" => "") - readme *= "## $(title)\n\n" - readme *= "![]($(svg))\n\n" - end - files_dict["README.md"] = Dict("content" => readme) local new_gist_url From 2ab42e6a4a16c1b061e8fe4dead49ec5fcc1d308 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 10:29:55 -0400 Subject: [PATCH 24/60] fix project toml --- Project.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Project.toml b/Project.toml index afa3012..08fd6d0 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "JSOBenchmarks" uuid = "9410e2bb-e8e7-4fa0-9768-685b196f59b5" -version = "0.1.0" authors = ["Dominique Orban "] +version = "0.1.0" [deps] BenchmarkTools = "6e4b80f9-dd63-53aa-95a3-0cdb28fa8baf" From fc96c5215a67bc8c602024faf8c3c9c7b319aeab Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 11:02:40 -0400 Subject: [PATCH 25/60] remove debugging code --- src/solver_benchmarks.jl | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 0e975df..65d7444 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -39,8 +39,6 @@ function run_solver_benchmarks( if is_git repo_dir = joinpath(bmark_dir, "..") repo = LibGit2.GitRepo(repo_dir) - println("repo_dir : $repo_dir") - println("bmark_dir : $bmark_dir") reference = _withcommit(joinpath(bmark_dir, script), repo, reference_branch) end @@ -120,7 +118,6 @@ end # to the original commit / branch. # This code is based on https://github.com/JuliaCI/PkgBenchmark.jl/blob/master/src/util.jl function _withcommit(script, repo, commit) - println(repo) original_commit = string(LibGit2.GitHash(LibGit2.GitObject(repo, "HEAD"))) local result LibGit2.transact(repo) do r From 1fbb7e2a031c2663ebdb3ac231226df03d0d36f7 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 11:06:05 -0400 Subject: [PATCH 26/60] apply suggestions from copilot --- src/solver_benchmarks.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 65d7444..684fcc0 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -32,7 +32,7 @@ function run_solver_benchmarks( # Run the benchmark script on this commit this_commit = Base.include(Main, joinpath(bmark_dir, script)) - @assert this_commit isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(this_commit)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solver function" + @assert this_commit isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(this_commit)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solvers function" # Run the benchmark script on the reference branch local reference @@ -125,7 +125,7 @@ function _withcommit(script, repo, commit) try LibGit2.checkout!(r, _shastring(r, commit)) result = Base.include(Main, script) - @assert result isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(result)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solver function" + @assert result isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(result)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solvers function" catch err rethrow(err) finally From aa5866baddc3c241e9776cbb64eba9c8c09e61ca Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 11:07:34 -0400 Subject: [PATCH 27/60] remove todos --- src/solver_benchmarks.jl | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 684fcc0..f07aee8 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -42,8 +42,6 @@ function run_solver_benchmarks( reference = _withcommit(joinpath(bmark_dir, script), repo, reference_branch) end - #TODO: save in a jld2 file: see run_benchmarks - # Plotting files_dict = Dict{String, Any}() svgs = String[] @@ -105,7 +103,7 @@ function run_solver_benchmarks( nothing, nothing, nothing, - new_gist_url, + update_gist ? gist_url : new_gist_url, svgs, ) From 9ba1afe82b96ee90476b388228036592930538a9 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 11:09:06 -0400 Subject: [PATCH 28/60] apply other suggestions from copilot --- src/solver_benchmarks.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index f07aee8..aab8948 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -66,6 +66,11 @@ function run_solver_benchmarks( end end + readme = "# $(repo_name) Solver Benchmarks\n\n" + readme *= "Comparison between current commit and $(reference_branch).\n\n" + + files_dict["README.md"] = Dict("content" => readme) + @info "creating or updating gist" # json description of gist json_dict = Dict{String, Any}( @@ -83,11 +88,6 @@ function run_solver_benchmarks( JSON.print(f, json_dict) end - readme = "# $(repo_name) Solver Benchmarks\n\n" - readme *= "Comparison between current commit and $(reference_branch).\n\n" - - files_dict["README.md"] = Dict("content" => readme) - local new_gist_url if update_gist update_gist_from_json_dict(gist_id, json_dict) From 2f686127eeb1bef881c4e77e21f53fb5762cdbae Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 16:09:53 -0400 Subject: [PATCH 29/60] return gist_urls --- src/JSOBenchmarks.jl | 2 +- src/solver_benchmarks.jl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/JSOBenchmarks.jl b/src/JSOBenchmarks.jl index be0ff95..6861fc9 100644 --- a/src/JSOBenchmarks.jl +++ b/src/JSOBenchmarks.jl @@ -201,7 +201,7 @@ function run_benchmarks( ) @info "finished" - return nothing + return update_gist ? gist_url : new_gist_url end # Utility functions diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index aab8948..c2a6b2b 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -108,7 +108,7 @@ function run_solver_benchmarks( ) @info "finished" - return nothing + return update_gist ? gist_url : new_gist_url end From f081bf7d7d1d1d7f7bdaee3a451421097f564f81 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 17:00:10 -0400 Subject: [PATCH 30/60] add markdown table --- src/solver_benchmarks.jl | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index c2a6b2b..c2c2827 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -42,9 +42,12 @@ function run_solver_benchmarks( reference = _withcommit(joinpath(bmark_dir, script), repo, reference_branch) end - # Plotting + # Plotting and tables files_dict = Dict{String, Any}() svgs = String[] + stats_columns = [:name, :f, :t] + hdr_override = Dict([:name => "Name", :f => "f(x)", :t => "Time"]) + tables = "# Solver Benchmarks Tables \n\n" if is_git for key in keys(this_commit) if haskey(reference, key) @@ -55,6 +58,7 @@ function run_solver_benchmarks( costnames = [value[2] for value in values] p = profile_solvers(stats_subset, costs, costnames;xlabel = "", ylabel = "") + tables *= pretty_stats(String, stats_subset[!, stats_columns], hdr_override = hdr_override, tf=tf_markdown) fname = "this_commit_vs_reference_$(key)" savefig("$(fname).svg") push!(svgs, "$(fname).svg") @@ -70,6 +74,7 @@ function run_solver_benchmarks( readme *= "Comparison between current commit and $(reference_branch).\n\n" files_dict["README.md"] = Dict("content" => readme) + files_dict["TABLES.md"] = Dict("content" => tables) @info "creating or updating gist" # json description of gist From b9e56bf1660e376d0fa9fe4cb1a15fff3abe22cf Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 17:06:55 -0400 Subject: [PATCH 31/60] update tables --- src/solver_benchmarks.jl | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index c2c2827..a730182 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -58,12 +58,15 @@ function run_solver_benchmarks( costnames = [value[2] for value in values] p = profile_solvers(stats_subset, costs, costnames;xlabel = "", ylabel = "") - tables *= pretty_stats(String, stats_subset[!, stats_columns], hdr_override = hdr_override, tf=tf_markdown) fname = "this_commit_vs_reference_$(key)" savefig("$(fname).svg") push!(svgs, "$(fname).svg") content = read("$(fname).svg", String) files_dict["$(fname).svg"] = Dict("content" => content) + + tables *= "\n" * fname * "\n" + tables *= pretty_stats(String, this_commit[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown) + tables *= pretty_stats(String, reference[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown) else @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." end From b7138c620d1bbfcef093ac7225cdc03fc7d3d84c Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 17:26:49 -0400 Subject: [PATCH 32/60] fix tables --- src/solver_benchmarks.jl | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index a730182..0f6149a 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -45,8 +45,8 @@ function run_solver_benchmarks( # Plotting and tables files_dict = Dict{String, Any}() svgs = String[] - stats_columns = [:name, :f, :t] - hdr_override = Dict([:name => "Name", :f => "f(x)", :t => "Time"]) + stats_columns = [:name, :objective, :elapsed_time] + hdr_override = Dict([:name => "Name", :objective => "f(x)", :elapsed_time => "Time"]) tables = "# Solver Benchmarks Tables \n\n" if is_git for key in keys(this_commit) @@ -63,10 +63,10 @@ function run_solver_benchmarks( push!(svgs, "$(fname).svg") content = read("$(fname).svg", String) files_dict["$(fname).svg"] = Dict("content" => content) - + println(this_commit[key]) tables *= "\n" * fname * "\n" - tables *= pretty_stats(String, this_commit[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown) - tables *= pretty_stats(String, reference[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown) + tables *= sprint(io -> pretty_stats(io, this_commit[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown)) + tables *= sprint(io -> pretty_stats(io, reference[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown)) else @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." end From 202282c7975d80aff34bc22bb5f975a1f1b05b9e Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 17:34:21 -0400 Subject: [PATCH 33/60] format tables --- src/solver_benchmarks.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 0f6149a..b4ddb02 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -58,14 +58,14 @@ function run_solver_benchmarks( costnames = [value[2] for value in values] p = profile_solvers(stats_subset, costs, costnames;xlabel = "", ylabel = "") - fname = "this_commit_vs_reference_$(key)" + fname = "## This commit vs reference: $(key)\n\n" savefig("$(fname).svg") push!(svgs, "$(fname).svg") content = read("$(fname).svg", String) files_dict["$(fname).svg"] = Dict("content" => content) - println(this_commit[key]) tables *= "\n" * fname * "\n" tables *= sprint(io -> pretty_stats(io, this_commit[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown)) + tables *= "\n" tables *= sprint(io -> pretty_stats(io, reference[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown)) else @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." From 5d177f3daea6e40e09f18aef000661f2da415770 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 17:43:25 -0400 Subject: [PATCH 34/60] revert fname --- src/solver_benchmarks.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index b4ddb02..c4a3b28 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -58,12 +58,12 @@ function run_solver_benchmarks( costnames = [value[2] for value in values] p = profile_solvers(stats_subset, costs, costnames;xlabel = "", ylabel = "") - fname = "## This commit vs reference: $(key)\n\n" + fname = "this_commit_vs_reference_$(key)" savefig("$(fname).svg") push!(svgs, "$(fname).svg") content = read("$(fname).svg", String) files_dict["$(fname).svg"] = Dict("content" => content) - tables *= "\n" * fname * "\n" + tables *= "\n## This commit vs reference: $(key)\n\n\n" tables *= sprint(io -> pretty_stats(io, this_commit[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown)) tables *= "\n" tables *= sprint(io -> pretty_stats(io, reference[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown)) From 36724685c2a3123f41198fb9dc0719202149966e Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 9 Mar 2026 17:51:59 -0400 Subject: [PATCH 35/60] add titles to tables --- src/solver_benchmarks.jl | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index c4a3b28..e9761f9 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -63,9 +63,10 @@ function run_solver_benchmarks( push!(svgs, "$(fname).svg") content = read("$(fname).svg", String) files_dict["$(fname).svg"] = Dict("content" => content) - tables *= "\n## This commit vs reference: $(key)\n\n\n" + tables *= "\n## This commit vs reference: $(key)\n\n" + tables *= "### This commit\n\n\n" tables *= sprint(io -> pretty_stats(io, this_commit[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown)) - tables *= "\n" + tables *= "\n\n### Reference\n\n\n" tables *= sprint(io -> pretty_stats(io, reference[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown)) else @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." From d74a81bdee51f63ae49fbd0d5d70b3ad157f7f95 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Wed, 11 Mar 2026 15:35:33 -0400 Subject: [PATCH 36/60] remove readme --- src/solver_benchmarks.jl | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index e9761f9..0e833bd 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -74,10 +74,6 @@ function run_solver_benchmarks( end end - readme = "# $(repo_name) Solver Benchmarks\n\n" - readme *= "Comparison between current commit and $(reference_branch).\n\n" - - files_dict["README.md"] = Dict("content" => readme) files_dict["TABLES.md"] = Dict("content" => tables) @info "creating or updating gist" From 953196ec2cf6f7eb0d25017a2cb50e0ec815f709 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Wed, 11 Mar 2026 15:41:17 -0400 Subject: [PATCH 37/60] add table_values kwargs --- src/solver_benchmarks.jl | 21 +++++++++++++-------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 0e833bd..d9ff01b 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -4,7 +4,8 @@ function run_solver_benchmarks( reference_branch::AbstractString = "main", gist_url::Union{AbstractString, Nothing} = nothing, script = "benchmarks.jl", - values = [(:elapsed_time, "CPU Time"), (:neval_obj, "# Objective Evals"), (:neval_grad, "# Gradient Evals")], + profile_values = [(:elapsed_time, "CPU Time"), (:neval_obj, "# Objective Evals"), (:neval_grad, "# Gradient Evals")], + table_values = [(:name, "Name"), (:objective, "f(x)"), (:elapsed_time, "Time")] ) update_gist = gist_url !== nothing @@ -45,24 +46,28 @@ function run_solver_benchmarks( # Plotting and tables files_dict = Dict{String, Any}() svgs = String[] - stats_columns = [:name, :objective, :elapsed_time] - hdr_override = Dict([:name => "Name", :objective => "f(x)", :elapsed_time => "Time"]) + + solved(df) = (df.status .== :first_order) + costs = [df -> .!solved(df) * Inf + getproperty(df, value[1]) for value in profile_values] + costnames = [value[2] for value in profile_values] + + stats_columns = [value[1] for value in table_values] + hdr_override = [value[2] for value in table_values] + tables = "# Solver Benchmarks Tables \n\n" if is_git for key in keys(this_commit) if haskey(reference, key) @info "Plotting $key" stats_subset = Dict(:this_commit => this_commit[key], :reference => reference[key]) - solved(df) = (df.status .== :first_order) - costs = [df -> .!solved(df) * Inf + getproperty(df, value[1]) for value in values] - costnames = [value[2] for value in values] - - p = profile_solvers(stats_subset, costs, costnames;xlabel = "", ylabel = "") + p = profile_solvers(stats_subset, costs, costnames, xlabel = "", ylabel = "") fname = "this_commit_vs_reference_$(key)" savefig("$(fname).svg") push!(svgs, "$(fname).svg") content = read("$(fname).svg", String) files_dict["$(fname).svg"] = Dict("content" => content) + + @info "Creating tables for $key" tables *= "\n## This commit vs reference: $(key)\n\n" tables *= "### This commit\n\n\n" tables *= sprint(io -> pretty_stats(io, this_commit[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown)) From 420d48d03a16190a24cc8e880ac052cf3664e2a8 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Wed, 11 Mar 2026 15:44:00 -0400 Subject: [PATCH 38/60] add function for default values --- src/solver_benchmarks.jl | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index d9ff01b..4d51f70 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -4,8 +4,8 @@ function run_solver_benchmarks( reference_branch::AbstractString = "main", gist_url::Union{AbstractString, Nothing} = nothing, script = "benchmarks.jl", - profile_values = [(:elapsed_time, "CPU Time"), (:neval_obj, "# Objective Evals"), (:neval_grad, "# Gradient Evals")], - table_values = [(:name, "Name"), (:objective, "f(x)"), (:elapsed_time, "Time")] + profile_values = solver_benchmark_profile_values(), + table_values = solver_benchmark_table_values() ) update_gist = gist_url !== nothing @@ -121,6 +121,13 @@ function run_solver_benchmarks( return update_gist ? gist_url : new_gist_url end +function solver_benchmark_profile_values() + return [(:elapsed_time, "CPU Time"), (:neval_obj, "# Objective Evals"), (:neval_grad, "# Gradient Evals")] +end + +function solver_benchmark_table_values() + return [(:name, "Name"), (:objective, "f(x)"), (:elapsed_time, "Time")] +end # Runs a script at a commit on a repo and afterwards goes back # to the original commit / branch. From 0444f4280d1015a3736e088f3df0489303616b5b Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Wed, 11 Mar 2026 15:57:14 -0400 Subject: [PATCH 39/60] fix hdr_override values --- src/solver_benchmarks.jl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 4d51f70..c71ac59 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -52,7 +52,6 @@ function run_solver_benchmarks( costnames = [value[2] for value in profile_values] stats_columns = [value[1] for value in table_values] - hdr_override = [value[2] for value in table_values] tables = "# Solver Benchmarks Tables \n\n" if is_git @@ -70,9 +69,9 @@ function run_solver_benchmarks( @info "Creating tables for $key" tables *= "\n## This commit vs reference: $(key)\n\n" tables *= "### This commit\n\n\n" - tables *= sprint(io -> pretty_stats(io, this_commit[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown)) + tables *= sprint(io -> pretty_stats(io, this_commit[key][!, stats_columns], hdr_override = table_values, tf=tf_markdown)) tables *= "\n\n### Reference\n\n\n" - tables *= sprint(io -> pretty_stats(io, reference[key][!, stats_columns], hdr_override = hdr_override, tf=tf_markdown)) + tables *= sprint(io -> pretty_stats(io, reference[key][!, stats_columns], hdr_override = table_values, tf=tf_markdown)) else @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." end From c2b88c769f05af868608d5050efb6a76c5b826f3 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Wed, 11 Mar 2026 16:13:33 -0400 Subject: [PATCH 40/60] fix hdr_override --- src/solver_benchmarks.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index c71ac59..442ba6e 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -69,9 +69,9 @@ function run_solver_benchmarks( @info "Creating tables for $key" tables *= "\n## This commit vs reference: $(key)\n\n" tables *= "### This commit\n\n\n" - tables *= sprint(io -> pretty_stats(io, this_commit[key][!, stats_columns], hdr_override = table_values, tf=tf_markdown)) + tables *= sprint(io -> pretty_stats(io, this_commit[key][!, stats_columns], hdr_override = Dict(table_values), tf=tf_markdown)) tables *= "\n\n### Reference\n\n\n" - tables *= sprint(io -> pretty_stats(io, reference[key][!, stats_columns], hdr_override = table_values, tf=tf_markdown)) + tables *= sprint(io -> pretty_stats(io, reference[key][!, stats_columns], hdr_override = Dict(table_values), tf=tf_markdown)) else @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." end From 94b90185ebf19a4c06c8a763d87ee6a69ff9e5cd Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 12 Mar 2026 11:29:57 -0400 Subject: [PATCH 41/60] force recompilation of package when checking out --- src/solver_benchmarks.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 442ba6e..3acc433 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -78,7 +78,7 @@ function run_solver_benchmarks( end end - files_dict["TABLES.md"] = Dict("content" => tables) + files_dict["tables.md"] = Dict("content" => tables) @info "creating or updating gist" # json description of gist @@ -138,6 +138,8 @@ function _withcommit(script, repo, commit) branch = try LibGit2.branch(r) catch err; nothing end try LibGit2.checkout!(r, _shastring(r, commit)) + # Recompile package + Pkg.precompile() result = Base.include(Main, script) @assert result isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(result)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solvers function" catch err From 7c5c6a8e9b1e50d66f9b4c3cfebd9a3cde68e51a Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 12 Mar 2026 13:22:51 -0400 Subject: [PATCH 42/60] run result on main branch in separate julia process --- src/solver_benchmarks.jl | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 3acc433..6f4d29f 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -40,6 +40,9 @@ function run_solver_benchmarks( if is_git repo_dir = joinpath(bmark_dir, "..") repo = LibGit2.GitRepo(repo_dir) + println(bmark_dir) + println(script) + println(joinpath(bmark_dir, script)) reference = _withcommit(joinpath(bmark_dir, script), repo, reference_branch) end @@ -138,9 +141,14 @@ function _withcommit(script, repo, commit) branch = try LibGit2.branch(r) catch err; nothing end try LibGit2.checkout!(r, _shastring(r, commit)) - # Recompile package - Pkg.precompile() - result = Base.include(Main, script) + + env_to_use = dirname(Pkg.Types.Context().env.project_file) + exec_str = + """ + include($(repr(script))) + """ + run(`$(Base.julia_cmd()) --project=$env_to_use --depwarn=no -e $exec_str`) + @assert result isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(result)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solvers function" catch err rethrow(err) From 05ac796fb1ddba1441d107616e9abb9debd993ee Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 12 Mar 2026 13:41:57 -0400 Subject: [PATCH 43/60] write and read result from JSON --- src/solver_benchmarks.jl | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 6f4d29f..0fc685f 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -40,9 +40,6 @@ function run_solver_benchmarks( if is_git repo_dir = joinpath(bmark_dir, "..") repo = LibGit2.GitRepo(repo_dir) - println(bmark_dir) - println(script) - println(joinpath(bmark_dir, script)) reference = _withcommit(joinpath(bmark_dir, script), repo, reference_branch) end @@ -145,10 +142,13 @@ function _withcommit(script, repo, commit) env_to_use = dirname(Pkg.Types.Context().env.project_file) exec_str = """ - include($(repr(script))) + using JSOBenchmarks + JSOBenchmarks._run_local($repr(script), "temp_result.json") """ run(`$(Base.julia_cmd()) --project=$env_to_use --depwarn=no -e $exec_str`) + result = JSON.read(read("temp_result.json", String), Dict{Symbol, DataFrame}) + @assert result isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(result)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solvers function" catch err rethrow(err) @@ -163,6 +163,13 @@ function _withcommit(script, repo, commit) return result end +function _run_local(script, file_name) + include(script) + open(file_name, "w") do io + JSON.write(io, res) + end +end + function _shastring(r::LibGit2.GitRepo, targetname) branch = LibGit2.lookup_branch(r, targetname) branch = branch === nothing ? LibGit2.lookup_branch(r, targetname, true) : branch # Search remote as well if not found locally. From 9c21ce36692e67ea215e4f1046b401c37ce3fced Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 12 Mar 2026 13:47:47 -0400 Subject: [PATCH 44/60] fix typo --- src/solver_benchmarks.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 0fc685f..708c3cb 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -143,7 +143,7 @@ function _withcommit(script, repo, commit) exec_str = """ using JSOBenchmarks - JSOBenchmarks._run_local($repr(script), "temp_result.json") + JSOBenchmarks._run_local($(repr(script)), "temp_result.json") """ run(`$(Base.julia_cmd()) --project=$env_to_use --depwarn=no -e $exec_str`) From a7269f2430fd165a8dc92576d851443373389dfd Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 12 Mar 2026 13:54:35 -0400 Subject: [PATCH 45/60] run script in main env --- src/solver_benchmarks.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 708c3cb..335ee41 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -164,7 +164,7 @@ function _withcommit(script, repo, commit) end function _run_local(script, file_name) - include(script) + res = include(Main, script) open(file_name, "w") do io JSON.write(io, res) end From 970f9edbde0485aa6617e061162d33fa39b1e0d8 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 12 Mar 2026 13:59:44 -0400 Subject: [PATCH 46/60] run Base.include --- src/solver_benchmarks.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 335ee41..299dbad 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -164,7 +164,7 @@ function _withcommit(script, repo, commit) end function _run_local(script, file_name) - res = include(Main, script) + res = Base.include(Main, script) open(file_name, "w") do io JSON.write(io, res) end From 46ab9405d0e45e90ddfc2fd89fdd2c1fd280a0a4 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 12 Mar 2026 18:43:57 -0400 Subject: [PATCH 47/60] save result file as a jld2 --- src/solver_benchmarks.jl | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 299dbad..a9eba4e 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -147,7 +147,7 @@ function _withcommit(script, repo, commit) """ run(`$(Base.julia_cmd()) --project=$env_to_use --depwarn=no -e $exec_str`) - result = JSON.read(read("temp_result.json", String), Dict{Symbol, DataFrame}) + result = load("temp.jld2")["result"] @assert result isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(result)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solvers function" catch err @@ -164,10 +164,8 @@ function _withcommit(script, repo, commit) end function _run_local(script, file_name) - res = Base.include(Main, script) - open(file_name, "w") do io - JSON.write(io, res) - end + result = Base.include(Main, script) + @save "temp.jld2" result end function _shastring(r::LibGit2.GitRepo, targetname) From 8db47ed422b1291e4a2844f360124f543fddbad4 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 12 Mar 2026 19:30:54 -0400 Subject: [PATCH 48/60] add latex table and save results files as jld2 --- src/solver_benchmarks.jl | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index a9eba4e..461b6a9 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -34,13 +34,14 @@ function run_solver_benchmarks( # Run the benchmark script on this commit this_commit = Base.include(Main, joinpath(bmark_dir, script)) @assert this_commit isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(this_commit)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solvers function" + @save "$(bmarkname)_solver_benchmarks_this_commit.jld2" this_commit # Run the benchmark script on the reference branch local reference if is_git repo_dir = joinpath(bmark_dir, "..") repo = LibGit2.GitRepo(repo_dir) - reference = _withcommit(joinpath(bmark_dir, script), repo, reference_branch) + reference = _withcommit(joinpath(bmark_dir, script), repo, reference_branch, bmarkname = bmarkname) end # Plotting and tables @@ -54,6 +55,7 @@ function run_solver_benchmarks( stats_columns = [value[1] for value in table_values] tables = "# Solver Benchmarks Tables \n\n" + latex_tables = "# Solver Benchmarks Tables \n\n" if is_git for key in keys(this_commit) if haskey(reference, key) @@ -67,11 +69,17 @@ function run_solver_benchmarks( files_dict["$(fname).svg"] = Dict("content" => content) @info "Creating tables for $key" + # TODO: make a function to avoid code repetition here tables *= "\n## This commit vs reference: $(key)\n\n" + latex_tables *= "\n## This commit vs reference: $(key)\n\n" tables *= "### This commit\n\n\n" + latex_tables *= "### This commit\n\n\n" tables *= sprint(io -> pretty_stats(io, this_commit[key][!, stats_columns], hdr_override = Dict(table_values), tf=tf_markdown)) + latex_tables *= sprint(io -> pretty_latex_stats(io, this_commit[key][!, stats_columns], hdr_override = Dict(table_values), tf=tf_latex)) tables *= "\n\n### Reference\n\n\n" + latex_tables *= "\n\n### Reference\n\n\n" tables *= sprint(io -> pretty_stats(io, reference[key][!, stats_columns], hdr_override = Dict(table_values), tf=tf_markdown)) + latex_tables *= sprint(io -> pretty_latex_stats(io, reference[key][!, stats_columns], hdr_override = Dict(table_values), tf=tf_latex)) else @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." end @@ -79,6 +87,7 @@ function run_solver_benchmarks( end files_dict["tables.md"] = Dict("content" => tables) + @save "$(bmarkname)_solver_benchmarks_tables.txt" latex_tables @info "creating or updating gist" # json description of gist @@ -131,7 +140,7 @@ end # Runs a script at a commit on a repo and afterwards goes back # to the original commit / branch. # This code is based on https://github.com/JuliaCI/PkgBenchmark.jl/blob/master/src/util.jl -function _withcommit(script, repo, commit) +function _withcommit(script, repo, commit; bmarkname = "") original_commit = string(LibGit2.GitHash(LibGit2.GitObject(repo, "HEAD"))) local result LibGit2.transact(repo) do r @@ -140,14 +149,15 @@ function _withcommit(script, repo, commit) LibGit2.checkout!(r, _shastring(r, commit)) env_to_use = dirname(Pkg.Types.Context().env.project_file) + save_file_name = "$(bmarkname)_solver_benchmarks_$(commit)" exec_str = """ using JSOBenchmarks - JSOBenchmarks._run_local($(repr(script)), "temp_result.json") + JSOBenchmarks._run_local($(repr(script)), $(save_file_name)) """ run(`$(Base.julia_cmd()) --project=$env_to_use --depwarn=no -e $exec_str`) - result = load("temp.jld2")["result"] + result = load("$(save_file_name).jld2")["result"] @assert result isa Dict{Symbol, DataFrame} "Expected the benchmark script to return a Dict{Symbol, DataFrame}, but got $(typeof(result)). Make sure your benchmark script returns a dict resulting from BenchmarkSolver.bmark_solvers function" catch err @@ -163,9 +173,9 @@ function _withcommit(script, repo, commit) return result end -function _run_local(script, file_name) +function _run_local(script, save_file_name) result = Base.include(Main, script) - @save "temp.jld2" result + @save "$(save_file_name).jld2" result end function _shastring(r::LibGit2.GitRepo, targetname) From 4546cf76c334044ae2c162cdce4d97e799dd08c1 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 12 Mar 2026 19:40:41 -0400 Subject: [PATCH 49/60] save pdfs --- src/solver_benchmarks.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 461b6a9..7fd368b 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -64,6 +64,7 @@ function run_solver_benchmarks( p = profile_solvers(stats_subset, costs, costnames, xlabel = "", ylabel = "") fname = "this_commit_vs_reference_$(key)" savefig("$(fname).svg") + savefig("profiles_$(fname).pdf") push!(svgs, "$(fname).svg") content = read("$(fname).svg", String) files_dict["$(fname).svg"] = Dict("content" => content) From 732654f683bc28eeec558404dd2a4137d29f537a Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 12 Mar 2026 21:31:27 -0400 Subject: [PATCH 50/60] convert variable to string --- src/solver_benchmarks.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 7fd368b..281b30d 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -154,7 +154,7 @@ function _withcommit(script, repo, commit; bmarkname = "") exec_str = """ using JSOBenchmarks - JSOBenchmarks._run_local($(repr(script)), $(save_file_name)) + JSOBenchmarks._run_local($(repr(script)), "$(save_file_name)") """ run(`$(Base.julia_cmd()) --project=$env_to_use --depwarn=no -e $exec_str`) From 3c89a5c90bd8a8dc096f809269e20176c1815e13 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 12 Mar 2026 21:43:14 -0400 Subject: [PATCH 51/60] fix no such file or directory --- src/solver_benchmarks.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 281b30d..857fc75 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -150,7 +150,7 @@ function _withcommit(script, repo, commit; bmarkname = "") LibGit2.checkout!(r, _shastring(r, commit)) env_to_use = dirname(Pkg.Types.Context().env.project_file) - save_file_name = "$(bmarkname)_solver_benchmarks_$(commit)" + save_file_name = "$(bmarkname)_solver_benchmarks_reference" exec_str = """ using JSOBenchmarks From 63e8c6f55aaf4f3978d61d1cb25c9902cc042d42 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Thu, 12 Mar 2026 21:48:30 -0400 Subject: [PATCH 52/60] remove copilot spurios suggestions --- src/solver_benchmarks.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 857fc75..0c167fb 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -76,11 +76,11 @@ function run_solver_benchmarks( tables *= "### This commit\n\n\n" latex_tables *= "### This commit\n\n\n" tables *= sprint(io -> pretty_stats(io, this_commit[key][!, stats_columns], hdr_override = Dict(table_values), tf=tf_markdown)) - latex_tables *= sprint(io -> pretty_latex_stats(io, this_commit[key][!, stats_columns], hdr_override = Dict(table_values), tf=tf_latex)) + latex_tables *= sprint(io -> pretty_latex_stats(io, this_commit[key][!, stats_columns], hdr_override = Dict(table_values))) tables *= "\n\n### Reference\n\n\n" latex_tables *= "\n\n### Reference\n\n\n" tables *= sprint(io -> pretty_stats(io, reference[key][!, stats_columns], hdr_override = Dict(table_values), tf=tf_markdown)) - latex_tables *= sprint(io -> pretty_latex_stats(io, reference[key][!, stats_columns], hdr_override = Dict(table_values), tf=tf_latex)) + latex_tables *= sprint(io -> pretty_latex_stats(io, reference[key][!, stats_columns], hdr_override = Dict(table_values))) else @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." end From 57ed7fd9b9cf3fddeb8c98f3ce347ea6ec91a4ee Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 13 Mar 2026 10:26:09 -0400 Subject: [PATCH 53/60] save latex file as txt --- src/solver_benchmarks.jl | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 0c167fb..934c351 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -88,7 +88,9 @@ function run_solver_benchmarks( end files_dict["tables.md"] = Dict("content" => tables) - @save "$(bmarkname)_solver_benchmarks_tables.txt" latex_tables + open("$(bmarkname)_solver_benchmarks_tables.txt", "w") do io + println(io, latex_tables) + end @info "creating or updating gist" # json description of gist From 0701786a016ffe374ba45b94a41ba21c1aa3fd72 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 13 Mar 2026 15:14:15 -0400 Subject: [PATCH 54/60] export solver_benchmark options --- src/JSOBenchmarks.jl | 1 + src/solver_benchmarks.jl | 7 +++++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/JSOBenchmarks.jl b/src/JSOBenchmarks.jl index 6861fc9..adc4dbd 100644 --- a/src/JSOBenchmarks.jl +++ b/src/JSOBenchmarks.jl @@ -23,6 +23,7 @@ export run_benchmarks, run_solver_benchmarks export profile_solvers_from_pkgbmark export create_gist_from_json_dict, create_gist_from_json_file export update_gist_from_json_dict, update_gist_from_json_file +export solver_benchmark_profile_values, solver_benchmark_table_values export write_md const git = Git.git() diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 934c351..4822287 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -4,8 +4,6 @@ function run_solver_benchmarks( reference_branch::AbstractString = "main", gist_url::Union{AbstractString, Nothing} = nothing, script = "benchmarks.jl", - profile_values = solver_benchmark_profile_values(), - table_values = solver_benchmark_table_values() ) update_gist = gist_url !== nothing @@ -45,6 +43,11 @@ function run_solver_benchmarks( end # Plotting and tables + local profile_values, table_values + + profile_values = Main.solver_benchmark_profile_values() + table_values = Main.solver_benchmark_table_values() + files_dict = Dict{String, Any}() svgs = String[] From 7a1f03ede327e81c58f31f4a2efc13c3a631a70f Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 13 Mar 2026 15:26:58 -0400 Subject: [PATCH 55/60] save tex file for each table --- src/solver_benchmarks.jl | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 4822287..e876211 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -58,7 +58,6 @@ function run_solver_benchmarks( stats_columns = [value[1] for value in table_values] tables = "# Solver Benchmarks Tables \n\n" - latex_tables = "# Solver Benchmarks Tables \n\n" if is_git for key in keys(this_commit) if haskey(reference, key) @@ -73,17 +72,17 @@ function run_solver_benchmarks( files_dict["$(fname).svg"] = Dict("content" => content) @info "Creating tables for $key" - # TODO: make a function to avoid code repetition here tables *= "\n## This commit vs reference: $(key)\n\n" - latex_tables *= "\n## This commit vs reference: $(key)\n\n" tables *= "### This commit\n\n\n" - latex_tables *= "### This commit\n\n\n" tables *= sprint(io -> pretty_stats(io, this_commit[key][!, stats_columns], hdr_override = Dict(table_values), tf=tf_markdown)) - latex_tables *= sprint(io -> pretty_latex_stats(io, this_commit[key][!, stats_columns], hdr_override = Dict(table_values))) + open("this_commit_$(key).tex", "w") do io + pretty_latex_stats(io, this_commit[key][!, stats_columns], hdr_override = Dict(table_values)) + end tables *= "\n\n### Reference\n\n\n" - latex_tables *= "\n\n### Reference\n\n\n" tables *= sprint(io -> pretty_stats(io, reference[key][!, stats_columns], hdr_override = Dict(table_values), tf=tf_markdown)) - latex_tables *= sprint(io -> pretty_latex_stats(io, reference[key][!, stats_columns], hdr_override = Dict(table_values))) + open("reference_$(key).tex", "w") do io + pretty_latex_stats(io, reference[key][!, stats_columns], hdr_override = Dict(table_values)) + end else @warn "$(reference_branch) branch benchmarks do not run the solver $key. Please update the benchmark solver list in a separate PR and rebase." end @@ -91,9 +90,6 @@ function run_solver_benchmarks( end files_dict["tables.md"] = Dict("content" => tables) - open("$(bmarkname)_solver_benchmarks_tables.txt", "w") do io - println(io, latex_tables) - end @info "creating or updating gist" # json description of gist From 6e9eae2cf10a6cc2b2320083dee8eece68906141 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Fri, 13 Mar 2026 16:10:54 -0400 Subject: [PATCH 56/60] use Base.invokelatest to allow user to switch options --- src/solver_benchmarks.jl | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index e876211..39af73f 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -45,8 +45,8 @@ function run_solver_benchmarks( # Plotting and tables local profile_values, table_values - profile_values = Main.solver_benchmark_profile_values() - table_values = Main.solver_benchmark_table_values() + profile_values = Base.invokelatest(solver_benchmark_profile_values) + table_values = Base.invokelatest(solver_benchmark_table_values) files_dict = Dict{String, Any}() svgs = String[] From 7513b7581b4378b5b5396b55a23af6badcbbaf1e Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 16 Mar 2026 10:35:28 -0400 Subject: [PATCH 57/60] revert changes to simple md report --- src/JSOBenchmarks.jl | 24 ++++++------------------ src/solver_benchmarks.jl | 21 +++++++++++---------- 2 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/JSOBenchmarks.jl b/src/JSOBenchmarks.jl index adc4dbd..425fadb 100644 --- a/src/JSOBenchmarks.jl +++ b/src/JSOBenchmarks.jl @@ -314,24 +314,12 @@ function write_simple_md_report( open(fname, "w") do f println(f, "Gist: $(gist_url)\n") println(f, "Full results stored as artifacts\n") - - if !isempty(svgs) - write_md_svgs(f, "Overview", gist_url, svgs) - end - - if judgement !== nothing - write_md(f, "Judgement", judgement) - println(f, "
") - end - - if this_commit !== nothing - write_md(f, "this_commit", this_commit) - println(f, "
") - end - - if reference !== nothing - write_md(f, "Reference", reference) - end + write_md_svgs(f, "Overview", gist_url, svgs) + write_md(f, "Judgement", judgement) + println(f, "
") + write_md(f, "this_commit", this_commit) + println(f, "
") + write_md(f, "Reference", reference) end end diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 39af73f..4aba1af 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -116,16 +116,17 @@ function run_solver_benchmarks( new_gist_url = string(new_gist.html_url) end - @info "preparing simple Markdown report" - is_git && - write_simple_md_report( - "bmark_$(bmarkname).md", - nothing, - nothing, - nothing, - update_gist ? gist_url : new_gist_url, - svgs, - ) + # TODO: Update the markdown report instead! + # @info "preparing simple Markdown report" + # is_git && + # write_simple_md_report( + # "bmark_$(bmarkname).md", + # nothing, + # nothing, + # nothing, + # update_gist ? gist_url : new_gist_url, + # svgs, + # ) @info "finished" return update_gist ? gist_url : new_gist_url From 59f07a0b08e21085f4e46c081e81eed419d9ab88 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 16 Mar 2026 10:46:45 -0400 Subject: [PATCH 58/60] update simple md report --- src/solver_benchmarks.jl | 18 +++++++----------- 1 file changed, 7 insertions(+), 11 deletions(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index 4aba1af..d96f108 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -116,17 +116,13 @@ function run_solver_benchmarks( new_gist_url = string(new_gist.html_url) end - # TODO: Update the markdown report instead! - # @info "preparing simple Markdown report" - # is_git && - # write_simple_md_report( - # "bmark_$(bmarkname).md", - # nothing, - # nothing, - # nothing, - # update_gist ? gist_url : new_gist_url, - # svgs, - # ) + # Update markdown report + if is_git + fname = "bmark_$(bmarkname).md" + open(fname, "w") do f + write_md_svgs(f, "SolverBenchmark Profiles", gist_url, svgs) + end + end @info "finished" return update_gist ? gist_url : new_gist_url From cd479caa998b211e3a99166abceedefdcdafe9c2 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 16 Mar 2026 11:36:18 -0400 Subject: [PATCH 59/60] add doc --- src/solver_benchmarks.jl | 52 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index d96f108..d564a1a 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -1,3 +1,55 @@ +""" + run_solver_benchmarks(repo_name, bmark_dir; reference_branch="main", gist_url=nothing, script="benchmarks.jl") + +Run a benchmark script, based on the SolverBenchmarks.jl package, for a Julia repository. + +This function executes a benchmark script (`script`) in the specified benchmark directory (`bmark_dir`) for +the current state of the repository containing `repo_name`. +The output of the script should be a result of `BenchmarkSolver.bmark_solvers`. If the repository is a Git repository, the +benchmarks are run on the current commit and optionally compared to a reference branch (default `"main"`). +The results are saved as `.jld2` files, performance profile plots and summary tables are generated. +Optionally, results can be uploaded or updated in a GitHub Gist (`gist_url`). + +# Arguments + +- `repo_name::AbstractString` + The name of the Julia package repository being benchmarked. + +- `bmark_dir::AbstractString` + Path to the directory containing the benchmark scripts. This is usually a `benchmarks/` folder + inside the repository. + +# Keyword Arguments + +- `reference_branch::AbstractString = "main"` + The Git branch used as a reference for comparison in plots and tables. + +- `gist_url::Union{AbstractString, Nothing} = nothing` + If provided, the function updates the existing Gist at this URL. Otherwise, a new Gist is created. + +- `script::AbstractString = "benchmarks.jl"` + The Julia script in `bmark_dir` that runs the benchmark suite. Must return a `Dict{Symbol, DataFrame}` + as produced by `BenchmarkSolver.bmark_solvers`. + +# Output + +Returns a `String` containing the URL of the Gist with benchmark results. If `gist_url` was provided, +the existing Gist is updated; otherwise, a new Gist URL is returned. + +# Plots and Tables values + +In order to compare specific outputs from the benchmark results, the `script` can override the functions + JSOBenchmarks.solver_benchmark_profile_values() + JSOBenchmarks.solver_benchmark_table_values() +to specify which columns from the DataFrames should be used for the performance profiles and summary tables, respectively. +Both should return an array of pairs, where the first element is a `Symbol` representing the column name in the DataFrame +and the second element is a `String` representing the label to be used in the plots and tables. + +# Notes + +- This function is mostly expected to be called from a GitHub workflow. +- Please refer to `SolverBenchmarks.bmark_solvers` for more information on how to write the benchmark script. +""" function run_solver_benchmarks( repo_name::AbstractString, bmark_dir::AbstractString; From 306d837991badd689f2ae911a49c27f0c7c3b314 Mon Sep 17 00:00:00 2001 From: MaxenceGollier Date: Mon, 16 Mar 2026 13:03:25 -0400 Subject: [PATCH 60/60] update report --- src/solver_benchmarks.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver_benchmarks.jl b/src/solver_benchmarks.jl index d564a1a..e47c7dd 100644 --- a/src/solver_benchmarks.jl +++ b/src/solver_benchmarks.jl @@ -171,7 +171,7 @@ function run_solver_benchmarks( # Update markdown report if is_git fname = "bmark_$(bmarkname).md" - open(fname, "w") do f + open(fname, "a") do f write_md_svgs(f, "SolverBenchmark Profiles", gist_url, svgs) end end