map

41
Aiming 大阪スタジオ エンジニア勉強会 2013-03-11 Tomohiro Kashiwada

Upload: kikairoya

Post on 12-Jul-2015

2.673 views

Category:

Documents


4 download

TRANSCRIPT

Page 1: Map

Aiming 大阪スタジオエンジニア勉強会 2013-03-11

Tomohiro Kashiwada

Page 2: Map

std::map を for で回したことがあるstd::map::lower_bound を使わないmap::find より map::begin をよく使う面倒だし std::map でも全探索する

Page 3: Map

速度が重要だと考えているなら、今すぐstd::map の全走査はやめましょう

std::list より、さらに遅い赤黒木で実装されているので仕方ないそもそも辞書なんだからキーを使って引けばよい……

Page 4: Map

std::map の典型的な内部実装平衡二分木の一つほとんどの操作が O(logN)

ただし定数係数が非常に大きい

Page 5: Map

出典: Wikipedia(ja) Red-black tree example.svg

Q. この赤黒木 (10 要素)を全走査するのに必要な dereference の回数は?

Page 6: Map

出典: Wikipedia(ja) Red-black tree example.svg

Page 7: Map

出典: Wikipedia(ja) Red-black tree example.svg

Page 8: Map

出典: Wikipedia(ja) Red-black tree example.svg

Page 9: Map

出典: Wikipedia(ja) Red-black tree example.svg

Page 10: Map

出典: Wikipedia(ja) Red-black tree example.svg

Page 11: Map

出典: Wikipedia(ja) Red-black tree example.svg

Page 12: Map

出典: Wikipedia(ja) Red-black tree example.svg

Page 13: Map

出典: Wikipedia(ja) Red-black tree example.svg

Page 14: Map

出典: Wikipedia(ja) Red-black tree example.svg

Page 15: Map

出典: Wikipedia(ja) Red-black tree example.svg

Page 16: Map

A. 16 回参考:

15 要素 25回31 要素 57回

出典: Wikipedia(ja) Red-black tree example.svg

Page 17: Map

コンテナを用途によって正しく使い分けることは、どんな最適化よりも重要

1byte のコピーを削減するよりも、用途にマッチしたコンテナを使うほうが効果大

具体的にはどういう基準で選定するのか?

Page 18: Map

選ぶのがめんどくさい unordered_map

全走査したい boost::flat_map

並び順が安定しててほしい boost::flat_map

任意の外部入力がキーになる可能性がある boost::flat_map

Page 19: Map

基本的には使わない必要になるのは……

• ハッシュ関数が定義できない、かつ、要素のコピーが非常に遅い場合

• 途中で要素の追加削除が発生する全走査をしたい場合

• std::map を受け取る外部ライブラリを使う場合

要らないですね

Page 20: Map

2重探索をしない• find insert は NG

begin を避ける• 必ずキーアクセスを使う

Page 21: Map

こんなコードはNGconst auto ite = m.find(k);if (ite == m.end()) {return *m.insert(std::make_pair(k,make_new_value(params...)

););

} else {return *ite;

}

Page 22: Map

こんなコードはNGconst auto ite = m.find(k);if (ite == m.end()) {return *m.insert(std::make_pair(k,make_new_value(params...)

););

} else {return *ite;

}

ここで探索して

ここでも探索している

しかも値を2回コピーしている

Page 23: Map

lower_bound を使うconst auto ite = m.lower_bound(k);if (ite == m.end() || ite->first != k) {return *m.emplace_hint(ite,std::piecewise_construct,std::forward_as_tuple(k),std::forward_as_tuple(params...)

);} else {return *ite;

}

Page 24: Map

lower_bound を使うconst auto ite = m.lower_bound(k);if (ite == m.end() || ite->first != k) {return *m.emplace_hint(ite,std::piecewise_construct,std::forward_as_tuple(k),std::forward_as_tuple(params...)

);} else {return *ite;

}

探索は1回

(ヒントが正しければ)

挿入は償却定数時間

direct initialization で無駄なコピーを回避

Page 25: Map

例:こんなデータを map に入れたいとき

struct item_instance: boost::noncopyable {item_uid_t uid;item_category_t category;values...;

};

Page 26: Map

安直にこういう map にすると……

std::map<item_uid_t,std::shared_ptr<item_instance>

> items;

Page 27: Map

「武器だけ列挙したい!」

std::vector<std::shared_ptr<item_instance>> ret;std::remove_copy_if(items.begin(),items.end(),std::back_inserter(ret),[&](const std::shared_ptr<item_instance> &i) {

return i->category != item_category::weapon;}

);return ret;

Page 28: Map

boost::filter_iterator を使う• http://www.boost.org/doc/libs/1_53_0/libs/iterator/d

oc/filter_iterator.html

Page 29: Map

結局同じ

const auto f = [](const std::shared_ptr<item_instance> &p

) {return p->category == item_category::weapon;

};return std::vector<std::shared_ptr<item_instance>>(boost::make_filter_iterator(

f, items.begin(), items.end()),boost::make_filter_iterator(

f, items.end(), items.end()));

Page 30: Map

副キー主キーへの map を作る

std::map<item_uid_t,std::shared_ptr<item_instance>

> items;std::multimap<item_category,item_uid_t

> items_by_category;

Page 31: Map

「master_id からも引きたい!」

std::multimap<item_master_id_t,item_uid_t

> items_by_master;

これを items を操作しているすべての

場所で更新するように変更しないといけない!

Page 32: Map

どうにもなりませんboost::flat_map ならまだマシ根本的な解決には……

• データ設計を変更するか、

• boost::multi_index を使うしかない

Page 33: Map

std::map は要らない子

Page 34: Map

「任意の外部入力がキーになる」とは?• hashdos

各 *map の特性の違い• std::map

• std::unordered_map

• boost::flat_map

Page 35: Map

ハッシュの衝突を意図的に大量に発生させる攻撃手法

キャラクター名をキーにする場合などどの実装を使っているか推測できれば攻撃自体は容易• この Web サービスは PHP だから……

• ゲームサーバなんてどうせ C++ だろうし……

最近の実装ではライブラリ側で対策されているので通用しない

Page 36: Map

おググりくださいhttp://blog.tokumaru.org/2011/12/webdosh

ashdos.html

Page 37: Map

std::map• 赤黒木

std::unordered_map• 普通のハッシュマップ

boost::flat_map• ソート済み配列

Page 38: Map

みんな知ってるイテレータの安定性がウリ

• ○追加削除が発生してもイテレータは失効しない

• ○順序付

• ×メモリ大食い (1要素につきポインタ3個)

• ×操作(走査)が遅い (最良で O(logN) )

Page 39: Map

a.k.a. stdext::hash_map

辞書が必要ならとりあえずこれで• ○ほとんどの操作が ave. O(1)

• ×最悪ケースで O(N)

• ×rehash が発生するとスパイク状の性能劣化

• △最適なハッシュ関数を定義するのが難しい

• ×非順序

Page 40: Map

推しmap

中身はただの vector なので……• ○走査・参照が速い

• ○省メモリ

• ×追加・削除が遅い (但しおよそ N>100 の場合)

• ×イテレータの安定性が無い

要素数が少ない場合は何も考えずにboost::flat_map を使ってよい

Page 41: Map

std::map は要らない子