EC2インスタンスでハイパースレッディングを無効化する【OS RHEL 7系】

## はじめに
EC2インスタンス上に直接DBなどのミドルウェアをインストールする場合、
CPUの論理コア数（`vCPU`）によってライセンス料が計算されることがあります。

ここで、**パフォーマンス向上のためにインスタンスをスケールアップしたいが、
論理コア数増加によって大幅にライセンス料が上がってしまう**というときに、
ハイパースレッディング無効化によって論理コア数を調整することで、
スペック向上させつつライセンス料を抑えることができます。

## ハイパースレッディングとは

https://ja.wikipedia.org/wiki/%E5%90%8C%E6%99%82%E3%83%9E%E3%83%AB%E3%83%81%E3%82%B9%E3%83%AC%E3%83%83%E3%83%87%E3%82%A3%E3%83%B3%E3%82%B0

https://gigazine.net/news/20191230-simultaneous-multithreading/

正確には同時マルチスレッディング（Simultaneous multithreading = SMT）と呼ばれ、
1つのCPUコアで複数スレッドを同時処理することでCPUの利用効率を上げ、
パフォーマンスを向上させる技術になります。

ハイパースレッディングはIntel CPUでの機能名ですが、
自分としてはこちらに聞き馴染みがあったので記事名にも使っています。

### EC2での例

例えば`m5.xlarge`であれば、物理コア数が2、論理コア数が4となっていて、
これはCPUコア1つに対して2スレッドが割り当てられていることを指します。
ただ、無効化時と比べてパフォーマンスが2倍になるかというとそうではなく、
検証方法にもよりますが10%~50%程度パフォーマンスが向上するとされています。

同じインスタンスファミリーで1つ上のインスタンスタイプにすると物理コア数が2倍になるので、
仮にハイパースレッディングを無効化しても、パフォーマンスは向上するはずです。

```
例
現状： m5.xlarge ハイパースレッディングON
物理コア数 2, 論理コア数 4

候補1： m5.2xlarge ハイパースレッディングOFF
物理コア数 4, 論理コア数 4

候補2： m5.2xlarge ハイパースレッディングON
物理コア数 4, 論理コア数 8
```
上の例だと、パフォーマンスは `現状 < 候補1 < 候補2` となることが予想されますが、
候補2のライセンス料が過大となる場合、候補1が選択肢になります。

## 設定方法

設定時のポイントは以下の2つです
1. 論理コア数を指定して起動することで、**物理コア数＝論理コア数** の状態とする
2. 論理コア数指定をしてからスケールアップする

1について、ここまでハイパースレッディング無効化と言ってきましたが、
Linux OSにSMT有効化・無効化フラグがあるわけではなく、論理コア数を指定して起動する形になります。

2について、スケールアップしてから設定だと、
利用するミドルウェアによっては一時的にライセンス違反となる可能性があります。
そのため、論理コア数を予め指定してからスケールアップします。
単にハイパースレッディングを無効化して検証したいだけであれば考慮不要です。

### 1. 現在のCPU設定を確認
`lscpu`コマンドで現在のCPU設定を確認します。

```
> lscpu

Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                4
On-line CPU(s) list:   0-3
Thread(s) per core:    2
```

上の結果はm5.xlargeインスタンスの例です。
CPU(s)の数字が論理コア数、Thread(s) per coreが物理コア1つに対してのスレッド割当数になります。
この場合、Thread(s) per coreは2で、物理コア1つに対して2スレッドが割り当てられています。
つまり、物理コア数は2、論理コア数は4のインスタンスだとわかります。
	
### 2. Kernelパラメータに論理コア数の設定を追加
Kernelパラメータで論理コア数を設定することができます。
この方法だと再起動で設定がリセットされることがありません。

設定する対象ファイルとパラメータはOSによって異なりますが、
この記事では、RHEL 7系で検証しているため、
設定ファイルは`/etc/sysconfig/grub` または `/etc/default/grub`、パラメータは`nr_cpus`となります。
また、ファイル編集後にgrub.cfgを再生成する必要があるので注意です。

```
# 編集
> vi /etc/sysconfig/grub
GRUB_CMDLINE_LINUX="元々の設定 nr_cpus=4"

# grub.cfgの再生成
> grub2-mkconfig -o /boot/grub2/grub.cfg

# 確認
> cat /boot/grub2/grub.cfg | grep nr_cpus
```

上の例では、m5.xlargeからm5.2xlargeへスケールアップする場合を想定しています。
スケールアップ前に、2xlargeの物理コア数である `nr_cpus=4` を追加します。
こうすることで、スケールアップ時に物理コア数＝論理コア数となります。
	
### 3. インスタンス停止
### 4. インスタンスタイプを1つ上げて起動
### 5. 確認
再度`lscpu`コマンドで確認します。Thread(s) per coreが1となっていればOKです。
```
> lscpu	

Architecture:          x86_64
CPU op-mode(s):        32-bit, 64-bit
Byte Order:            Little Endian
CPU(s):                4
On-line CPU(s) list:   0-3
Thread(s) per core:    1
```
上の例では、`nr_cpus=4`を指定してm5.2xlargeへスケールアップしています。
本来ならCPU(s)は8となりますが、4になっているので物理コア数と一致しています。

## 参考：各OSでの設定

| OS | 設定ファイル | パラメータ | 備考 |
|:-:|:-:|:-:|:-:|
| RHEL 7 | `/etc/sysconfig/grub` または `/etc/default/grub`  | nr_cpus | この記事で紹介。RHEL 8系以降も同じはずですが未検証です。 |
| RHEL 6 | `/boot/grub/grub.conf` | maxcpus | 未検証。[参考記事](https://zokibayashi.hatenablog.com/entry/2017/03/02/205414)  |
| Ubuntu 20.04  | `/etc/default/grub` | maxcpus | 未検証。[参考](https://manpages.ubuntu.com/manpages/focal/ja/man7/bootparam.7.html) |

## 参考：AWS CLIで論理コア数を指定して起動する

https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instance-specify-cpu-options.html

実はAWS CLIで起動時に直接論理コア数を指定して起動することもできます。
ちょっと試すだけならこちらのほうがお手軽かもしれません。
今回は、スケールアップに合わせてnr_cpus以外のパラメータも変更が必要だったため、
Kernelパラメータの編集で対応しました。

## 最後に

ハイパースレッディング無効化によって、物理コア数＝論理コア数とする設定方法を紹介しました。
意外と簡単だったのではないでしょうか。
あまり利用機会は無いと思いますが、手段の1つとして覚えておくといつか役に立つはずです。