From 4619d4c0c68619343c843c74da3ed41250d54e29 Mon Sep 17 00:00:00 2001 From: Ayla Croft Date: Tue, 20 Oct 2026 12:05:20 -0400 Subject: [PATCH] Name an arm64 CPU in the ML-DSA-87 bench, and print no p99.9 it cannot resolve The bench's first CI run on the box leg printed two things that were not what they said: - "machine: unknown (CPU part 0xd49)". arm64 Linux gives no model name in /proc/cpuinfo, only the implementer and part numbers, and the script fell back to "unknown". It now prints those numbers ("CPU implementer 0x41, CPU part 0xd49"). - At 500 rounds the p99.9 index is the last sample, so every p99.9 equalled max. A percentile is now printed only when a sample lies above it, and "-" otherwise; the box leg runs 2,000 rounds per size, the script's default, so its p99.9 is one. No figure changes meaning, and nothing in lib/ changes. Signed-off-by: Ayla Croft --- .github/workflows/ci.yml | 3 ++- tools/bench_mldsa.exs | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 5 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e0b221a..7a54a5b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -69,9 +69,10 @@ jobs: if: matrix.leg == 'box' run: mix run tools/mldsa_sample.exs mldsa87-sample # Recorded, not asserted: a shared runner's latency is the runner's, never a box's figure. + # 2,000 rounds per size, so the printed p99.9 is a percentile and not the maximum. - name: ML-DSA-87 latency on this runner (recorded, no threshold) if: matrix.leg == 'box' - run: mix run tools/bench_mldsa.exs 500 + run: mix run tools/bench_mldsa.exs 2000 - if: matrix.leg == 'box' uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2 with: diff --git a/tools/bench_mldsa.exs b/tools/bench_mldsa.exs index c7fd01d..bbb3f98 100644 --- a/tools/bench_mldsa.exs +++ b/tools/bench_mldsa.exs @@ -38,9 +38,12 @@ cpu = case File.read("/proc/cpuinfo") do {:ok, info} -> fields = for line <- String.split(info, "\n"), [k, v] <- [String.split(line, ":", parts: 2)], do: {String.trim(k), String.trim(v)} - model = Enum.find_value(fields, "unknown", fn {k, v} -> k in ["model name", "Model"] and v end) - part = Enum.find_value(fields, nil, fn {k, v} -> k == "CPU part" and v end) - "#{model}#{if part, do: " (CPU part #{part})", else: ""}, #{System.schedulers_online()} schedulers" + get = fn key -> Enum.find_value(fields, fn {k, v} -> k == key and v end) end + # x86_64 names its model; arm64 Linux gives only the implementer and part numbers (on + # GitHub's arm64 runner, 0x41 and 0xd49: Arm, Neoverse N2), so those are what is printed. + model = get.("model name") || get.("Model") || "CPU implementer #{get.("CPU implementer") || "?"}" + part = get.("CPU part") + "#{model}#{if part, do: ", CPU part #{part}", else: ""}; #{System.schedulers_online()} schedulers" _ -> "unknown" @@ -50,7 +53,13 @@ cpu = IO.puts("machine: #{cpu}; #{:erlang.system_info(:system_architecture)}") IO.puts("runtime: OTP #{:erlang.system_info(:otp_release)}, #{openssl}; #{rounds} rounds per size") -percentile = fn sorted, p -> Enum.at(sorted, min(length(sorted) - 1, trunc(p * length(sorted)))) end +# The p-th percentile is the sample at index trunc(p * rounds), printed only when some sample lies +# above it: at 1,000 rounds or fewer the p99.9 index is the last sample, which is max, not p99.9, +# so it prints "-". +percentile = fn sorted, p -> + i = trunc(p * length(sorted)) + if i < length(sorted) - 1, do: Enum.at(sorted, i), else: "-" +end measure = fn f -> for _ <- 1..rounds do