MacBook Pro (M1 Pro 10 Core)の実力

# はじめに

2021年10月に発売されたMacBook Proを購入したので、今まで使用していた2013年のMacBook Proと処理時間を比較してみました。結論を先に言ってしまうと、その早さに驚愕です。

||MacBook Pro 2013|MacBook Pro 2021|
|-|-|-|
|PC|MacBook Pro (Retina, 13-inch, Late 2013)|MacBook Pro (14インチ、2021)|
|CPU|2.6GHz デュアルコアIntel Core i5|Apple M1 Pro|
|Memory|8GB|16GB|
|OS|macOS Big Sur (11.6)|macOS Monterey (12.0.1)|
|Python|3.8|3.9|


# プログラムの説明

8607社の株式の時系列データを下記計算式にフィットさせaの値を求めます。

```math
y = e^{ax}
```

データを正規化するために、x=0のとき、y=1となるうようにデータを調整しています。1銘柄に対して、2011年から2016年、2012年から2017年、・・・、2017年から2021年のようにして7期間のaを算出して、結果をExcelファイルに出力してます。
起動時の引数としてワーカー数を指定します。concurrent.futuresにこのワーカー数を指定して並列処理をさせます。

プログラムは下記のようになります。

```python
import sys
import concurrent.futures
import numpy as np
from scipy.optimize import curve_fit
import pandas as pd
import stocktools

PERIOD_FROM = '2011-01-01'
PERIOD_TO = '2021-09-30'
PERIODS = [
    ['2011-01-01', '2015-12-31'],
    ['2012-01-01', '2016-12-31'],
    ['2013-01-01', '2017-12-31'],
    ['2014-01-01', '2018-12-31'],
    ['2015-01-01', '2019-12-31'],
    ['2016-01-01', '2020-12-31'],
    ['2017-01-01', '2021-09-30'],
]


def analyze_period(df_all, from_date, to_date):
    def func(x: int, a: int) -> int:
        return a ** x

    df = df_all[from_date: to_date].copy()
    if len(df) == 0:
        return None

    # 初期値が1となるように正規化
    if df['close'].iloc[0] == 0:
        df['close'] = df['close'] + 1
    df['nclose'] = df['close'] / df['close'].iloc[0]
    df['days'] = [x.days for x in df.index - df.index[0]]

    param, cov = curve_fit(func, df['days'], df['nclose'])
    df['pred'] = [func(x, param[0]) for x in df['days']]
    df['delta'] = df['nclose'] / df['pred'] - 1
    mse = np.sqrt(np.sum(df['delta'] ** 2))
    mse = mse / df['pred'].iloc[-1]

    result = {'base': param[0],
              'nclose': df['nclose'][-1],
              'pred': df['pred'][-1],
              'mse': mse}
    return result


def analyze(ticker):
    # print(f'{ticker}')

    data = stocktools.get_stock(ticker, PERIOD_FROM, PERIOD_TO)
    if data is None or len(data) == 0:
        return None

    df = data[['Close']].copy()
    df.columns = ['close']

    result = {'ticker': ticker}
    for i, period in enumerate(PERIODS):
        r = analyze_period(df, period[0], period[1])
        if r is not None:
            r2 = {}
            for key in r.keys():
                r2[f'{key}_{i}'] = r[key]
            result.update(r2)
    return result


def main():
    if len(sys.argv) != 2:
        max_workers = None
    else:
        max_workers = int(sys.argv[1])

    df_list = stocktools.get_stock_list()

    with concurrent.futures.ProcessPoolExecutor(max_workers=max_workers) as executer:
        results = executer.map(analyze, list(df_list.index))

    df = pd.DataFrame([x for x in results if x is not None])
    df.to_pickle('result.pkl')
    df.to_excel('result.xlsx', index=None)


if __name__ == '__main__':
    main()
```

stocktoolsというのは自前のツールで、株価の時系列データを取得するものです。時系列データは事前にダウンロードしています。

# 測定方法

ワーカー数を1からPCのCore数まで増やして測定していきます。
Core数は下記で求めました。

```python
import os
print(os.cpu_count())
```
MacBook Pro 2013では4、MacBook Pro 2021では10となります。

# 測定結果

単位は秒です。

|Max Workers|MacBook   2013|MacBook 2021|
|:-:|-:|-:|
|1|1333.275|267.186|
|2|683.213|145.021|
|3|634.725|101.219|
|4|608.148|77.986|
|5||63.899|
|6||54.719|
|7||48.195|
|8||43.393|
|9||42.079|
|10||41.673|

![perf.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/1106967/0832f1dc-0612-4750-e5c3-0e67db1c7e87.png)

MacBook Pro 2013では4Core使って608秒かかる計算が、MacBook Pro 2021では1Coreで267秒で2.2倍高速、4Core使うと78秒となり7.7倍高速になっています。10Core全て使うと42秒となり15倍くらい高速化されてます。

# 最後に

測定結果から分かりますが、1Core(並列処理を意識しない作り)だとその差は5倍くらいとなります。これだけでも十分早いのですが、10Coreを生かすために並列処理とすることで15倍くらい高速化できました。24時間以上かかるような計算をすることもあるので、うまく作れば数時間で終わらせることができるかも知れません。

測定していて気づいたことですが、MacBook Pro 2013はファンの音がすごくうるさかったのに対して、2021はほぼ無音でした。キーボードも温かくなりません。

実は2台のPCでTensorFlowでDeepLearningさせたのですが、こちらは性能差が4〜5倍でした。先に書いた通り、これは1Coreでの性能差となってます。バッチサイズを調整すればもう少し早くなるかも知れません。こちらについてはおいおい確かめようと思ってます。