3行で
- 平行に処理してみたりキャッシュが効くようにしたが早くはならなかった
- やってみたかったのでやった
- 後悔はしていない
その時のPR: github.com
チェックを並列に実行するが効果なし(むしろ増)
これまでのコミットチェックではAndroidのLintとktlint、UnitTestをstep
で順番に行っていた。高速化と言ったら並列化だろ、ってことで、PRのタイトルにもした通りまずはこれらのstep
を分割してjob
に昇格し、平行に実行させてみた。ついでに、共通で行うコンパイルの処理を前段に、レポートの処理を後段で行うようにしたらちょっとしたビルドパイプラインになった。
github actionsでは、jobs
の下に列挙したjobはすべて平行に実行される。指定したjob
の終了を待ってから開始したいjob
はその設定でjobs.needs: <<前段のjob-id>>
を書いておく。前段のjob-id
は[ ]
で囲んで複数指定できる。今回のパイプラインはjob
だけ書くと次のようになる。
- jobs - compile - lint - needs: compile - ktlint - needs: compile - unittest - needs: compile - report - needs: [lint, ktlint, unittest]
あとはそれぞれのjob
でGradleのキャッシュをリストアする処理などが行われるように設定していく。キャッシュのリストアのオーバーヘッドはあるだろうけどまあちょっとぐらいは早くなるでしょという期待を胸にワークフローを走らせてみたところ、これまで15分程度だった実行時間が20分ぐらいかかるようになってしまった。どうやらキャッシュのリストアが長すぎるというのと、ビルドキャッシュが効いていないというのが原因のようだったのでこれの対策を行うことにした。
キャッシュをちゃんとやる
キャッシュは2GB程度あり、リストアには2分程度かかっていた。これを前段でも中段でもやっているのでどうにかして節約したい。キャッシュにはactions/cache
を使っていて、当初はマニュアルのGradleの例に書いてる設定をほぼそのまま流用していた。しかし、この設定だとキーが変わったりキャッシュを意識的に消す処理をしない限りこれまで使っていたバージョンのwrapperやらcachesのなんやかやが全てキャッシュに残ったままになってしまい *1 、雪だるま式にサイズが大きくなってしまうのではないかと考えた。そこで、キャッシュサイズを小さくしつつ、かつサイズが必要以上に大きくなりすぎないようにするための方法を考えることにした。
現時点でのGradle 6.8.3において、私の環境では次のディレクトリにキャッシュが保存される様子。
- ~/.gradle
- caches
- wrapper
- <<プロジェクトルート>>/.gradle
このうち、wrapperディレクトリの下にはgradle wrapperのバイナリがバージョンごとに分かれて入っている。これがバージョンごとに200~300MB程度あり、実際に使うのは1つだけなのでそれ以外は不要である。まずはここを削るために次のようにした。
- name: cache gradle wrapper uses: actions/cache@v2 with: path: ~/.gradle/wrapper key: ${{ runner.os }}-gradle-wrapper-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
使用するgradle wrapperのバージョンはgradle-wrapper.propertiesに書いてあるものを使うのでこのファイルのハッシュをキーに使うことにして、このキーでキャッシュが見つからない時は代わりのキャッシュをリストアしないようrestore-keys
を指定しないようにした。
あとはライブラリのキャッシュやビルドキャッシュを保存する部分だが、ここはあっさりと、
- name: cache gradle uses: actions/cache@v2 with: path: | ~/.gradle/caches ./.gradle key: ${{ runner.os }}-gradle-cache-${{ github.sha }} restore-keys: | ${{ runner.os }}-gradle-cache- - name: cache build uses: actions/cache@v2 with: path: ./**/build key: ${{ runner.os }}-assemble-${{ github.sha }} restore-keys: | ${{ runner.os }}-assemble-
という感じにしてみた。キーにはgithub.sha
を付けてcommit checkごとに新しくなるようにした。ただし、~/.gradle/caches
や./.gradle
の下にはバージョン番号のディレクトリがあり、Gradleのバージョンが変わるごとに増えていくと考えられるがこれを消す処理は入れていない。消そうとせずにここのキーにもgradle-wrapper.properties
のハッシュをつけてもいいかもしれない。
この改善によって、2GB近くあったキャッシュが500MB程度になり、20分に膨れ上がった実行時間が17分にまで抑えられた。キャッシュのない初回の実行なので次回以降はもう少し短くなるはず。あとはmasterブランチにマージするワークフローでも同じようなこと*2をやってやれば次のPRの時にもこのキャッシュを引き継げる。