マイニングの電力効率をBluetoothワットチェッカーで最適化しよう

## 動機

GPUの設定（NVIDIAなら `nvidia-smi`）でGPUの消費電力の上限を設定することができるので，GPUの消費電力あたりのハッシュレートを最適化するだけならワットチェッカーなしでも行えます。しかしながら，GPU以外の電源装置やCPUなどの消費電力のことも考えると，ワットチェッカーを使ってシステム全体の電力を測定必要があると考えられます。そこで消費電力のログをBluetoothで取得できるワットチェッカーを使ってマイニングの電力効率を最適にする設定を探しました。

使用したワットチェッカーは以下のものです。

[Bluetoothワットチェッカー REX-BTWATTCH1](http://www.ratocsystems.com/products/subpage/btwattch1.html)

## Bluetoothアダプタの選定

ログを保存しておくために別のPCを用意するのも面倒なので，マイニングを行うPCにUSBタイプのBluetoothアダプタを取り付けてワットチェッカーと接続することにしました。以下のURLによると動作しないものもあるということなので注意しておいたほうが良さそうです。

http://www.ratocsystems.com/products/subpage/eco/btwattch1_usbadapter.html

## Linuxからワットチェッカーに接続する

公式に提供されているツールはWindows用とAndroid/iOS用のものしかなく，プロトコルの仕様書なども公開されていないのですが，以下のページでLinuxから接続する方法が解説されています。

[Armadillo-IoT G3で「Bluetooth ワットチェッカー」から計測データを表示](https://armadillo.atmark-techno.com/howto/armadillo_rex-btwattch1)

サンプルプログラムを動かすとこんな感じになります↓

![スクリーンショット 2018-01-18 16.50.58.png](https://qiita-image-store.s3.amazonaws.com/0/154409/7f8c6cab-373a-c396-8b04-04dde7d4184b.png)

私はbluetoothctl, sdptoolやrfcommなどのコマンドの意味をちゃんと理解していないのであまり解説できないのですが，上記のページの通りにコマンドを実行すると動作させることができました。少しだけ罠もあるので紹介します。

### だいたいのコマンドを実行するときには `sudo` が必要

`sudo` 無しで実行しても "Permission denied" というようなメッセージが表示されないものがあるので注意しましょう。

たとえば `bluetoothctl` を実行するときには `sudo` なしで実行するとプロンプトは表示されるのですが，何も入力を受け付けてくれず，ハングアップしたような見た目になります。（Ctrl-Cは動く）。

`sdptool add SP` を実行するときには `sudo` なしで実行すると何も表示されずに終了します（コマンドの終了コードは非ゼロです）。`Serial Port service registered` という出力がないといけません。

### bluetoothdのためのsystemdの設定に `--compat` を追加するときには `systemctl edit` ではうまくいかない

上記のページによれば，sdptoolを使用するためにbluetoothdのサービスを起動するときのオプションに `--compat` が必要なので， `/etc/systemd/system/dbus-org.bluez.service` を直接編集するように説明があります。しかしながら，一般にsystemdで既存のunitの設定ファイルを直接編集するのは良くないこととされていて， `systemctl edit` を使って設定ファイルの差分だけを記述する方法が用意されています。詳細は以下のページを参照して下さい。

[systemdで既存のunitを編集する2つの方法 - Qiita](https://qiita.com/nvsofts/items/529e422bb8a326401c39)

しかしながら，理由はよくわからないものの， `systemctl edit` を使った場合には `--compat` オプションが正しく適用されていないようなのです。仕方がないので設定ファイルを直接編集する方法を取りました。

## ワットチェッカーのログを保存する

公開されているC言語のサンプルプログラムがそのまま動作したので，出力をそのままファイルに保存することにしました。ただしプログラムの出力がバッファされてしまっているようで，直ちにファイルに書き込まれるわけではないようなので， `unbuffer` などを使ってバッファされないようにしたほうが良いと思います。

```
sudo unbuffer ./wattchecker /dev/rfcomm0 | tee -a ~/watt_logs/watt.log
```

ログはこんな感じになります（プログラムのほうを修正すればなんとでもなるのですが，ご自由にどうぞ）

```
2017/12/26 16:53:38
voltage = 105.22V , current = 365.26mA , power = 36.37W
2017/12/26 16:53:40
voltage = 105.29V , current = 365.93mA , power = 36.45W
2017/12/26 16:53:42
voltage = 105.32V , current = 365.20mA , power = 36.33W
2017/12/26 16:53:44
voltage = 105.16V , current = 363.63mA , power = 36.15W
2017/12/26 16:53:46
voltage = 105.18V , current = 364.60mA , power = 36.29W
```

パースするときには以下のような関数を使いました。

```python
import re
import datetime

def parse_watt(iters):
    pattern = r'^voltage = (?P<voltage>\d+\.\d+)V , current = (?P<current>\d+\.\d+)mA , power = (?P<power>\d+\.\d+)W$'
    for line in iters:
        line = line.rstrip('\n')
        m = re.match(pattern, line)
        if m:
            time_str = prev_line
            date = datetime.datetime.strptime(time_str, r'%Y/%m/%d %H:%M:%S')
            yield (date, float(m.group('power')))
        prev_line = line
```


## GPUの設定を変更しながらベンチマークを動かす

ワットチェッカーのログが取れるようになったので，GPUの電力設定を変更しながらベンチマーク動かしてハッシュレートを調べたいと思います。

今回は NVIDIAのGPUで [tpruvot/ccminer](https://github.com/tpruvot/ccminer) を動かしました。ハッシュ計算のアルゴリズムはLyra2v2を使いました。使用したスクリプトを以下に示します。

```bash
#!/bin/bash
set -ue -o pipefail
export LC_ALL=C

nvidia-smi -pm 1
for pl in $(seq 60 2.5 120); do
  # sudo で動かさないと --plimit が動作しないので注意
  hashrate=`./ccminer -a lyra2v2 --benchmark --time-limit 90 --plimit $pl 2>&1 >/dev/null`
  printf "`date`\t${pl}\t${hashrate}\n"
done
```

このスクリプトではGPUの消費電力の上限を60Wから120Wまで2.5Wごとに変化させて，それぞれの設定ごとに90秒間のベンチマークを動作させてハッシュレートを記録しています。

どこかのマイニングプールに接続してマイニングしながら計測を行うと無駄がなくて良いと思うかもしれませんが，タスクの待ち時間などでGPUが動いていない期間が発生してしまい，計測が不正確になってしまうのでやめたほうがいいです。代わりに `--benchmark` オプションを使ってGPUを全力で動かす設定にしましょう。 `--time-limit` はベンチマークを動かす時間を秒で指定するオプションです。 `--plimit` はGPUの消費電力の上限をワットで指定するオプションで `nvidia-smi -pl` と同様に設定に応じて動作周波数などを調整してくれます。

`--benchmark` モードでは指定した時間（今回は90秒）が経過したら標準エラー出力にハッシュレートを出力するので， `2>&1 >/dev/null` とすることでハッシュレートを標準出力にリダイレクトさせつつ，もともとの標準出力を捨てています。

## 結果

測定したシステムの構成は以下のとおりです。

<table>
  <tr><td>CPU</td><td>Intel(R) Pentium(R) CPU G4560 @ 3.50GHz (TDP 54W)</td></tr>
  <tr><td>電源</td><td>Antec NeoECO Classic NE550C (80PLUS BRONZE)</td></tr>
  <tr><td>GPU</td><td>NVIDIA GeForce GTX 1060 (3GB)</td></tr>
</table>

### システム全体の電力とccminerのplimitオプションの関係

![plimit-and-power.png](https://qiita-image-store.s3.amazonaws.com/0/154409/26def10d-f00f-9f7b-8eba-823bd6a4bd36.png)

横軸はccminerのplimitオプションで設定した値，縦軸はワットチェッカーで計測したシステム全体の消費電力です。エラーバーは90秒間のベンチマーク中のシステム全体の消費電力の標準偏差です（ワットチェッカーによるログ取得は2秒に1回行われるため）。

70Wあたりに不思議な傾向が見られました。GPUの電源制御の仕組みなどが関係しているのかもしれませんが，理由はよく分かりませんでした。知っている人がいたら教えてください。

### ハッシュレートとccminerのplimitオプションの関係

![plimit-and-hashrate.png](https://qiita-image-store.s3.amazonaws.com/0/154409/b748607f-7f40-c2b5-a07e-3653bcdd2f77.png)

横軸はccminerのplimitオプションで設定した値，縦軸はccminerのハッシュレートです。当たり前ですが，plimitの設定を高くすればするほどハッシュレートは高くなります。

### ハッシュレートとシステム全体の電力の関係

![hashrate-and-power.png](https://qiita-image-store.s3.amazonaws.com/0/154409/6886b06e-ad21-11c3-d61e-bbf8cd3ff0ed.png)

縦軸はワットチェッカーで計測したシステム全体の消費電力（90秒間の平均値），縦軸はccminerのハッシュレートです

### 電力あたりのハッシュレートとccminerのplimitオプションの関係

![hashrate-per-power-and-plimit.png](https://qiita-image-store.s3.amazonaws.com/0/154409/0ef40b86-b883-24e6-e550-19c88339aed2.png)

横軸はccminerのplimitオプションで設定した値，縦軸はシステム全体の消費電力あたりのハッシュレートです。

この実験で一番重要なグラフです。今使っているGPUではplimitの設定を72.5Wにしたときに消費電力あたりのハッシュレートが最適になるということがわかりました。

## 結論

Bluetoothワットチェッカーを使って消費電力を記録することでマイニングの電力効率の最適化を試みました。

今後はccminerの `--pstate`, `--cpu-clock`, `--mem-clock` あたりのオプションの最適化にも取り組んでみたいです。

## 付録 A. SPP，SDP，RFCOMMについて

SPPとかSDPとかRFCOMMとかいろいろな登場人物が登場するので，深く理解したい人は以下の資料あたりから調べてみるといいかもしれません。

* [Bluetoothを極める、間違いだらけのBluetoothドングル/アダプタ選び | ひろぶろぐ](https://hirofun.com/computer/pc/10085/)
* [21.4. Bluetooth | FreeBSD ハンドブック](https://www.freebsd.org/doc/ja_JP.eucJP/books/handbook/network-bluetooth.html)
* [Bluetooth - ArchWiki](https://wiki.archlinux.jp/index.php/Bluetooth)
* [List of Bluetooth protocols - Wikipedia](https://en.wikipedia.org/wiki/List_of_Bluetooth_protocols#Radio_frequency_communication_(RFCOMM))