先ほどの クラスのベンチマーク の続編。
オブジェクト合成でより良い(というか速い)手法ないかなと悩みつつ。
実験環境
同じく
- Node.js 14.15.1
- Deno 1.5.4
実験設定
- 10要素で構成されるオブジェクト2組を1つのオブジェクトにまとめる
- 要素のうち5つは名前が重複しており、合成元を合成先に上書きする
- 合成先はプロトタイプではなく、独立したオブジェクトとして新規に生成される
という前提で
- 合成先オブジェクトを生成するファンクションと、合成元オブジェクトを用意
- 実行時間計測
- 合成先オブジェクト生成ファンクションだけの結果
- 合成先オブジェクト生成ファンクション+各種アルゴリズムの結果
- 結果表示
- 各種アルゴリズムの結果部分を抽出表示
という手順。
オブジェクト合成アルゴリズム ここに集う
今回調査するアルゴリズムを御紹介いたしましょう。
- assign
- Object.assign()
- spread
- スプレッド構文
- for
- for inループ
- forEach
- keys forEachコールバック
- map
- keys mapコールバック
- entries
- entries列挙ループ
- lowtech1
- (対照用) オブジェクト要素として個別に代入
- lowtech2
- (対照用) 連想配列要素として個別に代入
多少のオーバーヘッドは仕方ないにしても、できるだけ対照用で挙げた方法に
近い(というか速い)アルゴリズムを採用したいという趣旨。
いざ、実験
今回は記述がコンパクトなので、1ソースでまとめていけます
bench_obj_merge.js
function CreateParent(){return {a:0,b:1,c:2,d:3,e:4,f:5,g:6,h:7,i:8,j:9};}
var sub={f:10,g:11,h:12,i:13,j:14,k:15,l:16,m:17,n:18,o:19};
// 実行内容
var funx={
dmy:()=>0, // 最初の実行項目は不利な結果が出るのでダミー
create:()=>CreateParent(),
assign:()=>Object.assign(CreateParent(),sub),
spread:()=>{return {...CreateParent(),...sub};},
for:()=>{
var t=CreateParent();
for(var k in sub)t[k]=sub[k];
return t;
},
forEach:()=>{
var t=CreateParent();
Object.keys(sub).forEach(k=>t[k]=sub[k]);
return t;
},
map:()=>{
var t=CreateParent();
Object.keys(sub).map(k=>t[k]=sub[k]);
return t;
},
entries:()=>{
var t=CreateParent();
for(var [k,v] of Object.entries(sub))t[k]=v;
return t;
},
lowtech1:()=>{
var t=CreateParent();
t.f=sub.f;
t.g=sub.g;
t.h=sub.h;
t.i=sub.i;
t.j=sub.j;
t.k=sub.k;
t.l=sub.l;
t.m=sub.m;
t.n=sub.n;
t.o=sub.o;
return t;
},
lowtech2:()=>{
var t=CreateParent();
t['f']=sub['f'];
t['g']=sub['g'];
t['h']=sub['h'];
t['i']=sub['i'];
t['j']=sub['j'];
t['k']=sub['k'];
t['l']=sub['l'];
t['m']=sub['m'];
t['n']=sub['n'];
t['o']=sub['o'];
return t;
},
};
// 計測結果を書き込むところ
var rec={};
// 動作テスト
//for(var k in funx)console.log([k,funx[k]()]);
// 繰り返し実行時間計測
var loop=Array(1000000);
for(var k in funx){
var f=funx[k];
var bgn=new Date;
for(var i of loop)f();
var end=new Date;
rec[k]=(end-bgn);
}
// 結果表示
for(var k in rec)console.log(k+' :'+(rec[k]-rec.create));
ここで予想外の事態。当初は 前回 と同じ1千万ループだったのですが、なかなか処理終わらなかったので一旦止めて桁減らしちゃいました。
それでループ回数が1桁減っているので、前回の結果と比べるときは御注意ください。
Node.js | Deno | |
---|---|---|
assign | 255.7 | 265.4 |
spread | 6906.3 | 6503.3 |
for | 289.2 | 484.4 |
forEach | 413.6 | 463.1 |
map | 445.5 | 505.7 |
entries | 417.3 | 428.4 |
lowtech1 | 10.1 | 8 |
lowtech2 | 10.3 | 8 |
まず目を疑ったのが、今(一部で)流行りのスプレッド構文、なんと1桁遅い。
記述が一番シンプルなだけに残念なところ。
で、とりあえず Object.assign() がベストというか幾分マシということで。
もっと素敵な手法ないものかしらん。