はじめに
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の値を求めます。
y = e^{ax}
データを正規化するために、x=0のとき、y=1となるうようにデータを調整しています。1銘柄に対して、2011年から2016年、2012年から2017年、・・・、2017年から2021年のようにして7期間のaを算出して、結果をExcelファイルに出力してます。
起動時の引数としてワーカー数を指定します。concurrent.futuresにこのワーカー数を指定して並列処理をさせます。
プログラムは下記のようになります。
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数は下記で求めました。
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 |
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での性能差となってます。バッチサイズを調整すればもう少し早くなるかも知れません。こちらについてはおいおい確かめようと思ってます。