mcmdのメソッド比較¶
nysol.mcmdが提供する70以上の処理メソッドの中から、主な処理メソッドについての処理速度の実験を行う。
準備¶
本節で扱っているPythonスクリプトの全ては github よりダウンロードできる ( リスト 16 )。 ダウンロードしたディレクトリ下 bench/methods に 以下の2つのスクリプトがある。
mkdata.py : 集計キーや数値項目を備えたランダムなデータを生成する
bench.py : ベンチマーク用スクリプト( リスト 21 )
1$ git clone https://github.com/nysol/bench.git
2$ cd bench/methods
利用データ¶
利用したデータは、「 mcmdベンチマーク用乱数データセット 」節で解説されている人工的に生成したデータであり、 上述のgitからダウンロードしたスクリプト mkdata.py を実行すれば ./DATA ディレクトリが作成され、 そこに 表 10 に示される異なる3つのサイズのデータが生成される。 処理には10分ほどかかる。 データの詳細については、「 mcmdベンチマーク用乱数データセット 」節を参照されたい。
名称 |
ファイル名 |
行数(ヘッダ除く) |
サイズ |
---|---|---|---|
small |
10000.csv |
10000 |
665 KB |
middle |
1000000.csv |
1000000 |
68.5 MB |
large |
100000000.csv |
100000000 |
7.05 GB |
検証内容¶
mcmdの処理メソッドの速度を評価するために、最も処理速度が速いであろう mcut 処理メソッドをベンチマークに、
他のメソッドの相対的実行時間を計測した。
個々の処理メソッドの関数を bench.py から抜粋したものを リスト 17 に示している。
それぞれの関数では、入力ファイル名 iFile
と 実行回数 loop
をパラメータとしてとる。
入力ファイルは、 表 10 に示される3種のCSVデータで、
実行回数は5回固定である。
時間の計測は、実行に要した実時間を計測し、その平均と標準偏差を計算している。
なお、出力は基本的にカレントディレクトリにファイルとして書き出している。
msum_key3
関数は、key3項目を集計キーにして全ての数値項目を合計する処理である。
このように関数の名前にキー項目など主だった特徴となるキーワードを用いている。
msum_key3_presort
関数では、キー項目を事前に並べ替えた後で msum
を実行している。
このことによって、裏で自動的に実行される並べ替えを省いて純粋なmsumの実行時間を計測できることになる。
1def mcut(iFile,loop):
2 sec=[]
3 for i in range(loop):
4 st=time.time()
5 nm.mcut(f="id,key1,key2,key3,int1,int2,float1,float2,date,time",i=iFile,o="oFile").run()
6 sec.append(time.time()-st)
7 return sec
8
9def msum_key3(iFile,loop):
10 sec=[]
11 for i in range(loop):
12 st=time.time()
13 nm.msum(k="key3",f="int1,int2,float1,float2",i=iFile,o="oFile").run()
14 sec.append(time.time()-st)
15 return sec
16
17def msum_key3_presort(iFile,loop):
18 nm.msortf(f="key3",i=iFile,o="sorted").run()
19 sec=[]
20 for i in range(loop):
21 st=time.time()
22 nm.msum(k="key3",f="int1,int2,float1,float2",i="sorted",o="oFile").run()
23 sec.append(time.time()-st)
24 return sec
実験結果¶
表 11 に、mcut処理メソッドをベンチマークにした相対時間を示す。 msum_key3 のlargeで6.2となっているが、mcutのlargeを処理した時間に比べて6.2倍の時間を要したことを意味している。 個々の処理内容の詳細については bench.py を直接参照されたい。 また、表 12 に実時間の平均と標準偏差を示す。
method |
small(1万行:665KB) |
middle(100万行:68.5MB) |
large(1億行:7.05GB) |
---|---|---|---|
mcut |
1 |
1 |
1 |
msum_key3 |
21.9 |
4.9 |
6.2 |
msum_key3_presort |
3.7 |
1.5 |
1.1 |
mhashsum_key3 |
7.4 |
2.6 |
1.8 |
msortf_key3 |
4.8 |
2.2 |
4.6 |
msortf_float2 |
20.8 |
6.1 |
12.9 |
method |
small(1万行:665KB) |
middle(100万行:68.5MB) |
large(1億行:7.05GB) |
---|---|---|---|
mcut |
0.004002(0.000703) |
0.307001(0.001781) |
30.737572(0.125166) |
msum_key3 |
0.087729(0.113214) |
1.494672(0.012172) |
190.174376(1.948978) |
msum_key3_presort |
0.014930(0.000098) |
0.463388(0.001697) |
35.182463(0.048672) |
mhashsum_key3 |
0.029656(0.002965) |
0.805394(0.003971) |
56.813676(0.107048) |
msortf_key3 |
0.019332(0.004538) |
0.666170(0.004806) |
140.808167(0.528175) |
msortf_float2 |
0.083201(0.131085) |
1.887404(0.024765) |
397.061643(0.161616) |
ベンチマークテストを実施した計算環境は以下の通りである。
PC: MacPro(2013)
CPU: 2.7GHz 12-Core Intel Xeon E5
memory: 64GB
hdd: USB3 HDD
参考: ベンチマークスクリプト¶
参考までにベンチマークテストに利用したスクリプトを、 リスト 18 に示す( 2021年08月18日 時点)。
1#!/usr/bin/env python
2# -*- coding: utf-8 -*-
3
4import os
5import time
6import datetime
7import nysol.mcmd as nm
8
9def mcut(iFile,loop):
10 sec=[]
11 for i in range(loop):
12 st=time.time()
13 nm.mcut(f="id,key1,key2,key3,int1,int2,float1,float2,date,time",i=iFile,o="oFile").run()
14 sec.append(time.time()-st)
15 return sec
16
17def msortf_key1(iFile,loop):
18 sec=[]
19 for i in range(loop):
20 st=time.time()
21 nm.msortf(f="key1",i=iFile,o="oFile").run()
22 sec.append(time.time()-st)
23 return sec
24
25def msortf_key2(iFile,loop):
26 sec=[]
27 for i in range(loop):
28 st=time.time()
29 nm.msortf(f="key1",i=iFile,o="oFile").run()
30 sec.append(time.time()-st)
31 return sec
32
33def msortf_key3(iFile,loop):
34 sec=[]
35 for i in range(loop):
36 st=time.time()
37 nm.msortf(f="key1",i=iFile,o="oFile").run()
38 sec.append(time.time()-st)
39 return sec
40
41def msortf_float2(iFile,loop):
42 sec=[]
43 for i in range(loop):
44 st=time.time()
45 nm.msortf(f="float2%n",i=iFile,o="oFile").run()
46 sec.append(time.time()-st)
47 return sec
48
49def msum_key3(iFile,loop):
50 sec=[]
51 for i in range(loop):
52 st=time.time()
53 nm.msum(k="key3",f="int1,int2,float1,float2",i=iFile,o="oFile").run()
54 sec.append(time.time()-st)
55 return sec
56
57def msum_key3_presort(iFile,loop):
58 nm.msortf(f="key3",i=iFile,o="sorted").run()
59 sec=[]
60 for i in range(loop):
61 st=time.time()
62 nm.msum(k="key3",f="int1,int2,float1,float2",i="sorted",o="oFile").run()
63 sec.append(time.time()-st)
64 return sec
65
66
67def mhashsum_key3(iFile,loop):
68 sec=[]
69 for i in range(loop):
70 st=time.time()
71 nm.mhashsum(k="key3",f="int1,int2,float1,float2",i=iFile,o="oFile").run()
72 sec.append(time.time()-st)
73 return sec
74
75
76##########################################################################
77# functions for benchmark test
78##########################################################################
79# calculate actual execution time for each method
80# iFile
81# method,dataSize,mean,sd
82# mcut,10000,0.004002,0.000703
83# mcut,1000000,0.307001,0.001781
84# oFile
85# method,small,middle,large
86# mcut,0.004002(0.000703),0.307001(0.001781),30.737572(0.125166)
87# msum_key3,0.087729(0.113214),1.494672(0.012172),190.174376(1.948978)
88# msum_key3_presort,0.014930(0.000098),0.463388(0.001697),35.182463(0.048672)
89def calTime(iFile,oFile):
90 f=None
91 f <<= nm.mnumber(q=True, a="id", i=iFile)
92 f <<= nm.mcal(c='$s{mean}+"("+$s{sd}+")"', a="time")
93 f <<= nm.m2cross(k="method",s="dataSize",f="time")
94 f <<= nm.msortf(f="id%n")
95 f <<= nm.mcut(f="method,10000:small,1000000:middle,100000000:large")
96 f <<= nm.mfldname(q=True,o=oFile)
97 f.run()
98
99# calculate relative execution time to "mcut" method for each method
100# iFile
101# method,dataSize,mean,sd
102# mcut,10000,0.004002,0.000703
103# mcut,1000000,0.307001,0.001781
104# oFile
105# mcut,1,1,1
106# msum_key3,21.9,4.9,6.2
107# msum_key3_presort,3.7,1.5,1.1
108# mhashsum_key3,7.4,2.6,1.8
109def calRelative(iFile,oFile):
110 mcut=None
111 mcut <<= nm.mselstr(f="method",v="mcut", i="methods.csv")
112
113 f=None
114 f <<= nm.mnumber(q=True, a="id", i=iFile)
115 f <<= nm.mjoin(k="dataSize",m=mcut,f="mean:base")
116 f <<= nm.mcal(c='round(${mean}/${base},0.1)', a="score")
117 f <<= nm.m2cross(k="method",s="dataSize",f="score")
118 f <<= nm.msortf(f="id%n")
119 f <<= nm.mcut(f="method,10000:small,1000000:middle,100000000:large")
120 f <<= nm.mfldname(q=True,o=oFile)
121 f.run()
122
123# calculate mean and SD of multiple executions
124def cal(sec):
125 mean=0
126 for s in sec:
127 mean+=s
128 mean/=len(sec)
129 sd=0
130 for s in sec:
131 sd+=(s-mean)**2
132 sd/=(len(sec)-1)
133 sd=sd**(1/2)
134 return mean,sd
135
136################
137# entry point
138
139iPath="./DATA"
140loop=5
141small =10000
142middle=1000000
143large =100000000
144funcs=[]
145funcs.append("mcut")
146funcs.append("msum_key3")
147funcs.append("msum_key3_presort")
148funcs.append("mhashsum_key3")
149funcs.append("msortf_key3")
150funcs.append("msortf_float2")
151
152with open("methods.csv","w") as fpw:
153 fpw.write("method,dataSize,mean,sd\n")
154 for func in funcs:
155 for size in [small,middle,large]:
156 iFile="%s/%d.csv"%(iPath,size)
157 name="%s_%s"%(func,size)
158 print("START:",name)
159 sec=eval(func+'("%s",%d)'%(iFile,loop))
160 print("tm",sec)
161 mean,sd=cal(sec)
162 fpw.write("%s,%s,%f,%f\n"%(func,size,mean,sd))
163
164today = datetime.date.today().strftime('%Y%m%d')
165calTime("methods.csv","time_%s.csv"%today)
166calRelative("methods.csv","score_%s.csv"%today)
167
168os.system("output files: methods.csv, time.csv, score.csv")