From a2a9b0d410555fd45daf152fe76612d50d6f29ce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 09:47:24 +0100 Subject: [PATCH 01/17] chore: allow to benchmark against main --- .github/workflows/performance_score_director.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index 5b75fe11..cda2bbf2 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -21,15 +21,15 @@ on: default: '25' required: true baseline: - description: 'Timefold Solver release' - default: '1.27.0' + description: 'Baseline ref' + default: 'v1.27.0' required: true jdk_branch: description: 'JDK version' default: '25' required: true branch: - description: 'Branch to benchmark (needs to use 999-SNAPSHOT)' + description: 'Ref to benchmark (needs to use 999-SNAPSHOT)' default: 'main' required: true branch_owner: From 0aa53880c0dfa0475f7d27e7c08e3e72843b1957 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 10:06:28 +0100 Subject: [PATCH 02/17] Attempt --- .../workflows/performance_score_director.yml | 58 ++++++++++++++++++- 1 file changed, 57 insertions(+), 1 deletion(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index cda2bbf2..cf01adc7 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -40,7 +40,32 @@ on: run-name: "Timefold Solver v${{ github.event.inputs.baseline }} vs. ${{ github.event.inputs.branch_owner }}/${{ github.event.inputs.branch }} (Java ${{ github.event.inputs.jdk_baseline }} vs. ${{ github.event.inputs.jdk_branch }})" jobs: + decisions: + runs-on: ubuntu-latest + outputs: + baseline_solver_version: ${{ steps.step1.outputs.version }} + needs_snapshot_built: ${{ steps.step1.outputs.needs_snapshot_built }} + steps: + - name: Determine the baseline + id: step1 + working-directory: ./timefold-solver-benchmarks + shell: bash + run: | + if [[ "${{ github.event.inputs.baseline }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + VERSION="${BASH_REMATCH[0]}" + NEEDS_SNAPSHOT_BUILT=false + echo "Baseline is a release tag." + else + # We're testing against a tag. + VERSION="999-SNAPSHOT" + NEEDS_SNAPSHOT_BUILT=true + echo "Baseline is a random branch." + fi + + echo "version=$BASELINE_VERSION" >> "$GITHUB_OUTPUT" + echo "needs_snapshot_built=$NEEDS_SNAPSHOT_BUILT" >> "$GITHUB_OUTPUT" build: + needs: decisions runs-on: ubuntu-latest # Leverage massive parallelization of Github-hosted runners. strategy: fail-fast: true # If one compilation fails, abort everything. @@ -67,11 +92,42 @@ jobs: server-username: 'MVN_USERNAME' server-password: 'MVN_PASSWORD' + # Only build the snapshots if determined by the decisions job. + - name: (SUT) Checkout timefold-solver + uses: actions/checkout@v5 + with: + repository: ${{ github.event.inputs.branch_owner }}/timefold-solver + ref: ${{ github.event.inputs.baseline }} + path: ./timefold-solver + - name: (Baseline) Quickly build timefold-solver + if: needs.decisions.outputs.needs_snapshot_built == 'true' + working-directory: ./timefold-solver + shell: bash + run: ./mvnw -B -Dquickly clean install + - name: (Baseline) Checkout timefold-solver-enterprise + if: needs.decisions.outputs.needs_snapshot_built == 'true' + uses: actions/checkout@v5 + with: + repository: TimefoldAI/timefold-solver-enterprise + ref: ${{ github.event.inputs.baseline }} + token: ${{ secrets.BENCHMARK_PUBLISH_TOKEN }} + path: ./timefold-solver-enterprise + - name: (Baseline) Quickly build timefold-solver-enterprise + if: needs.decisions.outputs.needs_snapshot_built == 'true' + working-directory: ./timefold-solver-enterprise + shell: bash + run: mvn -B -Dquickly clean install + - name: (Baseline) Delete checkouts + shell: bash + run: | + rm -rf ./timefold-solver + rm -rf ./timefold-solver-enterprise + - name: (Baseline) Compile the benchmark working-directory: ./timefold-solver-benchmarks shell: bash run: | - ./mvnw clean install -B -Dquickly -Dversion.ai.timefold.solver=${{ github.event.inputs.baseline }} + ./mvnw clean install -B -Dquickly -Dversion.ai.timefold.solver=${{ needs.decisions.outputs.baseline_solver_version }} mv target/benchmarks.jar ../benchmarks-baseline.jar - name: (SUT) Checkout timefold-solver From 6b9cba7475e18df208d70fe19299edfd494ed73b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 10:07:41 +0100 Subject: [PATCH 03/17] Fix 1 --- .github/workflows/performance_score_director.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index cf01adc7..39bf7b5c 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -48,7 +48,6 @@ jobs: steps: - name: Determine the baseline id: step1 - working-directory: ./timefold-solver-benchmarks shell: bash run: | if [[ "${{ github.event.inputs.baseline }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then From 81120dfa7619b4cc168ee05ebb9d5625422d2657 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 10:08:42 +0100 Subject: [PATCH 04/17] Testing defaults --- .github/workflows/performance_score_director.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index 39bf7b5c..6d20ce56 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -22,7 +22,7 @@ on: required: true baseline: description: 'Baseline ref' - default: 'v1.27.0' + default: 'main' required: true jdk_branch: description: 'JDK version' @@ -30,11 +30,11 @@ on: required: true branch: description: 'Ref to benchmark (needs to use 999-SNAPSHOT)' - default: 'main' + default: 'index' required: true branch_owner: description: 'User owning the branch' - default: 'TimefoldAI' + default: 'triceo' required: true run-name: "Timefold Solver v${{ github.event.inputs.baseline }} vs. ${{ github.event.inputs.branch_owner }}/${{ github.event.inputs.branch }} (Java ${{ github.event.inputs.jdk_baseline }} vs. ${{ github.event.inputs.jdk_branch }})" From 4625bbd9bdd3cdb544635da32a09d8306b76afef Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 10:16:21 +0100 Subject: [PATCH 05/17] Fix 2 --- .github/workflows/performance_score_director.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index 6d20ce56..a8142678 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -21,7 +21,7 @@ on: default: '25' required: true baseline: - description: 'Baseline ref' + description: 'Baseline branch or tag (branches need to use 999-SNAPSHOT)' default: 'main' required: true jdk_branch: @@ -29,7 +29,7 @@ on: default: '25' required: true branch: - description: 'Ref to benchmark (needs to use 999-SNAPSHOT)' + description: 'Branch to benchmark (needs to use 999-SNAPSHOT)' default: 'index' required: true branch_owner: @@ -92,10 +92,10 @@ jobs: server-password: 'MVN_PASSWORD' # Only build the snapshots if determined by the decisions job. - - name: (SUT) Checkout timefold-solver + - name: (Basline) Checkout timefold-solver uses: actions/checkout@v5 with: - repository: ${{ github.event.inputs.branch_owner }}/timefold-solver + repository: TimefoldAI/timefold-solver ref: ${{ github.event.inputs.baseline }} path: ./timefold-solver - name: (Baseline) Quickly build timefold-solver From b847519abe3bd555d63d7e1fc20640453a90def2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 10:26:06 +0100 Subject: [PATCH 06/17] Fix 3 --- .github/workflows/performance_score_director.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index a8142678..ac3b041f 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -37,7 +37,7 @@ on: default: 'triceo' required: true -run-name: "Timefold Solver v${{ github.event.inputs.baseline }} vs. ${{ github.event.inputs.branch_owner }}/${{ github.event.inputs.branch }} (Java ${{ github.event.inputs.jdk_baseline }} vs. ${{ github.event.inputs.jdk_branch }})" +run-name: "Timefold Solver TimefoldAI's ${{ github.event.inputs.baseline }} vs. '${{ github.event.inputs.branch_owner }}/${{ github.event.inputs.branch }}' (Java ${{ github.event.inputs.jdk_baseline }} vs. ${{ github.event.inputs.jdk_branch }})" jobs: decisions: @@ -102,7 +102,7 @@ jobs: if: needs.decisions.outputs.needs_snapshot_built == 'true' working-directory: ./timefold-solver shell: bash - run: ./mvnw -B -Dquickly clean install + run: ./mvnw -T 1C -B -Dquickly clean install - name: (Baseline) Checkout timefold-solver-enterprise if: needs.decisions.outputs.needs_snapshot_built == 'true' uses: actions/checkout@v5 @@ -115,7 +115,7 @@ jobs: if: needs.decisions.outputs.needs_snapshot_built == 'true' working-directory: ./timefold-solver-enterprise shell: bash - run: mvn -B -Dquickly clean install + run: ./mvnw -T 1C -B -Dquickly clean install - name: (Baseline) Delete checkouts shell: bash run: | @@ -124,9 +124,11 @@ jobs: - name: (Baseline) Compile the benchmark working-directory: ./timefold-solver-benchmarks + env: + BASELINE: ${{ needs.decisions.outputs.baseline_solver_version }} shell: bash run: | - ./mvnw clean install -B -Dquickly -Dversion.ai.timefold.solver=${{ needs.decisions.outputs.baseline_solver_version }} + ./mvnw clean install -B -Dquickly -Dversion.ai.timefold.solver=$BASELINE mv target/benchmarks.jar ../benchmarks-baseline.jar - name: (SUT) Checkout timefold-solver @@ -139,7 +141,7 @@ jobs: - name: (SUT) Quickly build timefold-solver working-directory: ./timefold-solver shell: bash - run: ./mvnw -B -Dquickly clean install + run: ./mvnw -T 1C -B -Dquickly clean install # Clone timefold-solver-enterprise - name: (SUT) Checkout timefold-solver-enterprise (Specified) @@ -163,7 +165,7 @@ jobs: - name: (SUT) Quickly build timefold-solver-enterprise working-directory: ./timefold-solver-enterprise shell: bash - run: mvn -B -Dquickly clean install + run: ./mvnw -T 1C -B -Dquickly clean install # Sometimes changes may be incompatible with the tag. # If the branch doesn't exist, we assume that the changes are compatible and move on. From 2f9ccb602399abd07d6c75ec8c6ce4b346c18775 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 10:29:41 +0100 Subject: [PATCH 07/17] Fix 4 --- .../workflows/performance_score_director.yml | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index ac3b041f..6546c28f 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -1,7 +1,10 @@ -# Both baseline and SUT (Software Under Test) are built from source first, +# Both baseline and SUT (Software Under Test) are built from source first*, # with their binaries uploaded as artifacts. # This is done on GitHub infrastructure, to achieve maximum parallelization. # +# * Unless the baseline is a release tag, +# in which case its binaries are downloaded from a repository. +# # The benchmark job downloads the binaries and runs them. # The baseline is established first, then the SUT is measured. # They both run in the same job, @@ -11,7 +14,7 @@ # Each benchmark gives a 99.9 % confidence interval. # The confidence intervals are compared to determine if the branch under test is a regression or an improvement. # The error threshold is expected to be below +/- 2.0 %. -name: Performance Regression Test - Score Director +name: Timefold Solver ScoreDirector Perf Regression Test on: workflow_dispatch: @@ -37,7 +40,7 @@ on: default: 'triceo' required: true -run-name: "Timefold Solver TimefoldAI's ${{ github.event.inputs.baseline }} vs. '${{ github.event.inputs.branch_owner }}/${{ github.event.inputs.branch }}' (Java ${{ github.event.inputs.jdk_baseline }} vs. ${{ github.event.inputs.jdk_branch }})" +run-name: "TimefoldAI's ${{ github.event.inputs.baseline }} vs. '${{ github.event.inputs.branch_owner }}/${{ github.event.inputs.branch }}' (Java ${{ github.event.inputs.jdk_baseline }} vs. ${{ github.event.inputs.jdk_branch }})" jobs: decisions: @@ -102,7 +105,7 @@ jobs: if: needs.decisions.outputs.needs_snapshot_built == 'true' working-directory: ./timefold-solver shell: bash - run: ./mvnw -T 1C -B -Dquickly clean install + run: ./mvnw -B -Dquickly clean install - name: (Baseline) Checkout timefold-solver-enterprise if: needs.decisions.outputs.needs_snapshot_built == 'true' uses: actions/checkout@v5 @@ -115,7 +118,7 @@ jobs: if: needs.decisions.outputs.needs_snapshot_built == 'true' working-directory: ./timefold-solver-enterprise shell: bash - run: ./mvnw -T 1C -B -Dquickly clean install + run: ./mvnw -B -Dquickly clean install - name: (Baseline) Delete checkouts shell: bash run: | @@ -141,7 +144,7 @@ jobs: - name: (SUT) Quickly build timefold-solver working-directory: ./timefold-solver shell: bash - run: ./mvnw -T 1C -B -Dquickly clean install + run: ./mvnw -B -Dquickly clean install # Clone timefold-solver-enterprise - name: (SUT) Checkout timefold-solver-enterprise (Specified) @@ -165,7 +168,7 @@ jobs: - name: (SUT) Quickly build timefold-solver-enterprise working-directory: ./timefold-solver-enterprise shell: bash - run: ./mvnw -T 1C -B -Dquickly clean install + run: ./mvnw -B -Dquickly clean install # Sometimes changes may be incompatible with the tag. # If the branch doesn't exist, we assume that the changes are compatible and move on. From 01c6fb8d3e71067d8ff3a0c677ccc2348b70fdc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 10:35:36 +0100 Subject: [PATCH 08/17] Fix 5 --- .../workflows/performance_score_director.yml | 47 +++++++++++++++---- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index 6546c28f..96d31709 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -64,9 +64,9 @@ jobs: echo "Baseline is a random branch." fi - echo "version=$BASELINE_VERSION" >> "$GITHUB_OUTPUT" + echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "needs_snapshot_built=$NEEDS_SNAPSHOT_BUILT" >> "$GITHUB_OUTPUT" - build: + build_baseline: needs: decisions runs-on: ubuntu-latest # Leverage massive parallelization of Github-hosted runners. strategy: @@ -95,7 +95,7 @@ jobs: server-password: 'MVN_PASSWORD' # Only build the snapshots if determined by the decisions job. - - name: (Basline) Checkout timefold-solver + - name: (Baseline) Checkout timefold-solver uses: actions/checkout@v5 with: repository: TimefoldAI/timefold-solver @@ -127,13 +127,45 @@ jobs: - name: (Baseline) Compile the benchmark working-directory: ./timefold-solver-benchmarks - env: - BASELINE: ${{ needs.decisions.outputs.baseline_solver_version }} shell: bash run: | - ./mvnw clean install -B -Dquickly -Dversion.ai.timefold.solver=$BASELINE + ./mvnw clean install -B -Dquickly -Dversion.ai.timefold.solver=${{ needs.decisions.outputs.baseline_solver_version }} mv target/benchmarks.jar ../benchmarks-baseline.jar + - name: Upload the binaries + uses: actions/upload-artifact@v5 + with: + name: binaries-${{ matrix.example }} + path: | + ./benchmarks-baseline.jar + if-no-files-found: error + build_sut: + runs-on: ubuntu-latest # Leverage massive parallelization of Github-hosted runners. + strategy: + fail-fast: true # If one compilation fails, abort everything. + matrix: + example: [cloud_balancing, conference_scheduling, curriculum_course, examination, machine_reassignment, meeting_scheduling, nurse_rostering, patient_admission_scheduling, task_assigning, traveling_tournament, tsp, vehicle_routing] + env: + MVN_USERNAME: '${{ secrets.JFROG_ENTERPRISE_READ_ONLY_ACCESS_USERNAME }}' + MVN_PASSWORD: '${{ secrets.JFROG_ENTERPRISE_READ_ONLY_ACCESS_TOKEN }}' + steps: + - name: Checkout timefold-solver-benchmarks + uses: actions/checkout@v5 + with: + repository: TimefoldAI/timefold-solver-benchmarks + path: ./timefold-solver-benchmarks + ref: main # Assume the version of main is compatible with the tagged Solver. + + - name: Setup JDK and Maven + uses: actions/setup-java@v5 + with: + java-version: 25 # Always build with the least recent supported JDK. + distribution: 'temurin' + cache: 'maven' + server-id: 'timefold-solver-enterprise' + server-username: 'MVN_USERNAME' + server-password: 'MVN_PASSWORD' + - name: (SUT) Checkout timefold-solver uses: actions/checkout@v5 with: @@ -192,12 +224,11 @@ jobs: with: name: binaries-${{ matrix.example }} path: | - ./benchmarks-baseline.jar ./benchmarks-sut.jar if-no-files-found: error benchmark: - needs: build + needs: [ build_baseline, build_sut ] runs-on: self-hosted # We need a stable machine to actually run the benchmarks. strategy: fail-fast: false # Jobs fail if the benchmark error is over predefined thresholds; other benchmarks continue. From 31252edec18c287a515b00343f81800810ea92dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 10:43:33 +0100 Subject: [PATCH 09/17] Fix 6 --- .github/workflows/performance_score_director.yml | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index 96d31709..6dead7bf 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -40,7 +40,7 @@ on: default: 'triceo' required: true -run-name: "TimefoldAI's ${{ github.event.inputs.baseline }} vs. '${{ github.event.inputs.branch_owner }}/${{ github.event.inputs.branch }}' (Java ${{ github.event.inputs.jdk_baseline }} vs. ${{ github.event.inputs.jdk_branch }})" +run-name: "TimefoldAI's ${{ github.event.inputs.baseline }} vs. ${{ github.event.inputs.branch_owner }}'s ${{ github.event.inputs.branch }}' (Java ${{ github.event.inputs.jdk_baseline }} vs. ${{ github.event.inputs.jdk_branch }})" jobs: decisions: @@ -135,7 +135,7 @@ jobs: - name: Upload the binaries uses: actions/upload-artifact@v5 with: - name: binaries-${{ matrix.example }} + name: ${{ matrix.example }}-sut-binary path: | ./benchmarks-baseline.jar if-no-files-found: error @@ -222,7 +222,7 @@ jobs: - name: Upload the binaries uses: actions/upload-artifact@v5 with: - name: binaries-${{ matrix.example }} + name: ${{ matrix.example }}-baseline-binary path: | ./benchmarks-sut.jar if-no-files-found: error @@ -262,7 +262,12 @@ jobs: - name: Download the benchmark binaries uses: actions/download-artifact@v6 with: - name: binaries-${{ matrix.example }} + name: ${{ matrix.example }}-baseline-binary + path: ./timefold-solver-benchmarks + - name: Download the benchmark binaries + uses: actions/download-artifact@v6 + with: + name: ${{ matrix.example }}-sut-binary path: ./timefold-solver-benchmarks # Fine-tuned for stability on GHA. @@ -354,6 +359,7 @@ jobs: if (( $(echo "$DIFF_MID >= 97.00" | bc -l) && $(echo "$DIFF_MID <= 103.00"|bc -l) )); then # Ignore differences of up to 3 %; we can't expect that level of precision anyway. + echo "### ✅ Within tolerance" >> $GITHUB_STEP_SUMMARY exit 0 elif [ "$SUT_RANGE_START" -gt "$BASELINE_RANGE_END" ]; then echo "### 🚀🚀🚀 Statistically significant improvement 🚀🚀🚀" >> $GITHUB_STEP_SUMMARY @@ -361,6 +367,7 @@ jobs: echo "### ‼️‼️‼️ Statistically significant regression ‼️‼️‼️" >> $GITHUB_STEP_SUMMARY export FAIL=true else + echo "### 🌟 All good" >> $GITHUB_STEP_SUMMARY exit 0 fi From 06b0a8a2f3d6e62185ae48dc4b38bdbe517613b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 10:53:49 +0100 Subject: [PATCH 10/17] Fix 7 --- .../workflows/performance_score_director.yml | 34 ++++++------------- 1 file changed, 11 insertions(+), 23 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index 6dead7bf..13654f57 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -119,11 +119,6 @@ jobs: working-directory: ./timefold-solver-enterprise shell: bash run: ./mvnw -B -Dquickly clean install - - name: (Baseline) Delete checkouts - shell: bash - run: | - rm -rf ./timefold-solver - rm -rf ./timefold-solver-enterprise - name: (Baseline) Compile the benchmark working-directory: ./timefold-solver-benchmarks @@ -135,7 +130,7 @@ jobs: - name: Upload the binaries uses: actions/upload-artifact@v5 with: - name: ${{ matrix.example }}-sut-binary + name: ${{ matrix.example }}-sut path: | ./benchmarks-baseline.jar if-no-files-found: error @@ -172,31 +167,25 @@ jobs: repository: ${{ github.event.inputs.branch_owner }}/timefold-solver ref: ${{ github.event.inputs.branch }} path: ./timefold-solver - - name: (SUT) Quickly build timefold-solver working-directory: ./timefold-solver shell: bash run: ./mvnw -B -Dquickly clean install # Clone timefold-solver-enterprise - - name: (SUT) Checkout timefold-solver-enterprise (Specified) - id: checkout-solver-enterprise - uses: actions/checkout@v5 - continue-on-error: true - with: - repository: TimefoldAI/timefold-solver-enterprise - ref: ${{ github.event.inputs.branch }} - token: ${{ secrets.BENCHMARK_PUBLISH_TOKEN }} - path: ./timefold-solver-enterprise - name: (SUT) Checkout timefold-solver-enterprise (Fallback) - if: steps.checkout-solver-enterprise.outcome != 'success' uses: actions/checkout@v5 with: repository: TimefoldAI/timefold-solver-enterprise - ref: main token: ${{ secrets.BENCHMARK_PUBLISH_TOKEN }} path: ./timefold-solver-enterprise - + - name: (SUT) Switch to correct branch if it exists + working-directory: ./timefold-solver-enterprise + shell: bash + run: | + if git branch --list "${{ github.event.inputs.branch }}" | grep -q .; then + git checkout ${{ github.event.inputs.branch }} + fi - name: (SUT) Quickly build timefold-solver-enterprise working-directory: ./timefold-solver-enterprise shell: bash @@ -211,7 +200,6 @@ jobs: repository: TimefoldAI/timefold-solver-benchmarks path: ./timefold-solver-benchmarks ref: ${{ github.event.inputs.branch }} - - name: (SUT) Compile the benchmarks working-directory: ./timefold-solver-benchmarks shell: bash @@ -222,7 +210,7 @@ jobs: - name: Upload the binaries uses: actions/upload-artifact@v5 with: - name: ${{ matrix.example }}-baseline-binary + name: ${{ matrix.example }}-baseline path: | ./benchmarks-sut.jar if-no-files-found: error @@ -262,12 +250,12 @@ jobs: - name: Download the benchmark binaries uses: actions/download-artifact@v6 with: - name: ${{ matrix.example }}-baseline-binary + name: ${{ matrix.example }}-baseline path: ./timefold-solver-benchmarks - name: Download the benchmark binaries uses: actions/download-artifact@v6 with: - name: ${{ matrix.example }}-sut-binary + name: ${{ matrix.example }}-sut path: ./timefold-solver-benchmarks # Fine-tuned for stability on GHA. From 35c2eb88dcea7a55fe06c55af1c026290e61dc57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 11:08:50 +0100 Subject: [PATCH 11/17] Fix 9 --- .../workflows/performance_score_director.yml | 67 ++++++++++++------- 1 file changed, 42 insertions(+), 25 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index 13654f57..858a7524 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -14,7 +14,7 @@ # Each benchmark gives a 99.9 % confidence interval. # The confidence intervals are compared to determine if the branch under test is a regression or an improvement. # The error threshold is expected to be below +/- 2.0 %. -name: Timefold Solver ScoreDirector Perf Regression Test +name: ScoreDirector Perf Regression Test on: workflow_dispatch: @@ -66,12 +66,14 @@ jobs: echo "version=$VERSION" >> "$GITHUB_OUTPUT" echo "needs_snapshot_built=$NEEDS_SNAPSHOT_BUILT" >> "$GITHUB_OUTPUT" + build_baseline: needs: decisions runs-on: ubuntu-latest # Leverage massive parallelization of Github-hosted runners. strategy: fail-fast: true # If one compilation fails, abort everything. matrix: + # When updating this list, use find-and-replace in the entire file to keep all such lists identical. example: [cloud_balancing, conference_scheduling, curriculum_course, examination, machine_reassignment, meeting_scheduling, nurse_rostering, patient_admission_scheduling, task_assigning, traveling_tournament, tsp, vehicle_routing] env: MVN_USERNAME: '${{ secrets.JFROG_ENTERPRISE_READ_ONLY_ACCESS_USERNAME }}' @@ -82,7 +84,7 @@ jobs: with: repository: TimefoldAI/timefold-solver-benchmarks path: ./timefold-solver-benchmarks - ref: main # Assume the version of main is compatible with the tagged Solver. + ref: main # Assume the ref is compatible with both baseline and SUT - name: Setup JDK and Maven uses: actions/setup-java@v5 @@ -95,18 +97,19 @@ jobs: server-password: 'MVN_PASSWORD' # Only build the snapshots if determined by the decisions job. - - name: (Baseline) Checkout timefold-solver + - name: Checkout timefold-solver + if: needs.decisions.outputs.needs_snapshot_built == 'true' uses: actions/checkout@v5 with: repository: TimefoldAI/timefold-solver ref: ${{ github.event.inputs.baseline }} path: ./timefold-solver - - name: (Baseline) Quickly build timefold-solver + - name: Quickly build timefold-solver if: needs.decisions.outputs.needs_snapshot_built == 'true' working-directory: ./timefold-solver shell: bash run: ./mvnw -B -Dquickly clean install - - name: (Baseline) Checkout timefold-solver-enterprise + - name: Checkout timefold-solver-enterprise if: needs.decisions.outputs.needs_snapshot_built == 'true' uses: actions/checkout@v5 with: @@ -114,13 +117,21 @@ jobs: ref: ${{ github.event.inputs.baseline }} token: ${{ secrets.BENCHMARK_PUBLISH_TOKEN }} path: ./timefold-solver-enterprise - - name: (Baseline) Quickly build timefold-solver-enterprise + - name: Quickly build timefold-solver-enterprise if: needs.decisions.outputs.needs_snapshot_built == 'true' working-directory: ./timefold-solver-enterprise shell: bash run: ./mvnw -B -Dquickly clean install - - - name: (Baseline) Compile the benchmark + - name: Switch to correct Benchmarks branch if it exists + if: needs.decisions.outputs.needs_snapshot_built == 'true' + working-directory: ./timefold-solver-benchmarks + shell: bash + run: | + if git branch --list "${{ github.event.inputs.baseline }}" | grep -q .; then + git checkout ${{ github.event.inputs.baseline }} + fi + git status + - name: Compile the benchmark working-directory: ./timefold-solver-benchmarks shell: bash run: | @@ -134,11 +145,13 @@ jobs: path: | ./benchmarks-baseline.jar if-no-files-found: error + build_sut: runs-on: ubuntu-latest # Leverage massive parallelization of Github-hosted runners. strategy: fail-fast: true # If one compilation fails, abort everything. matrix: + # When updating this list, use find-and-replace in the entire file to keep all such lists identical. example: [cloud_balancing, conference_scheduling, curriculum_course, examination, machine_reassignment, meeting_scheduling, nurse_rostering, patient_admission_scheduling, task_assigning, traveling_tournament, tsp, vehicle_routing] env: MVN_USERNAME: '${{ secrets.JFROG_ENTERPRISE_READ_ONLY_ACCESS_USERNAME }}' @@ -149,7 +162,7 @@ jobs: with: repository: TimefoldAI/timefold-solver-benchmarks path: ./timefold-solver-benchmarks - ref: main # Assume the version of main is compatible with the tagged Solver. + ref: main # Assume the ref is compatible with both baseline and SUIT - name: Setup JDK and Maven uses: actions/setup-java@v5 @@ -161,46 +174,49 @@ jobs: server-username: 'MVN_USERNAME' server-password: 'MVN_PASSWORD' - - name: (SUT) Checkout timefold-solver + - name: Checkout timefold-solver uses: actions/checkout@v5 with: repository: ${{ github.event.inputs.branch_owner }}/timefold-solver ref: ${{ github.event.inputs.branch }} path: ./timefold-solver - - name: (SUT) Quickly build timefold-solver + - name: Quickly build timefold-solver working-directory: ./timefold-solver shell: bash run: ./mvnw -B -Dquickly clean install # Clone timefold-solver-enterprise - - name: (SUT) Checkout timefold-solver-enterprise (Fallback) + - name: Checkout timefold-solver-enterprise uses: actions/checkout@v5 with: repository: TimefoldAI/timefold-solver-enterprise + ref: main token: ${{ secrets.BENCHMARK_PUBLISH_TOKEN }} path: ./timefold-solver-enterprise - - name: (SUT) Switch to correct branch if it exists + - name: Switch to correct Enterprise branch if it exists working-directory: ./timefold-solver-enterprise shell: bash run: | if git branch --list "${{ github.event.inputs.branch }}" | grep -q .; then git checkout ${{ github.event.inputs.branch }} fi - - name: (SUT) Quickly build timefold-solver-enterprise + git status + - name: Quickly build timefold-solver-enterprise working-directory: ./timefold-solver-enterprise shell: bash run: ./mvnw -B -Dquickly clean install # Sometimes changes may be incompatible with the tag. # If the branch doesn't exist, we assume that the changes are compatible and move on. - - name: (SUT) Checkout timefold-solver-benchmarks - uses: actions/checkout@v5 - continue-on-error: true - with: - repository: TimefoldAI/timefold-solver-benchmarks - path: ./timefold-solver-benchmarks - ref: ${{ github.event.inputs.branch }} - - name: (SUT) Compile the benchmarks + - name: Switch to correct Benchmarks branch if it exists + working-directory: ./timefold-solver-benchmarks + shell: bash + run: | + if git branch --list "${{ github.event.inputs.branch }}" | grep -q .; then + git checkout ${{ github.event.inputs.branch }} + fi + git status + - name: Compile the benchmarks working-directory: ./timefold-solver-benchmarks shell: bash run: | @@ -221,6 +237,7 @@ jobs: strategy: fail-fast: false # Jobs fail if the benchmark error is over predefined thresholds; other benchmarks continue. matrix: + # When updating this list, use find-and-replace in the entire file to keep all such lists identical. example: [cloud_balancing, conference_scheduling, curriculum_course, examination, machine_reassignment, meeting_scheduling, nurse_rostering, patient_admission_scheduling, task_assigning, traveling_tournament, tsp, vehicle_routing] env: MVN_USERNAME: '${{ secrets.JFROG_ENTERPRISE_READ_ONLY_ACCESS_USERNAME }}' @@ -272,7 +289,7 @@ jobs: cat scoredirector-benchmark.properties chmod +x run-scoredirector.sh - - name: (Baseline) Run the benchmark + - name: Run the benchmark working-directory: ./timefold-solver-benchmarks id: benchmark_baseline env: @@ -286,14 +303,14 @@ jobs: echo "RANGE_END=$(jq '.[0].primaryMetric.scoreConfidence[1]|round' results/scoredirector/${{ env.RUN_ID }}/results.json)" >> "$GITHUB_OUTPUT" echo "RANGE_MID=$(jq '.[0].primaryMetric.score|round' results/scoredirector/${{ env.RUN_ID }}/results.json)" >> "$GITHUB_OUTPUT" - - name: (SUT) Setup JDK and Maven + - name: Setup JDK and Maven uses: actions/setup-java@v5 with: java-version: ${{ github.event.inputs.jdk_branch }} distribution: 'temurin' check-latest: true - - name: (SUT) Run the benchmark + - name: Run the benchmark id: benchmark_sut working-directory: ./timefold-solver-benchmarks env: From 839b2e3458d904b41a1e570199c6802d6a20ce6f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 11:26:09 +0100 Subject: [PATCH 12/17] Finishing touches --- .../workflows/performance_score_director.yml | 25 ++++++++++--------- 1 file changed, 13 insertions(+), 12 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index 858a7524..2d05578f 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -1,19 +1,19 @@ -# Both baseline and SUT (Software Under Test) are built from source first*, +# Both baseline and SUT (Software Under Test) are built from source first [1], # with their binaries uploaded as artifacts. # This is done on GitHub infrastructure, to achieve maximum parallelization. # -# * Unless the baseline is a release tag, -# in which case its binaries are downloaded from a repository. -# # The benchmark job downloads the binaries and runs them. # The baseline is established first, then the SUT is measured. # They both run in the same job, -# to guarantee they ran on the same machine with the same performance characteristics. +# to guarantee they run on the same machine with the same performance characteristics. # This is done on a self-hosted runner which we completely control. # # Each benchmark gives a 99.9 % confidence interval. # The confidence intervals are compared to determine if the branch under test is a regression or an improvement. # The error threshold is expected to be below +/- 2.0 %. +# +# [1] Unless the baseline is a release tag, in which case its binaries are downloaded from a repository. +# name: ScoreDirector Perf Regression Test on: @@ -25,7 +25,7 @@ on: required: true baseline: description: 'Baseline branch or tag (branches need to use 999-SNAPSHOT)' - default: 'main' + default: 'v1.27.0' required: true jdk_branch: description: 'JDK version' @@ -33,14 +33,14 @@ on: required: true branch: description: 'Branch to benchmark (needs to use 999-SNAPSHOT)' - default: 'index' + default: 'main' required: true branch_owner: description: 'User owning the branch' - default: 'triceo' + default: 'TimefoldAI' required: true -run-name: "TimefoldAI's ${{ github.event.inputs.baseline }} vs. ${{ github.event.inputs.branch_owner }}'s ${{ github.event.inputs.branch }}' (Java ${{ github.event.inputs.jdk_baseline }} vs. ${{ github.event.inputs.jdk_branch }})" +run-name: "TimefoldAI's ${{ github.event.inputs.baseline }} vs. ${{ github.event.inputs.branch_owner }}'s ${{ github.event.inputs.branch }} (Java ${{ github.event.inputs.jdk_baseline }} vs. ${{ github.event.inputs.jdk_branch }})" jobs: decisions: @@ -122,6 +122,7 @@ jobs: working-directory: ./timefold-solver-enterprise shell: bash run: ./mvnw -B -Dquickly clean install + - name: Switch to correct Benchmarks branch if it exists if: needs.decisions.outputs.needs_snapshot_built == 'true' working-directory: ./timefold-solver-benchmarks @@ -336,7 +337,7 @@ jobs: - name: Archive benchmark data uses: actions/upload-artifact@v5 with: - name: results-${{ matrix.example }}-${{ env.SANITIZED_BASELINE }}_vs_${{ env.SANITIZED_BRANCH }} + name: assets-${{ matrix.example }}-${{ env.SANITIZED_BASELINE }}_vs_${{ env.SANITIZED_BRANCH }} path: | ./timefold-solver-benchmarks/scoredirector-benchmark.properties ./timefold-solver-benchmarks/${{ env.SANITIZED_BASELINE }}/*combined.jfr @@ -367,9 +368,9 @@ jobs: echo "### ✅ Within tolerance" >> $GITHUB_STEP_SUMMARY exit 0 elif [ "$SUT_RANGE_START" -gt "$BASELINE_RANGE_END" ]; then - echo "### 🚀🚀🚀 Statistically significant improvement 🚀🚀🚀" >> $GITHUB_STEP_SUMMARY + echo "### 🚀 Statistically significant improvement" >> $GITHUB_STEP_SUMMARY elif [ "$BASELINE_RANGE_START" -gt "$SUT_RANGE_END" ]; then - echo "### ‼️‼️‼️ Statistically significant regression ‼️‼️‼️" >> $GITHUB_STEP_SUMMARY + echo "### ‼️ Statistically significant regression ‼️" >> $GITHUB_STEP_SUMMARY export FAIL=true else echo "### 🌟 All good" >> $GITHUB_STEP_SUMMARY From 8c9dce4eb086b678a4694a7b9ce8521c8aa0135b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 12:36:56 +0100 Subject: [PATCH 13/17] More --- .../workflows/performance_score_director.yml | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index 2d05578f..b78dc751 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -358,10 +358,10 @@ jobs: SUT_RANGE_END: ${{ steps.benchmark_sut.outputs.RANGE_END }} shell: bash run: | - export BASELINE_DEV=$(echo "scale=2; ($BASELINE_RANGE_MID / $BASELINE_RANGE_START) * 100 - 100" | bc) - export SUT_DEV=$(echo "scale=2; ($SUT_RANGE_MID / $SUT_RANGE_START) * 100 - 100" | bc) - export DIFF_MID=$(echo "scale=2; ($BASELINE_RANGE_MID / $SUT_RANGE_MID) * 100" | bc) - export FAIL=false + BASELINE_DEV=$(echo "scale=2; ($BASELINE_RANGE_MID / $BASELINE_RANGE_START) * 100 - 100" | bc) + SUT_DEV=$(echo "scale=2; ($SUT_RANGE_MID / $SUT_RANGE_START) * 100 - 100" | bc) + DIFF_MID=$(echo "scale=2; ($BASELINE_RANGE_MID / $SUT_RANGE_MID) * 100" | bc) + FAIL=false if (( $(echo "$DIFF_MID >= 97.00" | bc -l) && $(echo "$DIFF_MID <= 103.00"|bc -l) )); then # Ignore differences of up to 3 %; we can't expect that level of precision anyway. @@ -371,17 +371,24 @@ jobs: echo "### 🚀 Statistically significant improvement" >> $GITHUB_STEP_SUMMARY elif [ "$BASELINE_RANGE_START" -gt "$SUT_RANGE_END" ]; then echo "### ‼️ Statistically significant regression ‼️" >> $GITHUB_STEP_SUMMARY - export FAIL=true + FAIL=true else echo "### 🌟 All good" >> $GITHUB_STEP_SUMMARY exit 0 fi + if [[ "${{ github.event.inputs.baseline }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then + BASELINE_URL="https://github.com/TimefoldAI/timefold-solver/releases/tag/${{ github.event.inputs.baseline }}" + else + BASELINE_URL="https://github.com/TimefoldAI/timefold-solver/tree/${{ github.event.inputs.branch }}" + fi + SUT_URL="https://github.com/${{ github.event.inputs.branch_owner }}/timefold-solver/tree/${{ github.event.inputs.branch }}" + echo "| | **Ref** | **Mean** |" >> $GITHUB_STEP_SUMMARY echo "|:------:|:-----------:|:-----------------:|" >> $GITHUB_STEP_SUMMARY - echo "| _Old_ | [v${{ github.event.inputs.baseline }}](https://github.com/TimefoldAI/timefold-solver/releases/tag/v${{ github.event.inputs.baseline }}) | ${BASELINE_RANGE_MID} ± ${BASELINE_DEV} % |" >> $GITHUB_STEP_SUMMARY - echo "| _New_ | [${{ github.event.inputs.branch_owner }}'s ${{ github.event.inputs.branch }}](https://github.com/${{ github.event.inputs.branch_owner }}/timefold-solver/tree/${{ github.event.inputs.branch }}) | ${SUT_RANGE_MID} ± ${SUT_DEV} % |" >> $GITHUB_STEP_SUMMARY - echo "| _Diff_ | | ${DIFF_MID} % |" >> $GITHUB_STEP_SUMMARY + echo "| _Old_ | [TimefoldAI's ${{ github.event.inputs.baseline }}]($BASELINE_URL) | $BASELINE_RANGE_MID ± $BASELINE_DEV % |" >> $GITHUB_STEP_SUMMARY + echo "| _New_ | [${{ github.event.inputs.branch_owner }}'s ${{ github.event.inputs.branch }}]($SUT_URL) | $SUT_RANGE_MID ± $SUT_DEV % |" >> $GITHUB_STEP_SUMMARY + echo "| _Diff_ | | $DIFF_MID % |" >> $GITHUB_STEP_SUMMARY echo "" >> $GITHUB_STEP_SUMMARY echo "Mean is in operations per second. Higher is better." >> $GITHUB_STEP_SUMMARY From 09b066dfb95319e2a7c2ca13f8224aaa369d33e9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 12:37:25 +0100 Subject: [PATCH 14/17] More --- .github/workflows/performance_score_director.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index b78dc751..a1e2fada 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -281,9 +281,9 @@ jobs: working-directory: ./timefold-solver-benchmarks shell: bash run: | - echo "forks=20" > scoredirector-benchmark.properties - echo "warmup_iterations=5" >> scoredirector-benchmark.properties - echo "measurement_iterations=5" >> scoredirector-benchmark.properties + echo "forks=2" > scoredirector-benchmark.properties + echo "warmup_iterations=1" >> scoredirector-benchmark.properties + echo "measurement_iterations=1" >> scoredirector-benchmark.properties echo "relative_score_error_threshold=0.02" >> scoredirector-benchmark.properties echo "score_director_type=cs" >> scoredirector-benchmark.properties echo "example=${{ matrix.example }}" >> scoredirector-benchmark.properties From c85690440e217df05de5ea63b4d82ff22e8c45bb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 12:48:52 +0100 Subject: [PATCH 15/17] More --- .github/workflows/performance_score_director.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index a1e2fada..0812bc07 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -366,7 +366,6 @@ jobs: if (( $(echo "$DIFF_MID >= 97.00" | bc -l) && $(echo "$DIFF_MID <= 103.00"|bc -l) )); then # Ignore differences of up to 3 %; we can't expect that level of precision anyway. echo "### ✅ Within tolerance" >> $GITHUB_STEP_SUMMARY - exit 0 elif [ "$SUT_RANGE_START" -gt "$BASELINE_RANGE_END" ]; then echo "### 🚀 Statistically significant improvement" >> $GITHUB_STEP_SUMMARY elif [ "$BASELINE_RANGE_START" -gt "$SUT_RANGE_END" ]; then @@ -374,7 +373,6 @@ jobs: FAIL=true else echo "### 🌟 All good" >> $GITHUB_STEP_SUMMARY - exit 0 fi if [[ "${{ github.event.inputs.baseline }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then From 6c132563f61a079bc9357aa8a9a9c149a47108e6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 12:56:56 +0100 Subject: [PATCH 16/17] Final --- .github/workflows/performance_score_director.yml | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index 0812bc07..dd74a4e5 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -281,9 +281,9 @@ jobs: working-directory: ./timefold-solver-benchmarks shell: bash run: | - echo "forks=2" > scoredirector-benchmark.properties - echo "warmup_iterations=1" >> scoredirector-benchmark.properties - echo "measurement_iterations=1" >> scoredirector-benchmark.properties + echo "forks=20" > scoredirector-benchmark.properties + echo "warmup_iterations=5" >> scoredirector-benchmark.properties + echo "measurement_iterations=5" >> scoredirector-benchmark.properties echo "relative_score_error_threshold=0.02" >> scoredirector-benchmark.properties echo "score_director_type=cs" >> scoredirector-benchmark.properties echo "example=${{ matrix.example }}" >> scoredirector-benchmark.properties @@ -372,13 +372,14 @@ jobs: echo "### ‼️ Statistically significant regression ‼️" >> $GITHUB_STEP_SUMMARY FAIL=true else - echo "### 🌟 All good" >> $GITHUB_STEP_SUMMARY + echo "### ⁉️ Undetermined result ⁉️" >> $GITHUB_STEP_SUMMARY + FAIL=true fi if [[ "${{ github.event.inputs.baseline }}" =~ ^v[0-9]+\.[0-9]+\.[0-9]+$ ]]; then BASELINE_URL="https://github.com/TimefoldAI/timefold-solver/releases/tag/${{ github.event.inputs.baseline }}" else - BASELINE_URL="https://github.com/TimefoldAI/timefold-solver/tree/${{ github.event.inputs.branch }}" + BASELINE_URL="https://github.com/TimefoldAI/timefold-solver/tree/${{ github.event.inputs.baseline }}" fi SUT_URL="https://github.com/${{ github.event.inputs.branch_owner }}/timefold-solver/tree/${{ github.event.inputs.branch }}" From 487d62052b38b3ffc6c17181822efd633678edbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Luk=C3=A1=C5=A1=20Petrovick=C3=BD?= Date: Sat, 8 Nov 2025 14:13:31 +0100 Subject: [PATCH 17/17] Potential fix for code scanning alert no. 14: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> --- .github/workflows/performance_score_director.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/performance_score_director.yml b/.github/workflows/performance_score_director.yml index dd74a4e5..88ea8547 100644 --- a/.github/workflows/performance_score_director.yml +++ b/.github/workflows/performance_score_director.yml @@ -15,6 +15,8 @@ # [1] Unless the baseline is a release tag, in which case its binaries are downloaded from a repository. # name: ScoreDirector Perf Regression Test +permissions: + contents: read on: workflow_dispatch: