そんなリザルトキャッシュで大丈夫か? #jjug

48
そんなリザルトキャッシュ で大丈夫か? @making 俊明 2014-09-17

Upload: makingx

Post on 16-Nov-2014

4.579 views

Category:

Technology


4 download

DESCRIPTION

JJUGナイトセミナー2014年9月のセッション 「そんなリザルトキャッシュで大丈夫か?」 のスライドです。

TRANSCRIPT

Page 1: そんなリザルトキャッシュで大丈夫か? #jjug

そんなリザルトキャッシュ で大丈夫か?@making 槙 俊明

2014-09-17

Page 2: そんなリザルトキャッシュで大丈夫か? #jjug

ここでいうリザルトキャッシュとは

•重い処理の結果を格納するメモリ(キャッシュ)

•リザルトキャッシュを再利用することで性能向上が期待できる

Page 3: そんなリザルトキャッシュで大丈夫か? #jjug

あなたのリザルトキャッシュ

•スレッドセーフですか?

•スケーラブルですか?

Page 4: そんなリザルトキャッシュで大丈夫か? #jjug
Page 5: そんなリザルトキャッシュで大丈夫か? #jjug

5-6 「効率的でスケーラブルなリザルトキャッシュを

構築する」 の内容をご紹介します

Page 6: そんなリザルトキャッシュで大丈夫か? #jjug

たまにみる実装

Page 7: そんなリザルトキャッシュで大丈夫か? #jjug

たまにみる実装static Map<BigInteger, List<BigInteger>> cache = new HashMap<>; void doGet(…) { BigInteger n = …; List<BigInteger> result = cache.get(n); if (result == null) { result = PrimeFactor.divide(n); // 素因数分解 cache.put(n, result); } // … }

Page 8: そんなリザルトキャッシュで大丈夫か? #jjug

たまにみる実装static Map<BigInteger, List<BigInteger>> cache = new HashMap<>; void doGet(…) { BigInteger n = …; List<BigInteger> result = cache.get(n); if (result == null) { result = PrimeFactor.divide(n); // 素因数分解 cache.put(n, result); } // … }

Page 9: そんなリザルトキャッシュで大丈夫か? #jjug

たまにみる実装static Map<BigInteger, List<BigInteger>> cache = new HashMap<>; void doGet(…) { BigInteger n = …; List<BigInteger> result = cache.get(n); if (result == null) { result = PrimeFactor.divide(n); // 素因数分解 cache.put(n, result); } // … } キャッシュになかったら 計算してキャッシュに追加

Page 10: そんなリザルトキャッシュで大丈夫か? #jjug

😡

Page 11: そんなリザルトキャッシュで大丈夫か? #jjug

static Map<BigInteger, List<BigInteger>> cache = new HashMap<>; void doGet(…) { BigInteger n = …; List<BigInteger> result = cache.get(n); if (result == null) { result = PrimeFactor.divide(n); // 素因数分解 cache.put(n, result); } // … } スレッドアンセーフ

Page 12: そんなリザルトキャッシュで大丈夫か? #jjug

よく見る実装static Map<BigInteger, List<BigInteger>> cache = new ConcurrentHashMap<>; void doGet(…) { BigInteger n = …; List<BigInteger> result = cache.get(n); if (result == null) { result = PrimeFactor.divide(n); // 素因数分解(重い処理) cache.put(n, result); } // … }

Page 13: そんなリザルトキャッシュで大丈夫か? #jjug

よく見る実装static Map<BigInteger, List<BigInteger>> cache = new ConcurrentHashMap<>; void doGet(…) { BigInteger n = …; List<BigInteger> result = cache.get(n); if (result == null) { result = PrimeFactor.divide(n); // 素因数分解(重い処理) cache.put(n, result); } // … }

スレッドセーフなMapに変更

Page 14: そんなリザルトキャッシュで大丈夫か? #jjug

Demo

Page 15: そんなリザルトキャッシュで大丈夫か? #jjug

😲

Page 16: そんなリザルトキャッシュで大丈夫か? #jjug

get calc put

check

get calc put

check

Page 17: そんなリザルトキャッシュで大丈夫か? #jjug

get calc put

check

get calc put

check

Page 18: そんなリザルトキャッシュで大丈夫か? #jjug

get calc put

check

get calc put

check

まだputされていない

Page 19: そんなリザルトキャッシュで大丈夫か? #jjug

static Map<BigInteger, List<BigInteger>> cache = new ConcurrentHashMap<>; void doGet(…) { BigInteger n = …; List<BigInteger> result = cache.get(n); if (result == null) { result = PrimeFactor.divide(n); // 素因数分解(重い処理) cache.put(n, result); } // … } Atomicじゃない

Page 20: そんなリザルトキャッシュで大丈夫か? #jjug

FutureTaskを使って遅延評価

Page 21: そんなリザルトキャッシュで大丈夫か? #jjug

FutureTaskを使って遅延評価static Map<BigInteger, FutureTask<List<BigInteger>>> cache = new ConcurrentHashMap<>; void doGet(…) { BigInteger n = …; FutureTask<List<BigInteger>> result = cache.get(n); if (result == null) { result = new FutureTask<>(() -> PrimeFactor.divide(n))); cache.put(n, result); result.run(); } // (略) result.get()で結果取得できるまでブロックする }

Page 22: そんなリザルトキャッシュで大丈夫か? #jjug

FutureTaskを使って遅延評価static Map<BigInteger, FutureTask<List<BigInteger>>> cache = new ConcurrentHashMap<>; void doGet(…) { BigInteger n = …; FutureTask<List<BigInteger>> result = cache.get(n); if (result == null) { result = new FutureTask<>(() -> PrimeFactor.divide(n))); cache.put(n, result); result.run(); } // (略) result.get()で結果取得できるまでブロックする }

Callableで処理を記述

Page 23: そんなリザルトキャッシュで大丈夫か? #jjug

FutureTaskを使って遅延評価static Map<BigInteger, FutureTask<List<BigInteger>>> cache = new ConcurrentHashMap<>; void doGet(…) { BigInteger n = …; FutureTask<List<BigInteger>> result = cache.get(n); if (result == null) { result = new FutureTask<>(() -> PrimeFactor.divide(n))); cache.put(n, result); result.run(); } // (略) result.get()で結果取得できるまでブロックする }

Callableで処理を記述

キャッシュに入れてから処理実行

Page 24: そんなリザルトキャッシュで大丈夫か? #jjug

get calcput

check

get put

check

Page 25: そんなリザルトキャッシュで大丈夫か? #jjug

Demo

Page 26: そんなリザルトキャッシュで大丈夫か? #jjug

😨

Page 27: そんなリザルトキャッシュで大丈夫か? #jjug

get calcput

check

get put

check

calc

Page 28: そんなリザルトキャッシュで大丈夫か? #jjug

get calcput

check

get put

check

calc

Page 29: そんなリザルトキャッシュで大丈夫か? #jjug

get calcput

check

get put

check

新しいFutureTaskで上書きされた

calc

Page 30: そんなリザルトキャッシュで大丈夫か? #jjug

get calcput

check

get put

check

新しいFutureTaskで上書きされた

calc結局2回計算

Page 31: そんなリザルトキャッシュで大丈夫か? #jjug

FutureTaskを使って遅延評価static Map<BigInteger, FutureTask<List<BigInteger>>> cache = new ConcurrentHashMap<>; void doGet(…) { BigInteger n = …; FutureTask<List<BigInteger>> result = cache.get(n); if (result == null) { result = new FutureTask<>(() -> PrimeFactor.divide(n))); cache.put(n, result); result.run(); } // (略) result.get()で結果取得できるまでブロックする } Atomicじゃない

Page 32: そんなリザルトキャッシュで大丈夫か? #jjug

ConcurrentMap#putIfAbsent

Page 33: そんなリザルトキャッシュで大丈夫か? #jjug

ConcurrentMap#putIfAbsentstatic ConcurrentMap<BigInteger, FutureTask<List<BigInteger>>> cache = new ConcurrentHashMap<>; void doGet(…) { BigInteger n = …; FutureTask<List<BigInteger>> result = cache.get(n); if (result == null) { FutureTask<List<BigInteger>> ft = new FutureTask<>(() -> PrimeFactor.divide(n))); result = cache.putIfAbsent(n, result); if (result == null) {ft.run(); result = ft;} } // (略) result.get()で結果取得できるまでブロックする

Page 34: そんなリザルトキャッシュで大丈夫か? #jjug

ConcurrentMap#putIfAbsentstatic ConcurrentMap<BigInteger, FutureTask<List<BigInteger>>> cache = new ConcurrentHashMap<>; void doGet(…) { BigInteger n = …; FutureTask<List<BigInteger>> result = cache.get(n); if (result == null) { FutureTask<List<BigInteger>> ft = new FutureTask<>(() -> PrimeFactor.divide(n))); result = cache.putIfAbsent(n, result); if (result == null) {ft.run(); result = ft;} } // (略) result.get()で結果取得できるまでブロックする

キーが存在しない場合はnullを、 存在する場合はそれを返す

Page 35: そんなリザルトキャッシュで大丈夫か? #jjug

get calcpIA

check

get pIA

check

pIA … putIfAbsent

Page 36: そんなリザルトキャッシュで大丈夫か? #jjug

get calcpIA

check

get pIA

check

pIA … putIfAbsent

put済みなので上書きしない

Page 37: そんなリザルトキャッシュで大丈夫か? #jjug

Demo

Page 38: そんなリザルトキャッシュで大丈夫か? #jjug

😌

Page 39: そんなリザルトキャッシュで大丈夫か? #jjug

でも面倒くさいね!

😒

Page 40: そんなリザルトキャッシュで大丈夫か? #jjug

Java SE 8から

Page 41: そんなリザルトキャッシュで大丈夫か? #jjug

Java SE 8から

static ConcurrentHashMap<BigInteger, List<BigInteger>> cache = new ConcurrentHashMap<>; void doGet(…) { BigInteger n = …; List<BigInteger> result = cache.computeIfAbsent(n, (x) -> PrimeFactor.divide(x)); // … }

Page 42: そんなリザルトキャッシュで大丈夫か? #jjug

Java SE 8から

static ConcurrentHashMap<BigInteger, List<BigInteger>> cache = new ConcurrentHashMap<>; void doGet(…) { BigInteger n = …; List<BigInteger> result = cache.computeIfAbsent(n, (x) -> PrimeFactor.divide(x)); // … } キーが存在しない場合はラムダ式

の計算結果を返す

Page 43: そんなリザルトキャッシュで大丈夫か? #jjug

Java SE 8から

static ConcurrentHashMap<BigInteger, List<BigInteger>> cache = new ConcurrentHashMap<>; void doGet(…) { BigInteger n = …; List<BigInteger> result = cache.computeIfAbsent(n, PrimeFactor::divide); // … }

メソッド参照でOK

Page 44: そんなリザルトキャッシュで大丈夫か? #jjug

Demo

Page 45: そんなリザルトキャッシュで大丈夫か? #jjug

Cool!

😎

Page 46: そんなリザルトキャッシュで大丈夫か? #jjug

まとめ•ConcurrentHashMap#putIfAbsent + FutureTaskで効率的なリザルトキャッシュを実装できる 😌

•JDK8からはConcurrentHashMap#computeIfAbsentでおk 😎

Page 47: そんなリザルトキャッシュで大丈夫か? #jjug

Java SE 8を使おう

Page 48: そんなリザルトキャッシュで大丈夫か? #jjug

ご清聴 ありがとうございました