Hateburo: kazeburo hatenablog

SRE / 運用系小姑 / Goを書くPerl Monger

ISUCON10予選問題で EC2 c6g.2xlarge vs c5.2xlarge をやってみる

少し前にISUCON10予選問題のスコアアップに取り組んでいたので、その成果を使い、EC2 Graviton2 インスタンスIntel CPUのインスタンスでスコアを比較してみます。

ISUCON10予選問題チャレンジをしていた時のスレはこちら

ここでは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をよく使えているようにみえます。

感想戦ブランチでのスコア比較

次に、私のブランチへ切り替えてスコア比較をしてみます。

ブランチはこちら

github.com

効果があまりなかったものもありますが、主な変更点は次のようになります。

  • nazotte検索に空間インデックスを利用
  • 検索用テーブルと、データ用テーブルを分離してMySQLの負荷を削減
  • 検索用インデックスの追加。
  • 検索時にrangeクエリにならないようrangeIDカラムを作成しtriggerにてデータ投入
  • 検索時にSQL_CALC_FOUND_ROWSを利用。結果をキャッシュしておき、同条件での検索時はSQL_CALC_FOUND_ROWSをつけずにキャッシュを利用する
  • chairとestateは全てインメモリキャッシュ。検索時は SELECT id をする
  • 検索結果やchairとestateをキャッシュする際は、JSON化したものをキャッシュする
  • bot判定を最適化
  • fiber (fasthttp) へのフレームワーク変更
  • MySQLに秘伝のタレを入れる

いれたMySQLに秘伝のタレ

disable-log-bin
innodb_doublewrite = 0
innodb_flush_log_at_trx_commit = 0
innodb_flush_method = O_DIRECT
innodb_adaptive_hash_index = 0

感想戦ブランチでのスコア

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個に絞った結果も合わせたグラフが次ようになります。

f:id:kazeburo:20201224103305p:plain

Graviton2はIntel CPUのインスタンスに比べてシングルスレッドのパフォーマンスが低めというベンチマーク結果もありますが、

www.anandtech.com

この結果からも初期状態ではMySQLでのマルチスレッド性能が、感想戦ブランチではベンチマーカーのシングルスレッド性能がスコアに影響しているのかなと考えております。

感想戦ブランチではGraviton2のインスタンスの方がスコアが低いという結果になってしまいましたが、インスタンスのコストはGraviton2の方が20%ほど低く、スコアをコストで割った数値ではGraviton2の方が高くなるでしょう。

armなインスタンスの利用ではミドルウェアのパッケージやdockerのイメージが用意されていないことがあるなど、いくつか困難にぶつかることはありますが、パフォーマンスがよくコストメリットも大きいのでやはり使っていくといいんじゃないかと思いますね~