[libcxx-commits] [libcxx] [llvm] [libc++] Request refinement commits from the benchmark cron job (PR #234739)
Louis Dionne via libcxx-commits
libcxx-commits at lists.llvm.org
Thu Oct 22 15:13:30 PDT 2026
https://github.com/ldionne updated https://github.com/llvm/llvm-project/pull/234739
>From 2e370651badf70ea015e62e77d64c7dcf66e2424 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 22 Oct 2026 15:56:58 -0400
Subject: [PATCH 1/3] [libc++] Request refinement commits from the benchmark
cron job
This closes the loop for automatic refinement of benchmark data.
It adds a CI job that determines which commits to bisect based on
the changepoints we find in the existing data, and incorporates them
into the set of commits to measure.
The result is that we automatically bisect commits between which
a significant changepoint is detected.
Assisted by Claude
---
.github/workflows/libcxx-benchmark-cron.yml | 127 ++++++++++-------
.../workflows/libcxx-lnt-anchor-commits.yml | 97 +++++++++++++
.../workflows/libcxx-lnt-refine-commits.yml | 129 ++++++++++++++++++
libcxx/utils/ci/lnt/README.md | 4 +-
libcxx/utils/ci/lnt/machines.json | 9 +-
5 files changed, 313 insertions(+), 53 deletions(-)
create mode 100644 .github/workflows/libcxx-lnt-anchor-commits.yml
create mode 100644 .github/workflows/libcxx-lnt-refine-commits.yml
diff --git a/.github/workflows/libcxx-benchmark-cron.yml b/.github/workflows/libcxx-benchmark-cron.yml
index c59135d8368875..8cf066c1bd8e15 100644
--- a/.github/workflows/libcxx-benchmark-cron.yml
+++ b/.github/workflows/libcxx-benchmark-cron.yml
@@ -1,8 +1,11 @@
# This file defines a workflow that periodically requests benchmark runs for the commits
# that should have historical performance data but don't yet.
#
+# The commits that should be benchmarked are the anchor commits, and on machines that enable it,
+# commits selected to bisect the most significant changes found in the data gathered so far.
+#
# The workflow keeps no state: on every invocation, the commits that should be benchmarked
-# are recomputed from Git and the ones that have been benchmarked are recomputed from LNT
+# are recomputed from Git and LNT and the ones that have been benchmarked are recomputed from LNT
# and from the Github Actions API. It then dispatches libcxx-lnt-pipeline.yml for the
# difference, within a budget. This makes the system converge towards having data for all
# the desired commits, without necessarily ever reaching that state (as new commits land).
@@ -38,6 +41,7 @@ jobs:
runs-on: ubuntu-26.04
outputs:
matrix: ${{ steps.select.outputs.matrix }}
+ refine-matrix: ${{ steps.select.outputs.refine-matrix }}
steps:
- name: Checkout the machine definitions
uses: actions/checkout at 3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
@@ -60,6 +64,48 @@ jobs:
machine: cfg['lnt-machine'],
...cfg.coverage,
}))));
+ // Among those, we only refine commits for machines with a `refinement` key.
+ core.setOutput('refine-matrix', JSON.stringify(tracked.filter(cfg => cfg.coverage.refinement).map(cfg => ({
+ machine: cfg['lnt-machine'],
+ ...cfg.coverage.refinement,
+ }))));
+
+ # Select the anchor commits of each machine.
+ anchors:
+ permissions:
+ contents: read
+ needs:
+ - select-machines
+ strategy:
+ matrix:
+ include: ${{ fromJSON(needs.select-machines.outputs.matrix) }}
+ fail-fast: false
+ name: "Anchors ${{ matrix.machine }}"
+ uses: $/.github/workflows/libcxx-lnt-anchor-commits.yml
+ with:
+ since: ${{ matrix.since }}
+ every: ${{ matrix.every }}
+ artifact-name: anchors-${{ matrix.machine }}
+
+ # Select the commits to refine for each machine.
+ refine:
+ permissions:
+ contents: read
+ needs:
+ - select-machines
+ if: ${{ needs.select-machines.outputs.refine-matrix != '[]' }}
+ strategy:
+ matrix:
+ include: ${{ fromJSON(needs.select-machines.outputs.refine-matrix) }}
+ fail-fast: false
+ name: "Refine ${{ matrix.machine }}"
+ uses: $/.github/workflows/libcxx-lnt-refine-commits.yml
+ with:
+ lnt-machine: ${{ matrix.machine }}
+ metric: ${{ matrix.metric }}
+ direction: ${{ matrix.direction }}
+ max-commits: ${{ matrix.max-commits }}
+ artifact-name: refinement-${{ matrix.machine }}
request-runs:
permissions:
@@ -67,6 +113,12 @@ jobs:
actions: write # to dispatch libcxx-lnt-pipeline.yml
needs:
- select-machines
+ - anchors
+ - refine
+ # Never prevent the anchor commits from being requested, even if selecting refinement commits failed
+ # or there was nothing to refine. This also runs when selecting the anchor commits of some machine
+ # failed, so that the other machines are unaffected.
+ if: ${{ !cancelled() && needs.select-machines.result == 'success' }}
strategy:
matrix:
include: ${{ fromJSON(needs.select-machines.outputs.matrix) }}
@@ -76,44 +128,14 @@ jobs:
timeout-minutes: 30
env:
MACHINE: ${{ matrix.machine }}
- SINCE: ${{ matrix.since }}
- EVERY: ${{ matrix.every }}
SAMPLES: ${{ matrix.samples }}
MAX_IN_FLIGHT: ${{ matrix.max-in-flight }}
- # Keep in sync with libcxx-lnt-submit.yml
+ # Keep in sync with libcxx-lnt-submit.yml and libcxx-lnt-refine-commits.yml
LNT_URL: https://lnt.llvm.org
DRY_RUN: ${{ inputs.dry-run || 'false' }}
steps:
- # Anchor commits are stable through time (except for the single new anchor commit every
- # day/week/month depending on the granularity). Since computing anchor commits requires
- # cloning the whole monorepo (which is expensive) and this job runs regularly, we cache
- # anchor commits and avoid recomputing them unless necessary.
- #
- # Since we currently use weekly granularity for anchor commits on most LNT machines, we
- # cache them and re-generate them only daily.
- - name: Compute the cache key for anchor commits
- id: anchor-key
- run: echo "key=libcxx-benchmark-cron-anchors-${MACHINE}-${SINCE}-${EVERY}-$(date -u +%F)" >> "${GITHUB_OUTPUT}"
-
- - name: Try restoring anchor commits for ${{ matrix.machine }} from cache
- id: restore-anchors
- uses: actions/cache/restore at 55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
- with:
- path: ${{ runner.temp }}/anchors.txt # restore to temp since the checkout below would overwrite it
- key: ${{ steps.anchor-key.outputs.key }}
-
- - name: Checkout the full LLVM monorepo
- if: ${{ steps.restore-anchors.outputs.cache-hit != 'true' }}
- uses: actions/checkout at 3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- with:
- persist-credentials: false
- # Selecting anchor commits requires full Git history, but not the blob content.
- fetch-depth: 0
- filter: blob:none
-
- - name: Checkout sparse LLVM monorepo
- if: ${{ steps.restore-anchors.outputs.cache-hit == 'true' }}
+ - name: Checkout the LNT tooling
uses: actions/checkout at 3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
@@ -129,26 +151,33 @@ jobs:
- name: Install dependencies
run: pip install -r libcxx/utils/ci/lnt/requirements.txt
- - name: Determine anchor commits
- if: ${{ steps.restore-anchors.outputs.cache-hit != 'true' }}
- run: |
- libcxx/utils/ci/lnt/select-anchor-commits --since "${SINCE}" --every "${EVERY}" --output anchors.txt
- # Anchor commits go from oldest to newest. Reverse them to prioritize newer commits first.
- tac anchors.txt > "${RUNNER_TEMP}/anchors.txt"
-
- - name: Cache anchor commits
- if: ${{ steps.restore-anchors.outputs.cache-hit != 'true' }}
- uses: actions/cache/save at 55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
+ # The artifact is missing if selecting the anchor commits failed, in which case this job fails too.
+ - name: Download anchor commits for ${{ matrix.machine }}
+ uses: actions/download-artifact at 9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
- path: ${{ runner.temp }}/anchors.txt
- key: ${{ steps.anchor-key.outputs.key }}
+ name: anchors-${{ matrix.machine }}
+ path: ${{ runner.temp }}/anchors
+
+ # The artifact is missing if selecting refinement commits failed, in which case we only request the
+ # anchor commits.
+ - name: Download refinement commits for ${{ matrix.machine }}
+ if: ${{ matrix.refinement }}
+ continue-on-error: true
+ uses: actions/download-artifact at 9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
+ with:
+ name: refinement-${{ matrix.machine }}
+ path: ${{ runner.temp }}/refinement
+ # Anchor commits come first, then the commits selected to refine changes.
- name: Determine which commits are missing from LNT
run: |
- libcxx/utils/ci/lnt/plan-benchmarks --commit-list "${RUNNER_TEMP}/anchors.txt" \
- --lnt-url "${LNT_URL}" --test-suite libcxx \
- --machine "${MACHINE}" --samples "${SAMPLES}" \
- --output plan.jsonl
+ # Anchor commits go from oldest to newest. Reverse them to prioritize newer commits first.
+ tac "${RUNNER_TEMP}/anchors/anchors.txt" > commits.txt
+ if [ -f "${RUNNER_TEMP}/refinement/refinement.txt" ]; then
+ cat "${RUNNER_TEMP}/refinement/refinement.txt" >> commits.txt
+ fi
+ libcxx/utils/ci/lnt/plan-benchmarks --commit-list commits.txt --lnt-url "${LNT_URL}" --test-suite libcxx \
+ --machine "${MACHINE}" --samples "${SAMPLES}" --output plan.jsonl
- name: Request the missing runs
env:
diff --git a/.github/workflows/libcxx-lnt-anchor-commits.yml b/.github/workflows/libcxx-lnt-anchor-commits.yml
new file mode 100644
index 00000000000000..8c4b8210a86cae
--- /dev/null
+++ b/.github/workflows/libcxx-lnt-anchor-commits.yml
@@ -0,0 +1,97 @@
+# This file defines a reusable workflow that selects the anchor commits of a LNT pipeline. Those
+# are the commits that should have historical benchmark data based on a fixed calendar interval.
+#
+# The selected commits are uploaded as an artifact containing a single `anchors.txt` file, with
+# one commit per line, oldest first.
+#
+# See libcxx/utils/ci/lnt/README.md for a description of the tools used here.
+
+name: "[libc++] Select anchor commits"
+
+permissions:
+ contents: read
+
+on:
+ workflow_call:
+ inputs:
+ since:
+ description: 'The date (YYYY-MM-DD) to select anchor commits from'
+ required: true
+ type: string
+ every:
+ description: 'The calendar granularity at which to select anchor commits (day, week or month)'
+ required: true
+ type: string
+ artifact-name:
+ description: 'The name of the artifact to upload the result as'
+ required: true
+ type: string
+
+jobs:
+ select:
+ name: Select anchor commits
+ runs-on: ubuntu-26.04
+ timeout-minutes: 30
+ defaults:
+ run:
+ shell: bash # for pipefail, which the default shell doesn't set
+ env:
+ SINCE: ${{ inputs.since }}
+ EVERY: ${{ inputs.every }}
+ steps:
+ # Anchor commits are stable through time (except for the single new anchor commit every
+ # day/week/month depending on the granularity). Since computing anchor commits requires
+ # cloning the whole monorepo (which is expensive) and this workflow is called regularly,
+ # we cache anchor commits and avoid recomputing them unless necessary.
+ #
+ # Since we currently use weekly granularity for anchor commits on most LNT machines, we
+ # cache them and re-generate them only daily.
+ - name: Compute the cache key
+ id: key
+ run: echo "key=lnt-anchor-commits-${SINCE}-${EVERY}-$(date -u +%F)" >> "${GITHUB_OUTPUT}"
+
+ - name: Try restoring the selection from cache
+ id: restore
+ uses: actions/cache/restore at 55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
+ with:
+ path: ${{ runner.temp }}/anchors.txt # restore to temp since the checkout below would overwrite it
+ key: ${{ steps.key.outputs.key }}
+
+ - name: Checkout the full LLVM monorepo
+ if: ${{ steps.restore.outputs.cache-hit != 'true' }}
+ uses: actions/checkout at 3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+ with:
+ persist-credentials: false
+ # Selecting anchor commits requires full Git history, but not the blob content.
+ fetch-depth: 0
+ filter: blob:none
+
+ - name: Install Python
+ if: ${{ steps.restore.outputs.cache-hit != 'true' }}
+ uses: actions/setup-python at 5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
+ with:
+ python-version: '3.14'
+ cache: pip
+ cache-dependency-path: libcxx/utils/ci/lnt/requirements.txt
+
+ - name: Install dependencies
+ if: ${{ steps.restore.outputs.cache-hit != 'true' }}
+ run: pip install -r libcxx/utils/ci/lnt/requirements.txt
+
+ - name: Select anchor commits
+ if: ${{ steps.restore.outputs.cache-hit != 'true' }}
+ run: libcxx/utils/ci/lnt/select-anchor-commits --since "${SINCE}" --every "${EVERY}" --output "${RUNNER_TEMP}/anchors.txt"
+
+ - name: Cache the selection
+ if: ${{ steps.restore.outputs.cache-hit != 'true' }}
+ uses: actions/cache/save at 55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
+ with:
+ path: ${{ runner.temp }}/anchors.txt
+ key: ${{ steps.key.outputs.key }}
+
+ - name: Upload the selection
+ uses: actions/upload-artifact at cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
+ with:
+ name: ${{ inputs.artifact-name }}
+ path: ${{ runner.temp }}/anchors.txt
+ if-no-files-found: error
diff --git a/.github/workflows/libcxx-lnt-refine-commits.yml b/.github/workflows/libcxx-lnt-refine-commits.yml
new file mode 100644
index 00000000000000..619adc7811f42f
--- /dev/null
+++ b/.github/workflows/libcxx-lnt-refine-commits.yml
@@ -0,0 +1,129 @@
+# This file defines a reusable workflow that selects the commits to benchmark on a LNT machine in order
+# to bisect the most significant changes found in the data gathered so far on that machine.
+#
+# The selected commits are uploaded as an artifact containing a single `refinement.txt` file, with one
+# commit per line, best first. That file is empty when there is nothing to refine.
+#
+# See libcxx/utils/ci/lnt/README.md for a description of the tools used here.
+
+name: "[libc++] Select refinement commits"
+
+permissions:
+ contents: read
+
+on:
+ workflow_call:
+ inputs:
+ lnt-machine:
+ description: 'The LNT machine whose data to refine'
+ required: true
+ type: string
+ metric:
+ description: 'The metric to look for changes in'
+ required: true
+ type: string
+ direction:
+ description: 'The direction of the changes to refine (increase, decrease or both)'
+ required: true
+ type: string
+ max-commits:
+ description: 'The maximum number of commits to select'
+ required: true
+ type: number
+ artifact-name:
+ description: 'The name of the artifact to upload the result as'
+ required: true
+ type: string
+
+jobs:
+ select:
+ name: Select refinement commits for ${{ inputs.lnt-machine }}
+ runs-on: ubuntu-26.04
+ timeout-minutes: 30
+ defaults:
+ run:
+ shell: bash # for pipefail, which the default shell doesn't set
+ env:
+ MACHINE: ${{ inputs.lnt-machine }}
+ METRIC: ${{ inputs.metric }}
+ DIRECTION: ${{ inputs.direction }}
+ MAX_COMMITS: ${{ inputs.max-commits }}
+ # Keep in sync with libcxx-lnt-submit.yml and libcxx-benchmark-cron.yml
+ LNT_URL: https://lnt.llvm.org
+ steps:
+ # The selection only changes as new runs are submitted to LNT, and computing it takes several minutes.
+ # It is cached and recomputed every few hours instead of every time this workflow is called.
+ - name: Compute the cache key
+ id: key
+ run: echo "key=lnt-refinement-commits-${MACHINE}-${METRIC}-${DIRECTION}-${MAX_COMMITS}-$(date -u +%F)-$((10#$(date -u +%H) / 3))" >> "${GITHUB_OUTPUT}"
+
+ - name: Try restoring the selection from cache
+ id: restore
+ uses: actions/cache/restore at 55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
+ with:
+ path: ${{ runner.temp }}/refinement.txt # restore to temp since the checkout below would overwrite it
+ key: ${{ steps.key.outputs.key }}
+
+ - name: Checkout the full LLVM monorepo
+ if: ${{ steps.restore.outputs.cache-hit != 'true' }}
+ uses: actions/checkout at 3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
+ with:
+ persist-credentials: false
+ # Selecting commits requires full Git history, but not the blob content.
+ fetch-depth: 0
+ filter: blob:none
+
+ - name: Install Python
+ if: ${{ steps.restore.outputs.cache-hit != 'true' }}
+ uses: actions/setup-python at 5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0
+ with:
+ python-version: '3.14'
+ cache: pip
+ cache-dependency-path: libcxx/utils/ci/lnt/requirements.txt
+
+ - name: Install dependencies
+ if: ${{ steps.restore.outputs.cache-hit != 'true' }}
+ run: pip install -r libcxx/utils/ci/lnt/requirements.txt
+
+ - name: Download the data from LNT
+ if: ${{ steps.restore.outputs.cache-hit != 'true' }}
+ run: |
+ # Download the data and the runs as of a fixed time point. That way, the commits that we consider
+ # "attempted" are exactly those used to find changepoints.
+ as_of=$(date -u -d '5 minutes ago' +%FT%TZ)
+ curl -sS --fail --compressed -G "${LNT_URL}/api/suites/libcxx/series" \
+ --data-urlencode machine="${MACHINE}" --data-urlencode metric="${METRIC}" \
+ --data-urlencode has_ordinal=true --data-urlencode submitted_before="${as_of}" -o series.jsonl
+
+ curl -sS --fail -G "${LNT_URL}/api/suites/libcxx/runs" \
+ --data-urlencode machine="${MACHINE}" --data-urlencode submitted_before="${as_of}" \
+ --data-urlencode limit=10000 \
+ | jq -r 'if .cursor.next then error("more runs than fit in a page") else .items[].commit end' \
+ | sort -u > attempted.txt
+
+ - name: Detect changepoints
+ if: ${{ steps.restore.outputs.cache-hit != 'true' }}
+ run: |
+ libcxx/utils/ci/lnt/detect-changepoints --input series.jsonl --series-by test --aggregate median \
+ --jobs "$(nproc)" -v --output changes.jsonl
+
+ - name: Select refinement commits
+ if: ${{ steps.restore.outputs.cache-hit != 'true' }}
+ run: |
+ libcxx/utils/ci/lnt/select-refinement-commits --changes changes.jsonl --attempted-commits attempted.txt \
+ --direction "${DIRECTION}" --max-commits "${MAX_COMMITS}" \
+ -v --output "${RUNNER_TEMP}/refinement.txt"
+
+ - name: Cache the selection
+ if: ${{ steps.restore.outputs.cache-hit != 'true' }}
+ uses: actions/cache/save at 55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
+ with:
+ path: ${{ runner.temp }}/refinement.txt
+ key: ${{ steps.key.outputs.key }}
+
+ - name: Upload the selection
+ uses: actions/upload-artifact at cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
+ with:
+ name: ${{ inputs.artifact-name }}
+ path: ${{ runner.temp }}/refinement.txt
+ if-no-files-found: error
diff --git a/libcxx/utils/ci/lnt/README.md b/libcxx/utils/ci/lnt/README.md
index 1ffa74a079f631..299abaafe7e7db 100644
--- a/libcxx/utils/ci/lnt/README.md
+++ b/libcxx/utils/ci/lnt/README.md
@@ -98,7 +98,9 @@ entries:
The `coverage` key establishes how far back and at which frequency performance should be measured
for that configuration. A machine without a `coverage` entry can be defined, but it won't result
-in historical data.
+in historical data. Within it, the optional `refinement` key enables bisecting the changes found in
+that data: it gives the `metric` to use for finding changepoints, the `direction` of the changes to
+bisect (`increase`, `decrease` or `both`) and the number of commits (`max-commits`) to select at a time.
The `pr-benchmarks` key determines whether the machine is used when benchmarking pull requests
with `/libcxx-bot benchmark`.
diff --git a/libcxx/utils/ci/lnt/machines.json b/libcxx/utils/ci/lnt/machines.json
index d1586b9c2cbe15..3b89c0f51c41dc 100644
--- a/libcxx/utils/ci/lnt/machines.json
+++ b/libcxx/utils/ci/lnt/machines.json
@@ -15,7 +15,8 @@
"since": "2023-01-01",
"every": "week",
"samples": 3,
- "max-in-flight": 4
+ "max-in-flight": 4,
+ "refinement": {"metric": "execution_time", "direction": "increase", "max-commits": 4}
}
},
{
@@ -34,7 +35,8 @@
"since": "2023-12-01",
"every": "week",
"samples": 3,
- "max-in-flight": 4
+ "max-in-flight": 4,
+ "refinement": {"metric": "execution_time", "direction": "increase", "max-commits": 4}
}
},
{
@@ -53,7 +55,8 @@
"since": "2023-01-01",
"every": "week",
"samples": 3,
- "max-in-flight": 8
+ "max-in-flight": 8,
+ "refinement": {"metric": "execution_time", "direction": "increase", "max-commits": 4}
}
},
{
>From 6c2c09f84d0a4e403ea2abc990511fcd83eea05d Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 22 Oct 2026 17:36:16 -0400
Subject: [PATCH 2/3] [libc++] Download the series of a machine a few tests at
a time
Downloading the whole series of a machine in a single request currently makes
LNT time out (it answers 503 after about 3 minutes), since the response is over
500 MB of uncompressed data.
Until that is fixed on the LNT side, download the series 50 tests at a time,
which keeps every request fast and its URL below the ~8 KB that LNT accepts.
Locally, this downloads the whole series of a machine in about 100 seconds.
Assisted by Claude
---
.github/workflows/libcxx-lnt-refine-commits.yml | 16 ++++++++++++++--
1 file changed, 14 insertions(+), 2 deletions(-)
diff --git a/.github/workflows/libcxx-lnt-refine-commits.yml b/.github/workflows/libcxx-lnt-refine-commits.yml
index 619adc7811f42f..d037c6d4073409 100644
--- a/.github/workflows/libcxx-lnt-refine-commits.yml
+++ b/.github/workflows/libcxx-lnt-refine-commits.yml
@@ -91,9 +91,21 @@ jobs:
# Download the data and the runs as of a fixed time point. That way, the commits that we consider
# "attempted" are exactly those used to find changepoints.
as_of=$(date -u -d '5 minutes ago' +%FT%TZ)
- curl -sS --fail --compressed -G "${LNT_URL}/api/suites/libcxx/series" \
+
+ # TODO: Download the series in a single request once LNT can serve it before timing out.
+ # In the meantime, we download the series a few tests at a time. The tests are passed in the URL,
+ # which LNT limits to ~8KB: 50 tests per request keeps us below that.
+ curl -sS --fail --compressed -G "${LNT_URL}/api/suites/libcxx/tests" \
--data-urlencode machine="${MACHINE}" --data-urlencode metric="${METRIC}" \
- --data-urlencode has_ordinal=true --data-urlencode submitted_before="${as_of}" -o series.jsonl
+ --data-urlencode limit=10000 \
+ | jq -r 'if .cursor.next then error("more tests than fit in a page") else [.items[].name] end
+ | range(0; length; 50) as $i | .[$i:$i + 50] | map("test=" + @uri) | join("&")' \
+ > queries.txt
+ while read -r query; do
+ curl -sS --fail --compressed -G "${LNT_URL}/api/suites/libcxx/series?${query}" \
+ --data-urlencode machine="${MACHINE}" --data-urlencode metric="${METRIC}" \
+ --data-urlencode has_ordinal=true --data-urlencode submitted_before="${as_of}"
+ done < queries.txt > series.jsonl
curl -sS --fail -G "${LNT_URL}/api/suites/libcxx/runs" \
--data-urlencode machine="${MACHINE}" --data-urlencode submitted_before="${as_of}" \
>From 451c01ee34b59ffd0660aa7648be0fd8d98d0df9 Mon Sep 17 00:00:00 2001
From: Louis Dionne <ldionne.2 at gmail.com>
Date: Thu, 22 Oct 2026 18:13:08 -0400
Subject: [PATCH 3/3] Revert "[libc++] Download the series of a machine a few
tests at a time"
This reverts commit 6c2c09f84d0a4e403ea2abc990511fcd83eea05d.
---
.github/workflows/libcxx-lnt-refine-commits.yml | 16 ++--------------
1 file changed, 2 insertions(+), 14 deletions(-)
diff --git a/.github/workflows/libcxx-lnt-refine-commits.yml b/.github/workflows/libcxx-lnt-refine-commits.yml
index d037c6d4073409..619adc7811f42f 100644
--- a/.github/workflows/libcxx-lnt-refine-commits.yml
+++ b/.github/workflows/libcxx-lnt-refine-commits.yml
@@ -91,21 +91,9 @@ jobs:
# Download the data and the runs as of a fixed time point. That way, the commits that we consider
# "attempted" are exactly those used to find changepoints.
as_of=$(date -u -d '5 minutes ago' +%FT%TZ)
-
- # TODO: Download the series in a single request once LNT can serve it before timing out.
- # In the meantime, we download the series a few tests at a time. The tests are passed in the URL,
- # which LNT limits to ~8KB: 50 tests per request keeps us below that.
- curl -sS --fail --compressed -G "${LNT_URL}/api/suites/libcxx/tests" \
+ curl -sS --fail --compressed -G "${LNT_URL}/api/suites/libcxx/series" \
--data-urlencode machine="${MACHINE}" --data-urlencode metric="${METRIC}" \
- --data-urlencode limit=10000 \
- | jq -r 'if .cursor.next then error("more tests than fit in a page") else [.items[].name] end
- | range(0; length; 50) as $i | .[$i:$i + 50] | map("test=" + @uri) | join("&")' \
- > queries.txt
- while read -r query; do
- curl -sS --fail --compressed -G "${LNT_URL}/api/suites/libcxx/series?${query}" \
- --data-urlencode machine="${MACHINE}" --data-urlencode metric="${METRIC}" \
- --data-urlencode has_ordinal=true --data-urlencode submitted_before="${as_of}"
- done < queries.txt > series.jsonl
+ --data-urlencode has_ordinal=true --data-urlencode submitted_before="${as_of}" -o series.jsonl
curl -sS --fail -G "${LNT_URL}/api/suites/libcxx/runs" \
--data-urlencode machine="${MACHINE}" --data-urlencode submitted_before="${as_of}" \
More information about the libcxx-commits
mailing list