システムプラットフォームチームの id:rskmm0chang です。
この記事は、はてなのSREが毎月交代で書いているSRE連載の2025年11月号です。10月の記事はid:cohalzさんの GitHub Organizationを安心して利用するための最近の機能紹介でした。
前回も11月に書いていて、11月になるとブログが書きたくなるようです。 developer.hatenastaff.com
GitHub Actions Runnerのパフォーマンス比較を始めた経緯
はてなでは、Actions Runner Controllerを利用して、Self-hosted runnersを提供しています。 もともとはスポットインスタンスの活用によるコストダウンを目指していましたが、(当然ながら)ジョブが途中で終了してしまうため、あまり活用されず、また、その時点ではRunnerのパフォーマンスに問題があり、あまり活用されない状況でした。
ジョブの中断に関しては、オンデマンドインスタンスにして解決しましたが、当然コストは上がります。その分パフォーマンスでカバーする必要があり、ボトルネックなどをdstatで調査したところ、Disk I/Oがボトルネックであることがわかりました。特にdocker-composeであれもこれも立ち上げてテスト、というジョブでは、Diskへのアクセスが多く、ボトルネックとなっていました。
当初はスポットインスタンスでの活用を目指していたこともあり、インスタンスの種類に縛られないようにEBSでのパフォーマンス改善を目指していましたが、IOPSなどを調整してもあまり改善が見られませんでした。
スポットインスタンスをやめて、オンデマンドに切り替えたことで、ローカルSSDを搭載しているEC2も候補になり、ローカルSSDを搭載しているEC2で、上記のようなジョブを実行したところ明らかにパフォーマンスが改善し、効果が確認できました。
この改善をもって、Self-hosted runnerのパフォーマンスの向上が確認できましたが、これが「GitHub-hosted Runnerに対してどれだけ優位なのか」、あるいは「Self-hosted runnerのどの要素(CPUやDisk I/Oなど)がボトルネックになりやすいのか」を客観的に把握する必要性を感じ、ベンチマークを用いてパフォーマンスを定期的に確認し、KPIとして改善していくこととしました。
パフォーマンスの比較対象としては
- CPU
- Disk I/O
としています。
それぞれの結果を1つのブログでどちらも記載すると長くなってしまうため、本記事ではCPUのみとし、Disk I/Oは別途ブログに記載予定のため、この記事のタイトルにも"(CPU)"をつけています。
なお、本記事におけるGitHub-hosted runnerの測定は、private repository上で実行しています。
ubuntu-slimの登場
もともとパフォーマンス試験はubuntu-latestをベースとした、2 Core Runnerで比較していましたが、先日1 Core Runnerであるubuntu-slimが発表されました。
すでに他の方がブログを記載しているため、ubuntu-slimの紹介はここでは記載しません。
はてなのSelf-hosted runnersでは、ubuntu-slimが出る前から、1 Core Runnerも用意していたため、1 Core Runnerでもパフォーマンス比較を始めました。
2 Coreのパフォーマンス比較
まずは、2 Core環境での比較を行います。Self-hosted runnerは、コンテナベースで構築しており、AMD64とARM64のそれぞれで測定しました。
なお、GitHub-hosted runnerにおけるarmは、自分たちでlabelを作成する必要があり、下記に記載するubuntu-24.04-arm-2xはこちらで作成したlabelです。
比較対象
| Runner種別 | Architecture | label | 備考 |
|---|---|---|---|
| GitHub-hosted | AMD64 | ubuntu-latest |
環境はVM |
| GitHub-hosted | ARM64 | ubuntu-24.04-arm-2x |
環境はVM |
| Self-hosted | AMD64 | Self-hosted (2Core/AMD) |
環境はコンテナ、スペックはCPU: 2 , Memory 8GB、インスタンスタイプはm6id |
| Self-hosted | ARM64 | Self-hosted (2Core/ARM) |
環境はコンテナ、CPU: 2 , Memory 8GB、インスタンスタイプはm8gd |
比較内容
コメントの(1)〜(4)は結果の表で使用します。
- name: sysbench 1 process 2 threads # (1) 整数演算 2スレッド並列処理のパフォーマンス id: cpu_test_sysbench_1_process_2_threads run: | sysbench cpu --cpu-max-prime=100000000 --threads=2 run - name: sysbench 2 processes 1 thread # (2) 整数演算 2コアのパフォーマンス id: cpu_test_sysbench_2_processes_1_thread run: | for i in $(seq 1 2); do (sysbench cpu --cpu-max-prime=100000000 --threads=1 run) & done wait - name: sysbench 2 processes 2 threads # (3) 整数演算 2コアの並列処理のパフォーマンス id: cpu_test_sysbench_2_processes_2_threads run: | for i in $(seq 1 2); do (sysbench cpu --cpu-max-prime=100000000 --threads=2 run) & done wait - name: stress-ng # (4) 行列計算 id: cpu_test_stress-ng run: | stress-ng --matrix 2 --matrix-ops 2500000
結果
3回の平均値を記載しています。
| 項目 | ubuntu-latest | ubuntu-24.04-arm-2x | Self-hosted (2Core/AMD) | Self-hosted (2Core/ARM) |
|---|---|---|---|---|
| 総実行時間 | 24m 19s | 12m 28s | 14m 48s | 14m 8s |
| (1) | 3m 23s | 2m 7s | 2m 14s | 2m 34s |
| (2) | 3m 23s | 2m 7s | 2m 14s | 2m 34s |
| (3) | 6m 46s | 4m 14s | 4m 29s | 5m 7s |
| (4) | 10m 23s | 3m 28s | 5m 36s | 3m 37s |
簡単な考察
ubuntu-latestはどの処理もあまり早くない- 特にmatrixのパフォーマンスが顕著に悪い
ubuntu-24.04-arm-2xはパフォーマンスに優れている- Self-hosted runnerはどちらのアーキテクチャも比較的パフォーマンスが良い
armはmatrixのパフォーマンスが良い
1 Coreのパフォーマンス比較
比較対象
| Runner種別 | Architecture | label | 備考 |
|---|---|---|---|
| GitHub-hosted | AMD64 | ubuntu-slim |
環境はコンテナ |
| Self-hosted | AMD64 | Self-hosted (1Core/AMD) |
環境はコンテナ、スペックはCPU: 1, Memory 4GB、インスタンスタイプはm6id |
| Self-hosted | ARM64 | Self-hosted (1Core/ARM) |
環境はコンテナ、スペックはCPU: 1, Memory 4GB、インスタンスタイプはm8gd |
比較内容
ubuntu-slimは15分の実行制限があり、あまり長いジョブを実行することができないため、2 Coreのパフォーマンス試験と一部異なるジョブを実行しています。
コメントの(1)〜(3)は上記と同様に結果の表で使用します。
- name: sysbench 1 threads # (1) 整数演算 1コア単体のパフォーマンス id: cpu_test_sysbench1 run: | sysbench cpu --cpu-max-prime=100000000 --threads=1 run - name: sysbench 2 threads # (2) 整数演算 1コアの並列処理のパフォーマンス id: cpu_test_sysbench2 run: | sysbench cpu --cpu-max-prime=100000000 --threads=2 run - name: stress-ng # (3) 行列計算 id: cpu_test_stress-ng run: | stress-ng --matrix 1 --matrix-ops 500000
結果
3回の平均値を記載しています。
| 項目 | ubuntu-slim | Self-hosted (1Core/AMD) | Self-hosted (1Core/ARM) |
|---|---|---|---|
| 総実行時間 | 13m 9s | 9m 22s | 9m 34s |
| (1) | 2m 3s | 2m 15s | 2m 34s |
| (2) | 7m 1s | 4m 39s | 5m 8s |
| (3) | 3m 46s | 2m 15s | 1m 27s |
簡単な考察
ほぼ2 Coreと同じような内容ですが、下記のようなことが言えます。
ubuntu-slimは1コア1スレッドのパフォーマンスはよいが、並列処理になるとパフォーマンスが劣化する- CPUのスペック(後述)を見る限りでは、
ubuntu-latestと同じマシンに載っているようだが、単純計算が比較的早い - 相当に単純なジョブであれば使用に問題はなさそう
- CPUのスペック(後述)を見る限りでは、
- Self-hosted runnerはコア数とスレッド数が比例して性能が出るのに対し、GitHub-hosted Runnerでは挙動が異なる
- 2コアの
ubuntu-latestで2 threadsのパフォーマンスと、1コアのubuntu-slimで1 threadのパフォーマンスが比例しない - Self-hosted runnerはどちらのアーキテクチャも比較的パフォーマンスが良い
armはmatrixのパフォーマンスが良い
まとめ
このブログでは、GitHub ActionsのRunnerにおける、CPUのパフォーマンス比較を行いました。CPUは処理内容によって差が大きいため、実際のジョブでは今回の結果ほどの差が出ないこともありますが、ジョブのパフォーマンスアップを考えたときにどこにボトルネックがあるのかの比較になれば幸いです。
すぐにSelf-hosted runnersを準備するのは難しいかもしれませんが、GitHub-hostedのArm runnerであればすでにGAとなっており利用可能です。対応できる部分からArmへ移行していくことが、ジョブの高速化への近道となりそうです。
パフォーマンス試験を行うきっかけとなったDisk I/Oに関しては、また別のブログで記載しますので、お待ち下さい。
おまけ
2025/11/25時点のubuntu-latestにおいてlscpuを実行すると、下記のようになっており、2 CoreというよりHyper-Threadingによる論理的な2 Coreであることがわかります。
Architecture: x86_64 CPU op-mode(s): 32-bit, 64-bit Address sizes: 48 bits physical, 48 bits virtual Byte Order: Little Endian CPU(s): 2 On-line CPU(s) list: 0,1 Vendor ID: AuthenticAMD Model name: AMD EPYC 7763 64-Core Processor CPU family: 25 Model: 1 Thread(s) per core: 2 Core(s) per socket: 1 Socket(s): 1 Stepping: 1
上記の"簡単な考察"のところでubuntu-latest,ubuntu-slimはともにマルチスレッドがかなり遅くなることを記載しましたが、このlscpuの結果が示す通り、物理コアではなくHyper-Threadingによる論理コアが割り当てられているためだと考えられます。
2 Coreの(3)の結果で時間がきれいに2倍(3m23s -> 6m46s)になっているのは、ubuntu-latestのパフォーマンスが維持されているというよりは、(1)や(2)の時点ですでに物理コアの演算能力が飽和しており、(3)では単純に処理待ちの行列が2倍になったためと考えられます。sysbenchのようなメモリ競合の少ないタスクでは、OSのスケジューリングによるオーバーヘッドが少ないため、こうしたきれいな線形の結果になりやすいようです。
ubuntu-24.04-arm-2xにおいてlscpuを実行すると、こちらは2 Coreになっていることがわかります。
Architecture: aarch64 CPU op-mode(s): 32-bit, 64-bit Byte Order: Little Endian CPU(s): 2 On-line CPU(s) list: 0,1 Vendor ID: ARM Model name: Neoverse-N2 Model: 0 Thread(s) per core: 1 Core(s) per socket: 2 Socket(s): 1