Skip to content

Commit 6dae988

Browse files
authored
Run benchmarks in CI on macOS in addition to linux (#3444)
Run benchmarks on macOS in addition to Linux. ### Motivation: 1. macOS and Linux benchmark yield different results and we would like to rack both of them. 2. default Linux GitHub workers do not provide access to PMU, which is necessary for instructions metric benchmarking ### Modifications: - `benchmarks.yml` workflow support running benchmarks on self-hosted macOS runners ### Result: Benchmarks are running on both linux and macOS runners. An example of an opt-in PR running just one Xcode version: https://github.com/apple/swift-nio/actions/runs/19468002318/job/55707591945. An example of an opt-in PR and thresholds expectations check failed: https://github.com/apple/swift-nio/actions/runs/19468236909/job/55708359111 I've been running `swift-nio` benchmarks on macOS runners and it is much less stable than Linux for allocations and context switches. I suggest we make this an opt-in feature and take in only for instructions count benchmarks, for example upcoming to the `swift-log`. This also makes it less stressful for macOS runners pool.
1 parent 6e02c7a commit 6dae988

File tree

1 file changed

+140
-5
lines changed

1 file changed

+140
-5
lines changed

.github/workflows/benchmarks.yml

Lines changed: 140 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,37 @@ on:
7171
description: "Boolean to enable the Windows nightly main Swift version matrix job. Currently has no effect!" # TODO: implement Windows benchmarking
7272
default: false
7373

74+
macos_xcode_16_3_enabled:
75+
type: boolean
76+
description: "Boolean to enable the macOS Xcode 16.3 benchmark job. Defaults to false."
77+
default: false
78+
macos_xcode_16_4_enabled:
79+
type: boolean
80+
description: "Boolean to enable the macOS Xcode 16.4 benchmark job. Defaults to false."
81+
default: false
82+
macos_xcode_26_0_enabled:
83+
type: boolean
84+
description: "Boolean to enable the macOS Xcode 26.0 benchmark job. Defaults to false."
85+
default: false
86+
macos_xcode_26_1_enabled:
87+
type: boolean
88+
description: "Boolean to enable the macOS Xcode 26.1 benchmark job. Defaults to false."
89+
default: false
90+
macos_xcode_latest_beta_enabled:
91+
type: boolean
92+
description: "Boolean to enable the macOS Xcode latest beta benchmark job. Defaults to false."
93+
default: false
94+
95+
macos_runner_pool:
96+
type: string
97+
description: "The runner pool which will be requested for macOS jobs."
98+
default: "nightly"
99+
100+
macos_env_vars:
101+
type: string
102+
description: "Environment variables for macOS jobs as JSON (e.g., '{\"DEBUG\":\"1\",\"LOG_LEVEL\":\"info\"}')."
103+
default: "{}"
104+
74105
linux_env_vars:
75106
type: string
76107
description: "Environment variables for Linux jobs as JSON (e.g., '{\"DEBUG\":\"1\",\"LOG_LEVEL\":\"info\"}')."
@@ -82,8 +113,8 @@ on:
82113
default: ""
83114

84115
jobs:
85-
construct-matrix:
86-
name: Construct Benchmarks matrix
116+
construct-matrix-linux:
117+
name: Construct Linux Benchmarks matrix
87118
runs-on: ubuntu-latest
88119
outputs:
89120
benchmarks-matrix: '${{ steps.generate-matrix.outputs.benchmarks-matrix }}'
@@ -115,11 +146,115 @@ jobs:
115146
MATRIX_LINUX_NIGHTLY_NEXT_ENABLED: ${{ inputs.linux_nightly_6_1_enabled && inputs.linux_nightly_next_enabled }}
116147
MATRIX_LINUX_NIGHTLY_MAIN_ENABLED: ${{ inputs.linux_nightly_main_enabled }}
117148

118-
benchmarks:
149+
construct-matrix-macos:
150+
name: Construct macOS Benchmarks matrix
151+
runs-on: ubuntu-latest
152+
outputs:
153+
macos-matrix: '${{ steps.generate-matrix.outputs.macos-matrix }}'
154+
steps:
155+
- name: Checkout repository
156+
uses: actions/checkout@v4
157+
with:
158+
persist-credentials: false
159+
- id: generate-matrix
160+
run: |
161+
# Validate JSON environment variables
162+
macos_env_vars_json='${{ inputs.macos_env_vars }}'
163+
164+
if ! echo "$macos_env_vars_json" | jq empty 2>/dev/null; then
165+
echo "Error: macos_env_vars is not valid JSON"
166+
exit 1
167+
fi
168+
169+
runner_pool="${MACOS_RUNNER_POOL}"
170+
xcode_16_3_enabled="${MACOS_XCODE_16_3_ENABLED}"
171+
xcode_16_4_enabled="${MACOS_XCODE_16_4_ENABLED}"
172+
xcode_26_0_enabled="${MACOS_XCODE_26_0_ENABLED}"
173+
xcode_26_1_enabled="${MACOS_XCODE_26_1_ENABLED}"
174+
xcode_latest_beta_enabled="${MACOS_XCODE_LATEST_BETA_ENABLED}"
175+
176+
# Create matrix from inputs
177+
matrix='{"config": []}'
178+
179+
if [[ "$xcode_16_3_enabled" == "true" ]]; then
180+
matrix=$(echo "$matrix" | jq -c \
181+
--arg runner_pool "$runner_pool" \
182+
--argjson env_vars "$macos_env_vars_json" \
183+
'.config[.config| length] |= . + { "name": "macOS (Xcode 16.3)", "xcode_version": "16.3", "xcode_app": "Xcode_16.3.app", "os": "sequoia", "arch": "ARM64", "pool": $runner_pool, "env": $env_vars }')
184+
fi
185+
186+
if [[ "$xcode_16_4_enabled" == "true" ]]; then
187+
matrix=$(echo "$matrix" | jq -c \
188+
--arg runner_pool "$runner_pool" \
189+
--argjson env_vars "$macos_env_vars_json" \
190+
'.config[.config| length] |= . + { "name": "macOS (Xcode 16.4)", "xcode_version": "16.4", "xcode_app": "Xcode_16.4.app", "os": "sequoia", "arch": "ARM64", "pool": $runner_pool, "env": $env_vars }')
191+
fi
192+
193+
if [[ "$xcode_26_0_enabled" == "true" ]]; then
194+
matrix=$(echo "$matrix" | jq -c \
195+
--arg runner_pool "$runner_pool" \
196+
--argjson env_vars "$macos_env_vars_json" \
197+
'.config[.config| length] |= . + { "name": "macOS (Xcode 26.0)", "xcode_version": "26.0", "xcode_app": "Xcode_26.0.app", "os": "sequoia", "arch": "ARM64", "pool": $runner_pool, "env": $env_vars }')
198+
fi
199+
200+
if [[ "$xcode_26_1_enabled" == "true" ]]; then
201+
matrix=$(echo "$matrix" | jq -c \
202+
--arg runner_pool "$runner_pool" \
203+
--argjson env_vars "$macos_env_vars_json" \
204+
'.config[.config| length] |= . + { "name": "macOS (Xcode 26.1)", "xcode_version": "26.1", "xcode_app": "Xcode_26.1.app", "os": "sequoia", "arch": "ARM64", "pool": $runner_pool, "env": $env_vars }')
205+
fi
206+
207+
if [[ "$xcode_latest_beta_enabled" == "true" ]]; then
208+
matrix=$(echo "$matrix" | jq -c \
209+
--arg runner_pool "$runner_pool" \
210+
--argjson env_vars "$macos_env_vars_json" \
211+
'.config[.config| length] |= . + { "name": "macOS (Xcode latest beta)", "xcode_version": "latest beta", "xcode_app": "Xcode-latest.app", "os": "sequoia", "arch": "ARM64", "pool": $runner_pool, "env": $env_vars }')
212+
fi
213+
214+
echo "macos-matrix=$matrix" >> "$GITHUB_OUTPUT"
215+
env:
216+
MACOS_RUNNER_POOL: ${{ inputs.macos_runner_pool }}
217+
MACOS_XCODE_16_3_ENABLED: ${{ inputs.macos_xcode_16_3_enabled }}
218+
MACOS_XCODE_16_4_ENABLED: ${{ inputs.macos_xcode_16_4_enabled }}
219+
MACOS_XCODE_26_0_ENABLED: ${{ inputs.macos_xcode_26_0_enabled }}
220+
MACOS_XCODE_26_1_ENABLED: ${{ inputs.macos_xcode_26_1_enabled }}
221+
MACOS_XCODE_LATEST_BETA_ENABLED: ${{ inputs.macos_xcode_latest_beta_enabled }}
222+
223+
benchmarks-linux:
119224
name: Benchmarks
120-
needs: construct-matrix
225+
needs: construct-matrix-linux
121226
# Workaround https://github.com/nektos/act/issues/1875
122227
uses: apple/swift-nio/.github/workflows/swift_test_matrix.yml@main
123228
with:
124229
name: "Benchmarks"
125-
matrix_string: '${{ needs.construct-matrix.outputs.benchmarks-matrix }}'
230+
matrix_string: '${{ needs.construct-matrix-linux.outputs.benchmarks-matrix }}'
231+
232+
benchmarks-macos:
233+
name: ${{ matrix.config.name }}
234+
needs: construct-matrix-macos
235+
runs-on: [self-hosted, macos, "${{ matrix.config.os }}", "${{ matrix.config.arch }}", "${{ matrix.config.pool }}"]
236+
timeout-minutes: 30
237+
strategy:
238+
fail-fast: false
239+
matrix: ${{ fromJson(needs.construct-matrix-macos.outputs.macos-matrix) }}
240+
steps:
241+
- name: Checkout repository
242+
uses: actions/checkout@v4
243+
with:
244+
persist-credentials: false
245+
submodules: true
246+
- name: Install jemalloc
247+
run: |
248+
brew install jemalloc
249+
- name: Export environment variables
250+
if: ${{ matrix.config.env != '' && matrix.config.env != '{}'}}
251+
run: |
252+
echo "Exporting environment variables from matrix configuration..."
253+
echo '${{ toJSON(matrix.config.env) }}' | jq -r 'to_entries[] | "\(.key)=\(.value)"' >> $GITHUB_ENV
254+
echo '${{ toJSON(matrix.config.env) }}' | jq -r 'to_entries[] | "exporting \(.key)=\(.value)"'
255+
- name: Run benchmarks script
256+
run: |
257+
curl -s https://raw.githubusercontent.com/apple/swift-nio/main/scripts/check_benchmark_thresholds.sh | BENCHMARK_PACKAGE_PATH=${{ inputs.benchmark_package_path }} bash -s -- --disable-sandbox --allow-writing-to-package-directory
258+
env:
259+
DEVELOPER_DIR: "/Applications/${{ matrix.config.xcode_app }}"
260+
SWIFT_VERSION: "Xcode ${{ matrix.config.xcode_version }}"

0 commit comments

Comments
 (0)