少し前にISUCON10予選問題のスコアアップに取り組んでいたので、その成果を使い、EC2 Graviton2 インスタンスとIntel CPUのインスタンスでスコアを比較してみます。
ISUCON10予選問題チャレンジをしていた時のスレはこちら
ISUCON10予選のベンチマーク、Macbook Pro上で、23000まででるようになった。Xeon Gold 6148 x2上だと37000。
— スタンドの時間です! (@kazeburo) 2020年10月28日
ここではISUCON10予選時のように複数台用意するのではなく、1台のサーバでGoのアプリケーションとMySQLを動作させ、同じサーバ上からベンチマークを実行しています。
準備
初期データを作る際のdockerコンテナでarm64対応していないものがあるため、Intel CPUのインスタンスで作成してscpする方法を取りました。
まず、 c5.2xlarge
のインスタンスを起動して準備していきます
# 依存のインストール sudo apt-get update sudo apt-get install -y apt-transport-https ca-certificates curl gnupg-agent software-properties-common python3-pip git curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo apt-key add - # Dockerをいれる sudo add-apt-repository \ "deb [arch=$(dpkg --print-architecture)] https://download.docker.com/linux/ubuntu \ $(lsb_release -cs) \ stable" sudo apt-get update sudo apt-get install -y docker-ce docker-ce-cli containerd.io sudo gpasswd -a $USER docker # 一度logout & login # docker-compose を pip でインストール sudo pip3 install -U docker-compose # Goのインストール curl -fsSL https://golang.org/dl/go1.15.6.linux-$(dpkg --print-architecture).tar.gz | sudo tar -C /usr/local -xzf - export PATH=$PATH:/usr/local/go/bin echo 'export PATH=$PATH:/usr/local/go/bin' | sudo tee /etc/profile.d/go.sh # 初期データ作成で使うコマンドのインストール go get github.com/orisano/wayt export PATH=$PATH:$HOME/go/bin echo 'export PATH=PATH=$PATH:$HOME/go/bin' | tee -a ~/.bashrc # ISUCON10 予選問題をgit cloneして初期データ作成 git clone https://github.com/isucon/isucon10-qualify.git cd isucon10-qualify/initial-data pip3 install -r requirements.txt make
初期データ作成時に一度止まってしまいましたが、Ctrl-Cしてもう一度実行したらうまく動きました。
MySQLをインストールして、ユーザ作成をします。簡単なパスワードになるので気をつけましょう。
sudo apt install mysql-server sudo mysql > CREATE USER 'isucon'@'%' IDENTIFIED BY 'isucon'; > GRANT ALL ON *.* TO `isucon`@`%`; > CREATE DATABASE isuumo;
ここまでできたら c6g.2xlarge
側の準備をします。
c6g.2xlarge
でインスタンスを起動し、
sudo apt-get update sudo apt-get install -y mysql-server git make export PATH=$PATH:/usr/local/go/bin echo 'export PATH=$PATH:/usr/local/go/bin' | sudo tee /etc/profile.d/go.sh sudo mysql > CREATE USER 'isucon'@'%' IDENTIFIED BY 'isucon'; > GRANT ALL ON *.* TO `isucon`@`%`; > CREATE DATABASE isuumo;
このあと、Intel CPUのインスタンスから、isucon10-qualify
ディレクトリを scpで Graviton2 のインスタンスにコピーすれば準備完了です。
初期状態でのスコア比較
まず、Intel CPUである c5.2xlarge での初期スコア
isuumoアプリを make
して起動
cd isucon10-qualify/webapp/go make ./isuumo
別ターミナルを開き、ベンチマークを実行します。
cd isucon10-qualify/bench make ./bench
初期スコア
初期状態ではタイムアウトも多くますが、最終的に以下のスコアになりました。
c5.2xlarge
での初期スコア
2020/12/23 13:26:06 bench.go:102: 最終的な負荷レベル: 8 {"pass":true,"score":1335,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":34}],"reason":"OK","language":"go"}
c6g.2xlarge でも同じようにターミナルを2つ開いてアプリケーションとベンチマークを実行します。
c6g.2xlarge
での初期スコア
2020/12/23 14:18:14 bench.go:102: 最終的な負荷レベル: 9 {"pass":true,"score":1552,"messages":[{"text":"POST /api/estate/nazotte: リクエストに失敗しました (タイムアウトしました)","count":48}],"reason":"OK","language":"go"}
初期状態では、c6g.2xlarge
の方が 16%
ほどスコアがよいという結果になりました。
ベンチマーク時の負荷の様子を top
コマンドでみると次のようになっておりました。
c5.2xlarge
top - 14:15:39 up 10 min, 3 users, load average: 7.18, 2.64, 0.96 Tasks: 146 total, 2 running, 144 sleeping, 0 stopped, 0 zombie %Cpu(s): 85.6 us, 3.2 sy, 0.0 ni, 10.3 id, 0.5 wa, 0.0 hi, 0.4 si, 0.0 st MiB Mem : 15549.2 total, 12448.3 free, 929.9 used, 2171.0 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 14411.4 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 777 mysql 20 0 2989500 506064 37208 S 638.9 3.2 9:38.23 mysqld 1331 ubuntu 20 0 1526204 34796 7164 S 44.9 0.2 0:36.05 isuumo 1486 ubuntu 20 0 1521292 139536 7692 S 30.0 0.9 0:07.15 bench
c6g.2xlarge
top - 14:18:03 up 16 min, 3 users, load average: 5.98, 2.22, 1.13 Tasks: 189 total, 2 running, 187 sleeping, 0 stopped, 0 zombie %Cpu(s): 92.9 us, 4.0 sy, 0.0 ni, 1.7 id, 0.3 wa, 0.0 hi, 1.1 si, 0.0 st MiB Mem : 15709.3 total, 11525.9 free, 880.3 used, 3303.1 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 14661.9 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 3516 mysql 20 0 3049328 490156 34860 S 707.0 3.0 11:29.75 /usr/sbin/mysqld 4896 ubuntu 20 0 1598624 40468 6208 S 39.5 0.3 0:46.90 ./isuumo 5193 ubuntu 20 0 1661656 153140 6860 S 33.9 1.0 0:13.45 ./bench
明らかにMySQLがボトルネックですが、c6g.2xlarge の方がidleが少なくCPUをよく使えているようにみえます。
感想戦ブランチでのスコア比較
次に、私のブランチへ切り替えてスコア比較をしてみます。
ブランチはこちら
効果があまりなかったものもありますが、主な変更点は次のようになります。
- nazotte検索に空間インデックスを利用
- 検索用テーブルと、データ用テーブルを分離してMySQLの負荷を削減
- 検索用インデックスの追加。
- 検索時にrangeクエリにならないようrangeIDカラムを作成しtriggerにてデータ投入
- 検索時にSQL_CALC_FOUND_ROWSを利用。結果をキャッシュしておき、同条件での検索時はSQL_CALC_FOUND_ROWSをつけずにキャッシュを利用する
- chairとestateは全てインメモリキャッシュ。検索時は
SELECT id
をする - 検索結果やchairとestateをキャッシュする際は、JSON化したものをキャッシュする
- bot判定を最適化
- fiber (fasthttp) へのフレームワーク変更
- MySQLに秘伝のタレを入れる
感想戦ブランチでのスコア
c5.2xlarge
での感想戦ブランチスコア
$ ./bench 2020/12/23 14:34:27 bench.go:78: === initialize === 2020/12/23 14:34:32 bench.go:90: === verify === 2020/12/23 14:34:32 bench.go:100: === validation === 2020/12/23 14:34:34 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:36 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:38 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:38 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:38 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:38 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:39 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:34:39 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:35:32 bench.go:102: 最終的な負荷レベル: 11 {"pass":true,"score":41462,"messages":[],"reason":"OK","language":"go"}
初期状態では負荷レベルが最大の11までには届きませんでしたが、ここでは7秒ほどで最大に達し、最終スコアは4万を超えてきました。これはMacbook Pro上でやっていたときより若干良い結果
c6g.2xlarge
での感想戦ブランチスコア
ubuntu@ip-10-0-11-141:~/isucon10-qualify/bench$ ./bench 2020/12/23 14:36:20 bench.go:78: === initialize === 2020/12/23 14:36:30 bench.go:90: === verify === 2020/12/23 14:36:31 bench.go:100: === validation === 2020/12/23 14:36:33 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:34 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:35 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:36 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:36 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:36 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:37 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:36:38 load.go:181: 負荷レベルが上昇しました。 2020/12/23 14:37:31 bench.go:102: 最終的な負荷レベル: 11 {"pass":true,"score":39460,"messages":[],"reason":"OK","language":"go"}
Intel CPUに比べると5%ほど低いスコアです。何回か実行してみましたが、4万に届くことはありませんでした。
ベンチマーク実行中の負荷をtopコマンドでみると次のようになっていました。
c5.2xlarge
top - 14:45:24 up 39 min, 3 users, load average: 3.68, 2.66, 2.15 Tasks: 147 total, 2 running, 145 sleeping, 0 stopped, 0 zombie %Cpu(s): 57.6 us, 4.3 sy, 0.0 ni, 36.8 id, 0.0 wa, 0.0 hi, 1.3 si, 0.0 st MiB Mem : 15549.2 total, 10447.3 free, 2096.7 used, 3005.3 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 13346.3 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 3879 ubuntu 20 0 1663700 163836 7724 S 277.3 1.0 1:00.39 bench 1691 mysql 20 0 3335408 651500 37268 S 189.7 4.1 22:25.35 mysqld 3650 ubuntu 20 0 2572288 1.0g 15200 R 45.0 6.7 2:13.37 isuumo
c6g.2xlarge
top - 14:40:43 up 39 min, 3 users, load average: 1.44, 1.86, 1.69 Tasks: 185 total, 1 running, 184 sleeping, 0 stopped, 0 zombie %Cpu(s): 58.2 us, 3.6 sy, 0.0 ni, 37.0 id, 0.0 wa, 0.0 hi, 1.2 si, 0.0 st MiB Mem : 15709.3 total, 9707.4 free, 2011.2 used, 3990.7 buff/cache MiB Swap: 0.0 total, 0.0 free, 0.0 used. 13669.2 avail Mem PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 7423 ubuntu 20 0 1810080 205848 6788 S 268.8 1.3 0:59.13 ./bench 5417 mysql 20 0 3724652 652440 35008 S 203.0 4.1 18:10.73 /usr/sbin/mysqld 7352 ubuntu 20 0 2499408 986528 14492 S 38.9 6.1 0:50.62 ./isuumo
感想戦ブランチでは全体のCPU使用率が下がっています。大きな差ではありませんが、Intel CPUの方がMySQLのCPU使用率が小さくベンチマークとisuumoアプリのCPU使用率が大きいようにみえます。
感想など
tasksetコマンドを使い、benchコマンドが使うCPUを1個に絞った結果も合わせたグラフが次ようになります。
Graviton2はIntel CPUのインスタンスに比べてシングルスレッドのパフォーマンスが低めというベンチマーク結果もありますが、
この結果からも初期状態ではMySQLでのマルチスレッド性能が、感想戦ブランチではベンチマーカーのシングルスレッド性能がスコアに影響しているのかなと考えております。
感想戦ブランチではGraviton2のインスタンスの方がスコアが低いという結果になってしまいましたが、インスタンスのコストはGraviton2の方が20%ほど低く、スコアをコストで割った数値ではGraviton2の方が高くなるでしょう。
armなインスタンスの利用ではミドルウェアのパッケージやdockerのイメージが用意されていないことがあるなど、いくつか困難にぶつかることはありますが、パフォーマンスがよくコストメリットも大きいのでやはり使っていくといいんじゃないかと思いますね~