質問編集履歴

6

誤字など修正

2017/10/12 00:33

投稿

mosa
mosa

スコア218

test CHANGED
File without changes
test CHANGED
@@ -136,7 +136,7 @@
136
136
 
137
137
  * キャッシュなし
138
138
 
139
- * LRUしないキャッシュ
139
+ * LRUで削除しないキャッシュ
140
140
 
141
141
  * Collections.syncronizedMap() で排他するLRUキャッシュ
142
142
 
@@ -172,11 +172,11 @@
172
172
 
173
173
  class NonCache<K, V> implements Calculator<K, V>{
174
174
 
175
- private Function<K, V> initializer;
175
+ private Function<K, V> initializer;
176
-
176
+
177
- NonCache(Function<K, V> initializer){ this.initializer = initializer; }
177
+ NonCache(Function<K, V> initializer){ this.initializer = initializer; }
178
-
178
+
179
- @Override public V get(K key){ return initializer.apply(key); }
179
+ @Override public V get(K key){ return initializer.apply(key); }
180
180
 
181
181
  }
182
182
 
@@ -186,106 +186,106 @@
186
186
 
187
187
  class NonLruCache<K, V> implements Calculator<K, V>{
188
188
 
189
- private int capacity;
189
+ private int capacity;
190
-
190
+
191
- private Function<K, V> initializer;
191
+ private Function<K, V> initializer;
192
-
192
+
193
- private Map<K, V> map = new LinkedHashMap<>(capacity, 1.0f, true);
193
+ private Map<K, V> map = new LinkedHashMap<>(capacity, 1.0f, true);
194
-
194
+
195
- NonLruCache(int capacity, Function<K, V> initializer){
195
+ NonLruCache(int capacity, Function<K, V> initializer){
196
-
196
+
197
- this.capacity = capacity;
197
+ this.capacity = capacity;
198
-
198
+
199
- this.initializer = initializer;
199
+ this.initializer = initializer;
200
+
201
+ }
202
+
203
+ @Override public V get(K key){ return map.computeIfAbsent(key, initializer); }
204
+
205
+ }
206
+
207
+
208
+
209
+ // Collections.syncronizedMap() で排他するLRUキャッシュ
210
+
211
+ class LruCache1<K, V> implements Calculator<K, V>{
212
+
213
+ private int capacity;
214
+
215
+ private Function<K, V> initializer;
216
+
217
+ private Map<K, V> map = Collections.synchronizedMap(new LinkedHashMap<K, V>(capacity, 1.0f, true){
218
+
219
+ @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
220
+
221
+ });
222
+
223
+ LruCache1(int capacity, Function<K, V> initializer){
224
+
225
+ this.capacity = capacity;
226
+
227
+ this.initializer = initializer;
228
+
229
+ }
230
+
231
+ @Override public V get(K key){ return map.computeIfAbsent(key, initializer); }
232
+
233
+ }
234
+
235
+
236
+
237
+ // StampedLock.writeLock() で排他するLRUキャッシュ
238
+
239
+ class LruCache2<K, V> implements Calculator<K, V>{
240
+
241
+ private int capacity;
242
+
243
+ private StampedLock lock = new StampedLock();
244
+
245
+ private Function<K, V> initializer;
246
+
247
+ private Map<K, V> map = new LinkedHashMap<K, V>(capacity, 1.0f, true){
248
+
249
+ @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
250
+
251
+ };
252
+
253
+ LruCache2(int capacity, Function<K, V> initializer){
254
+
255
+ this.capacity = capacity;
256
+
257
+ this.initializer = initializer;
258
+
259
+ }
260
+
261
+ @Override public V get(K key){
262
+
263
+ long stamp = lock.writeLock();
264
+
265
+ try{ return map.computeIfAbsent(key, initializer); } finally{ lock.unlockWrite(stamp); }
266
+
267
+ }
268
+
269
+
270
+
271
+ }
272
+
273
+
274
+
275
+ // daisuke7さんに教えていただいたbarakbさんのライブラリ https://github.com/barakb/Cache
276
+
277
+ class LruCache3<K, V> implements Calculator<K, V>{
278
+
279
+ private Cache<K, V> cache;
280
+
281
+ LruCache3(int capacity, Function<K, V> initializer){ cache = new Cache<>(initializer::apply, capacity); }
282
+
283
+ @Override public V get(K key){
284
+
285
+ try{ return cache.get(key); } catch(Throwable throwable){ throw new RuntimeException(throwable); }
200
286
 
201
287
  }
202
288
 
203
- @Override public V get(K key){ return map.computeIfAbsent(key, initializer); }
204
-
205
- }
206
-
207
-
208
-
209
- // Collections.syncronizedMap() で排他するLRUキャッシュ
210
-
211
- class LruCache1<K, V> implements Calculator<K, V>{
212
-
213
- private int capacity;
214
-
215
- private Function<K, V> initializer;
216
-
217
- private Map<K, V> map = Collections.synchronizedMap(new LinkedHashMap<K, V>(capacity, 1.0f, true){
218
-
219
- @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
220
-
221
- });
222
-
223
- LruCache1(int capacity, Function<K, V> initializer){
224
-
225
- this.capacity = capacity;
226
-
227
- this.initializer = initializer;
228
-
229
- }
230
-
231
- @Override public V get(K key){ return map.computeIfAbsent(key, initializer); }
232
-
233
- }
234
-
235
-
236
-
237
- // StampedLock.writeLock() で排他するLRUキャッシュ
238
-
239
- class LruCache2<K, V> implements Calculator<K, V>{
240
-
241
- private int capacity;
242
-
243
- private StampedLock lock = new StampedLock();
244
-
245
- private Function<K, V> initializer;
246
-
247
- private Map<K, V> map = new LinkedHashMap<K, V>(capacity, 1.0f, true){
248
-
249
- @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
250
-
251
- };
252
-
253
- LruCache2(int capacity, Function<K, V> initializer){
254
-
255
- this.capacity = capacity;
256
-
257
- this.initializer = initializer;
258
-
259
- }
260
-
261
- @Override public V get(K key){
262
-
263
- long stamp = lock.writeLock();
264
-
265
- try{ return map.computeIfAbsent(key, initializer); } finally{ lock.unlockWrite(stamp); }
266
-
267
- }
268
-
269
-
270
-
271
- }
272
-
273
-
274
-
275
- // daisuke7さんに教えていただいたbarakbさんのライブラリ https://github.com/barakb/Cache
276
-
277
- class LruCache3<K, V> implements Calculator<K, V>{
278
-
279
- private Cache<K, V> cache;
280
-
281
- LruCache3(int capacity, Function<K, V> initializer){ cache = new Cache<>(initializer::apply, capacity); }
282
-
283
- @Override public V get(K key){
284
-
285
- try{ return cache.get(key); } catch(Throwable throwable){ throw new RuntimeException(throwable); }
286
-
287
- }
288
-
289
289
 
290
290
 
291
291
  }
@@ -318,87 +318,87 @@
318
318
 
319
319
 
320
320
 
321
- public static final int COST = 1000; // 計算コスト
322
-
323
- public static final int SIZE = 1000; // 要素数
324
-
325
- public static final double DUPLICATION_RATE = 0.0; // 重複率
326
-
327
- public static final double CAPACITY_RATE = 1.0; // 容量率
328
-
329
- public static final int CAPACITY = (int) (Math.round(SIZE * (1.0 - DUPLICATION_RATE) * CAPACITY_RATE)); // 容量
330
-
331
- public static final List<Integer> LIST = IntStream.rangeClosed(0, SIZE - 1).boxed()
332
-
333
- .map(it -> (int) ((double) it * (1.0 - DUPLICATION_RATE))).collect(Collectors.toList());
334
-
335
- public static final int UNIQUE = (int) LIST.stream().distinct().count(); // ユニーク数
336
-
337
-
338
-
339
- public static void main(String... args){
340
-
341
- Function<Integer, Integer> calc = it -> it + IntStream.rangeClosed(1, COST * 10000).sum();
342
-
343
- Collections.shuffle(LIST); // リストをシャッフル
344
-
345
- System.out.println("計算コスト:" + COST + " 要素数:" + SIZE + " ユニーク数:" + UNIQUE + " 重複率:" + DUPLICATION_RATE + " 容量:" + CAPACITY + " 容量率:" + CAPACITY_RATE);
346
-
347
- seri("キャッシュなし", new NonCache<>(calc));
348
-
349
- para("キャッシュなし", new NonCache<>(calc));
350
-
351
- seri("LRUなし", new NonLruCache<>(CAPACITY, calc));
352
-
353
- para("LRUなし", new NonLruCache<>(CAPACITY, calc));
354
-
355
- seri("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
356
-
357
- para("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
358
-
359
- seri("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
360
-
361
- para("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
362
-
363
- seri("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
364
-
365
- para("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
366
-
367
- }
368
-
369
-
370
-
371
- private static void seri(String name, Calculator<Integer, Integer> calculator){
372
-
373
- long start = System.currentTimeMillis();
374
-
375
- LIST.stream().mapToInt(calculator::get).sum();
376
-
377
- System.out.println(name + " シリアル " + (System.currentTimeMillis() - start) + "ms. ");
378
-
379
- }
380
-
381
-
382
-
383
- private static void para(String name, Calculator<Integer, Integer> calculator){
384
-
385
- long start = System.currentTimeMillis();
386
-
387
- LIST.parallelStream().mapToInt(calculator::get).sum();
388
-
389
- System.out.println(name + " パラレル " + (System.currentTimeMillis() - start) + "ms. ");
390
-
391
- }
392
-
393
-
394
-
395
- }
396
-
397
- ```
398
-
399
-
400
-
401
- ### 速度計測結果
321
+ public static final int COST = 1000; // 計算コスト
322
+
323
+ public static final int SIZE = 1000; // 要素数
324
+
325
+ public static final double DUPLICATION_RATE = 0.0; // 重複率
326
+
327
+ public static final double CAPACITY_RATE = 1.0; // 容量率
328
+
329
+ public static final int CAPACITY = (int) (Math.round(SIZE * (1.0 - DUPLICATION_RATE) * CAPACITY_RATE)); // 容量
330
+
331
+ public static final List<Integer> LIST = IntStream.rangeClosed(0, SIZE - 1).boxed()
332
+
333
+ .map(it -> (int) ((double) it * (1.0 - DUPLICATION_RATE))).collect(Collectors.toList());
334
+
335
+ public static final int UNIQUE = (int) LIST.stream().distinct().count(); // ユニーク数
336
+
337
+
338
+
339
+ public static void main(String... args){
340
+
341
+ Function<Integer, Integer> calc = it -> it + IntStream.rangeClosed(1, COST * 10000).sum();
342
+
343
+ Collections.shuffle(LIST); // リストをシャッフル
344
+
345
+ System.out.println("計算コスト:" + COST + " 要素数:" + SIZE + " ユニーク数:" + UNIQUE + " 重複率:" + DUPLICATION_RATE + " 容量:" + CAPACITY + " 容量率:" + CAPACITY_RATE);
346
+
347
+ seri("キャッシュなし", new NonCache<>(calc));
348
+
349
+ para("キャッシュなし", new NonCache<>(calc));
350
+
351
+ seri("LRUなし", new NonLruCache<>(CAPACITY, calc));
352
+
353
+ para("LRUなし", new NonLruCache<>(CAPACITY, calc));
354
+
355
+ seri("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
356
+
357
+ para("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
358
+
359
+ seri("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
360
+
361
+ para("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
362
+
363
+ seri("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
364
+
365
+ para("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
366
+
367
+ }
368
+
369
+
370
+
371
+ private static void seri(String name, Calculator<Integer, Integer> calculator){
372
+
373
+ long start = System.currentTimeMillis();
374
+
375
+ LIST.stream().mapToInt(calculator::get).sum();
376
+
377
+ System.out.println(name + " シリアル " + (System.currentTimeMillis() - start) + "ms. ");
378
+
379
+ }
380
+
381
+
382
+
383
+ private static void para(String name, Calculator<Integer, Integer> calculator){
384
+
385
+ long start = System.currentTimeMillis();
386
+
387
+ LIST.parallelStream().mapToInt(calculator::get).sum();
388
+
389
+ System.out.println(name + " パラレル " + (System.currentTimeMillis() - start) + "ms. ");
390
+
391
+ }
392
+
393
+
394
+
395
+ }
396
+
397
+ ```
398
+
399
+
400
+
401
+ ### 速度計測結果
402
402
 
403
403
 
404
404
 
@@ -406,7 +406,7 @@
406
406
 
407
407
  →「キャッシュなしのパラレル」が当然最速。「LRUなしのパラレル」も排他がないため高速。
408
408
 
409
- →**「barakbさんのライブラリパラレル」も高速。すごい。**
409
+ →**「barakbさんのライブラリパラレル」も高速。すごい。**
410
410
 
411
411
 
412
412
 
@@ -440,7 +440,7 @@
440
440
 
441
441
  ### 2.2割重複。キャッシュあふれなし。
442
442
 
443
- →当然、「キャッシュなし」以外すべてくなる。
443
+ →当然、「キャッシュなし」以外すべてくなる。
444
444
 
445
445
 
446
446
 
@@ -474,9 +474,9 @@
474
474
 
475
475
  #### 3.8割重複。キャッシュあふれなし。
476
476
 
477
- →すごくくなる。キャッシュの効果が大きい。
477
+ →すごくくなる。キャッシュの効果が大きい。
478
-
478
+
479
- →「barakbさんのライブラリもパラレル」があいかわらずすごい。
479
+ **「barakbさんのライブラリもパラレル」があいかわらずすごい。**
480
480
 
481
481
 
482
482
 
@@ -510,9 +510,9 @@
510
510
 
511
511
  #### 4.2割重複。キャッシュあふれあり。
512
512
 
513
- →LRUがあると当然、やや遅くなる。
513
+ →LRUでの削除があると当然、やや遅くなる。
514
-
514
+
515
- →**それでも「barakbさんのライブラリもパラレル」が速い。**
515
+ →**それでも「barakbさんのライブラリもパラレル」が速い。** → 「LRUなしのパラレル」に迫る勢い。
516
516
 
517
517
 
518
518
 

5

Javaでの速度計測を追記

2017/10/12 00:33

投稿

mosa
mosa

スコア218

test CHANGED
File without changes
test CHANGED
@@ -4,7 +4,7 @@
4
4
 
5
5
 
6
6
 
7
- ### 要件
7
+ ### 要件
8
8
 
9
9
 
10
10
 
@@ -32,7 +32,7 @@
32
32
 
33
33
 
34
34
 
35
- ### 質問
35
+ ### 質問
36
36
 
37
37
 
38
38
 
@@ -52,88 +52,42 @@
52
52
 
53
53
 
54
54
 
55
- ### ソースコード →バグあ
55
+ ### ソースコード (ご指摘によ一部修正)
56
56
 
57
57
 
58
58
 
59
59
  ```Kotlin
60
60
 
61
+ import java.util.*
62
+
61
63
  class LruCache<K, V>(val capacity: Int, private val initializer: (K) -> V) {
62
64
 
63
-
64
-
65
- private val map = Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
65
+ private val map = java.util.Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
66
-
66
+
67
- override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?): Boolean {
67
+ override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?) = size > capacity
68
-
69
- return size > capacity
70
-
71
- }
72
68
 
73
69
  })
74
70
 
75
-
76
-
77
- operator fun get(key: K): V {
78
-
79
- return map.getOrPut(key) {
80
-
81
- if (!map.containsKey(key)) map[key] = initializer(key)
71
+ operator fun get(key: K): V = map.computeIfAbsent(key, initializer)
82
-
83
- return map[key]!!
84
-
85
- }
86
-
87
- }
88
-
89
-
90
72
 
91
73
  override fun toString() = map.toString()
92
74
 
93
-
94
-
95
- }
75
+ }
96
-
76
+
97
- ```
77
+ ```
98
-
99
-
100
-
78
+
79
+
80
+
81
+
82
+
83
+
84
+
101
- ### ご指摘を受けての修正版ソースコード
85
+ ### ■使用例
102
86
 
103
87
 
104
88
 
105
89
  ```Kotlin
106
90
 
107
- import java.util.*
108
-
109
- class LruCache<K, V>(val capacity: Int, private val initializer: (K) -> V) {
110
-
111
- private val map = java.util.Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
112
-
113
- override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?) = size > capacity
114
-
115
- })
116
-
117
- operator fun get(key: K): V = map.computeIfAbsent(key, initializer)
118
-
119
- override fun toString() = map.toString()
120
-
121
- }
122
-
123
- ```
124
-
125
-
126
-
127
-
128
-
129
-
130
-
131
- ### 使用例
132
-
133
-
134
-
135
- ```Kotlin
136
-
137
91
  // 容量が3で2倍の値を返し、計算結果をmemoizeするキャッシュ
138
92
 
139
93
  val cache = LruCache<Int, Int>(capacity=3) { it * 2 }
@@ -148,7 +102,7 @@
148
102
 
149
103
 
150
104
 
151
- ### 実行結果
105
+ ### 実行結果
152
106
 
153
107
 
154
108
 
@@ -162,96 +116,428 @@
162
116
 
163
117
 
164
118
 
165
-
166
-
167
119
  ---
168
120
 
169
121
 
170
122
 
123
+ ### ========追記========
124
+
125
+
126
+
127
+ ---
128
+
129
+
130
+
171
- ### ここから追記
131
+ ### ■速度計測
172
-
173
-
174
-
175
-
176
-
132
+
133
+
134
+
177
- ##### **■キャッシュなしの実装** の速度
135
+ キャッシュクラスを5種類用意。(今回はJavaで)
136
+
178
-
137
+ * キャッシュなし
138
+
179
-
139
+ * LRUしないキャッシュ
140
+
180
-
141
+ * Collections.syncronizedMap() で排他するLRUキャッシュ
142
+
143
+ * StampedLock.writeLock() で排他するLRUキャッシュ
144
+
145
+ * daisuke7さんに教えていただいた[barakbさんのライブラリ](https://github.com/barakb/Cache)
146
+
147
+
148
+
181
- ```Kotlin
149
+ ```Java
150
+
151
+ import org.async.utils.cache.Cache;
152
+
153
+
154
+
155
+ import java.util.Collections;
156
+
157
+ import java.util.LinkedHashMap;
158
+
159
+ import java.util.Map;
160
+
161
+ import java.util.concurrent.locks.StampedLock;
162
+
163
+ import java.util.function.Function;
164
+
165
+
166
+
167
+ interface Calculator<K, V>{ V get(K key); }
168
+
169
+
182
170
 
183
171
  // キャッシュなし
184
172
 
185
- val cache = object {
186
-
187
- operator fun get(key: Int) = Math.sin(key.toDouble()) * Math.cos(key.toDouble()) * Math.tan(key.toDouble())
188
-
189
- }
190
-
191
-
192
-
193
- // マルチスレッドでアクセス
194
-
195
- val executorService = Executors.newFixedThreadPool(8)
196
-
197
- val start = System.currentTimeMillis()
198
-
199
- (1..10000000).forEach { cache[it / 3] }
200
-
201
- executorService.shutdown()
202
-
203
- println((System.currentTimeMillis() - start).toString() + "ms")
204
-
205
- ```
206
-
207
-
208
-
209
- →10回処理した結果 **平均:7286.2ms** ```7452ms, 7126ms, 7296ms, 7287ms, 7276ms, 7281ms, 7298ms, 7277ms, 7295ms, 7274ms```
210
-
211
-
212
-
213
-
214
-
215
-
216
-
217
- ##### **■質問者の実装** の速度
218
-
219
-
220
-
221
- ```Kotlin
222
-
223
- val cache = LruCache<Int, Double>(10000) { Math.sin(it.toDouble()) * Math.cos(it.toDouble()) * Math.tan(it.toDouble()) }
224
-
225
- // あとは一緒
226
-
227
- ```
228
-
229
-
230
-
231
- →10回処理した結果 **平均:2772.7ms** ```3239ms, 3069ms, 2702ms, 2666ms, 2677ms, 2667ms, 2675ms, 2677ms, 2690ms, 2665ms```
232
-
233
-
234
-
235
-
236
-
237
-
238
-
239
-
240
-
241
-
242
-
243
- ##### **■barakbさんのライブラリ** の速度
244
-
245
-
246
-
247
- ```Kotlin
248
-
249
- val cache = Cache<Int, Double>({ Math.sin(it.toDouble()) * Math.cos(it.toDouble()) * Math.tan(it.toDouble()) }, 10000)
250
-
251
- // あとは一緒
252
-
253
- ```
254
-
255
-
256
-
257
- →10回処理した結果 **平均:3049.1ms** ```3513ms, 3292ms, 3089ms, 2933ms, 2932ms, 2949ms, 2948ms, 2934ms, 2949ms, 2952ms```
173
+ class NonCache<K, V> implements Calculator<K, V>{
174
+
175
+ private Function<K, V> initializer;
176
+
177
+ NonCache(Function<K, V> initializer){ this.initializer = initializer; }
178
+
179
+ @Override public V get(K key){ return initializer.apply(key); }
180
+
181
+ }
182
+
183
+
184
+
185
+ // LRUしないキャッシュ
186
+
187
+ class NonLruCache<K, V> implements Calculator<K, V>{
188
+
189
+ private int capacity;
190
+
191
+ private Function<K, V> initializer;
192
+
193
+ private Map<K, V> map = new LinkedHashMap<>(capacity, 1.0f, true);
194
+
195
+ NonLruCache(int capacity, Function<K, V> initializer){
196
+
197
+ this.capacity = capacity;
198
+
199
+ this.initializer = initializer;
200
+
201
+ }
202
+
203
+ @Override public V get(K key){ return map.computeIfAbsent(key, initializer); }
204
+
205
+ }
206
+
207
+
208
+
209
+ // Collections.syncronizedMap() で排他するLRUキャッシュ
210
+
211
+ class LruCache1<K, V> implements Calculator<K, V>{
212
+
213
+ private int capacity;
214
+
215
+ private Function<K, V> initializer;
216
+
217
+ private Map<K, V> map = Collections.synchronizedMap(new LinkedHashMap<K, V>(capacity, 1.0f, true){
218
+
219
+ @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
220
+
221
+ });
222
+
223
+ LruCache1(int capacity, Function<K, V> initializer){
224
+
225
+ this.capacity = capacity;
226
+
227
+ this.initializer = initializer;
228
+
229
+ }
230
+
231
+ @Override public V get(K key){ return map.computeIfAbsent(key, initializer); }
232
+
233
+ }
234
+
235
+
236
+
237
+ // StampedLock.writeLock() で排他するLRUキャッシュ
238
+
239
+ class LruCache2<K, V> implements Calculator<K, V>{
240
+
241
+ private int capacity;
242
+
243
+ private StampedLock lock = new StampedLock();
244
+
245
+ private Function<K, V> initializer;
246
+
247
+ private Map<K, V> map = new LinkedHashMap<K, V>(capacity, 1.0f, true){
248
+
249
+ @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
250
+
251
+ };
252
+
253
+ LruCache2(int capacity, Function<K, V> initializer){
254
+
255
+ this.capacity = capacity;
256
+
257
+ this.initializer = initializer;
258
+
259
+ }
260
+
261
+ @Override public V get(K key){
262
+
263
+ long stamp = lock.writeLock();
264
+
265
+ try{ return map.computeIfAbsent(key, initializer); } finally{ lock.unlockWrite(stamp); }
266
+
267
+ }
268
+
269
+
270
+
271
+ }
272
+
273
+
274
+
275
+ // daisuke7さんに教えていただいたbarakbさんのライブラリ https://github.com/barakb/Cache
276
+
277
+ class LruCache3<K, V> implements Calculator<K, V>{
278
+
279
+ private Cache<K, V> cache;
280
+
281
+ LruCache3(int capacity, Function<K, V> initializer){ cache = new Cache<>(initializer::apply, capacity); }
282
+
283
+ @Override public V get(K key){
284
+
285
+ try{ return cache.get(key); } catch(Throwable throwable){ throw new RuntimeException(throwable); }
286
+
287
+ }
288
+
289
+
290
+
291
+ }
292
+
293
+ ```
294
+
295
+
296
+
297
+ 以下のプログラムで実行。(メモリやGCに注意が必要)
298
+
299
+
300
+
301
+
302
+
303
+ ```Java
304
+
305
+ import java.util.Collections;
306
+
307
+ import java.util.List;
308
+
309
+ import java.util.function.Function;
310
+
311
+ import java.util.stream.Collectors;
312
+
313
+ import java.util.stream.IntStream;
314
+
315
+
316
+
317
+ public class LruCacheTest{
318
+
319
+
320
+
321
+ public static final int COST = 1000; // 計算コスト
322
+
323
+ public static final int SIZE = 1000; // 要素数
324
+
325
+ public static final double DUPLICATION_RATE = 0.0; // 重複率
326
+
327
+ public static final double CAPACITY_RATE = 1.0; // 容量率
328
+
329
+ public static final int CAPACITY = (int) (Math.round(SIZE * (1.0 - DUPLICATION_RATE) * CAPACITY_RATE)); // 容量
330
+
331
+ public static final List<Integer> LIST = IntStream.rangeClosed(0, SIZE - 1).boxed()
332
+
333
+ .map(it -> (int) ((double) it * (1.0 - DUPLICATION_RATE))).collect(Collectors.toList());
334
+
335
+ public static final int UNIQUE = (int) LIST.stream().distinct().count(); // ユニーク数
336
+
337
+
338
+
339
+ public static void main(String... args){
340
+
341
+ Function<Integer, Integer> calc = it -> it + IntStream.rangeClosed(1, COST * 10000).sum();
342
+
343
+ Collections.shuffle(LIST); // リストをシャッフル
344
+
345
+ System.out.println("計算コスト:" + COST + " 要素数:" + SIZE + " ユニーク数:" + UNIQUE + " 重複率:" + DUPLICATION_RATE + " 容量:" + CAPACITY + " 容量率:" + CAPACITY_RATE);
346
+
347
+ seri("キャッシュなし", new NonCache<>(calc));
348
+
349
+ para("キャッシュなし", new NonCache<>(calc));
350
+
351
+ seri("LRUなし", new NonLruCache<>(CAPACITY, calc));
352
+
353
+ para("LRUなし", new NonLruCache<>(CAPACITY, calc));
354
+
355
+ seri("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
356
+
357
+ para("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
358
+
359
+ seri("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
360
+
361
+ para("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
362
+
363
+ seri("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
364
+
365
+ para("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
366
+
367
+ }
368
+
369
+
370
+
371
+ private static void seri(String name, Calculator<Integer, Integer> calculator){
372
+
373
+ long start = System.currentTimeMillis();
374
+
375
+ LIST.stream().mapToInt(calculator::get).sum();
376
+
377
+ System.out.println(name + " シリアル " + (System.currentTimeMillis() - start) + "ms. ");
378
+
379
+ }
380
+
381
+
382
+
383
+ private static void para(String name, Calculator<Integer, Integer> calculator){
384
+
385
+ long start = System.currentTimeMillis();
386
+
387
+ LIST.parallelStream().mapToInt(calculator::get).sum();
388
+
389
+ System.out.println(name + " パラレル " + (System.currentTimeMillis() - start) + "ms. ");
390
+
391
+ }
392
+
393
+
394
+
395
+ }
396
+
397
+ ```
398
+
399
+
400
+
401
+ ### 速度計測結果
402
+
403
+
404
+
405
+ #### 1.重複なし。キャッシュあふれなし。
406
+
407
+ →「キャッシュなしのパラレル」が当然最速。「LRUなしのパラレル」も排他がないため高速。
408
+
409
+ →**「barakbさんのライブラリもパラレル」も高速。すごい。**
410
+
411
+
412
+
413
+ ```
414
+
415
+ 計算コスト:1000 要素数:1000 ユニーク数:1000 重複率:0.0 容量:1000 容量率:1.0
416
+
417
+ キャッシュなし シリアル 3000ms.
418
+
419
+ キャッシュなし パラレル 519ms.
420
+
421
+ LRUなし シリアル 2998ms.
422
+
423
+ LRUなし パラレル 530ms.
424
+
425
+ LRUキャッシュ1 シリアル 3004ms.
426
+
427
+ LRUキャッシュ1 パラレル 3030ms.
428
+
429
+ LRUキャッシュ2 シリアル 3009ms.
430
+
431
+ LRUキャッシュ2 パラレル 3029ms.
432
+
433
+ barakbさんのライブラリ シリアル 3007ms.
434
+
435
+ barakbさんのライブラリ パラレル 514ms.
436
+
437
+ ```
438
+
439
+
440
+
441
+ ### 2.2割重複。キャッシュあふれなし。
442
+
443
+ →当然、「キャッシュなし」以外すべて早くなる。
444
+
445
+
446
+
447
+ ```
448
+
449
+ 計算コスト:1000 要素数:1000 ユニーク数:800 重複率:0.2 容量:800 容量率:1.0
450
+
451
+ キャッシュなし シリアル 2958ms.
452
+
453
+ キャッシュなし パラレル 517ms.
454
+
455
+ LRUなし シリアル 2369ms.
456
+
457
+ LRUなし パラレル 471ms.
458
+
459
+ LRUキャッシュ1 シリアル 2358ms.
460
+
461
+ LRUキャッシュ1 パラレル 2393ms.
462
+
463
+ LRUキャッシュ2 シリアル 2365ms.
464
+
465
+ LRUキャッシュ2 パラレル 2382ms.
466
+
467
+ barakbさんのライブラリ シリアル 2351ms.
468
+
469
+ barakbさんのライブラリ パラレル 389ms.
470
+
471
+ ```
472
+
473
+
474
+
475
+ #### 3.8割重複。キャッシュあふれなし。
476
+
477
+ →すごく早くなる。キャッシュの効果が大きい。
478
+
479
+ →「barakbさんのライブラリもパラレル」があいかわらずすごい。
480
+
481
+
482
+
483
+ ```
484
+
485
+ 計算コスト:1000 要素数:1000 ユニーク数:200 重複率:0.8 容量:200 容量率:1.0
486
+
487
+ キャッシュなし シリアル 3028ms.
488
+
489
+ キャッシュなし パラレル 525ms.
490
+
491
+ LRUなし シリアル 596ms.
492
+
493
+ LRUなし パラレル 136ms.
494
+
495
+ LRUキャッシュ1 シリアル 615ms.
496
+
497
+ LRUキャッシュ1 パラレル 624ms.
498
+
499
+ LRUキャッシュ2 シリアル 611ms.
500
+
501
+ LRUキャッシュ2 パラレル 614ms.
502
+
503
+ barakbさんのライブラリ シリアル 595ms.
504
+
505
+ barakbさんのライブラリ パラレル 99ms.
506
+
507
+ ```
508
+
509
+
510
+
511
+ #### 4.2割重複。キャッシュあふれあり。
512
+
513
+ →LRUがあると当然、やや遅くなる。
514
+
515
+ →**それでも「barakbさんのライブラリもパラレル」が速い。**
516
+
517
+
518
+
519
+ ```
520
+
521
+ 計算コスト:1000 要素数:1000 ユニーク数:800 重複率:0.2 容量:400 容量率:0.5
522
+
523
+ キャッシュなし シリアル 2965ms.
524
+
525
+ キャッシュなし パラレル 516ms.
526
+
527
+ LRUなし シリアル 2381ms.
528
+
529
+ LRUなし パラレル 413ms.
530
+
531
+ LRUキャッシュ1 シリアル 2546ms.
532
+
533
+ LRUキャッシュ1 パラレル 2577ms.
534
+
535
+ LRUキャッシュ2 シリアル 2549ms.
536
+
537
+ LRUキャッシュ2 パラレル 2521ms.
538
+
539
+ barakbさんのライブラリ シリアル 2525ms.
540
+
541
+ barakbさんのライブラリ パラレル 456ms.
542
+
543
+ ```

4

誤記修正

2017/10/11 11:31

投稿

mosa
mosa

スコア218

test CHANGED
File without changes
test CHANGED
@@ -114,7 +114,7 @@
114
114
 
115
115
  })
116
116
 
117
- override operator fun get(key: K): V = map.computeIfAbsent(key, initializer)
117
+ operator fun get(key: K): V = map.computeIfAbsent(key, initializer)
118
118
 
119
119
  override fun toString() = map.toString()
120
120
 

3

コメントでのご指摘を受けての修正

2017/10/10 05:04

投稿

mosa
mosa

スコア218

test CHANGED
File without changes
test CHANGED
@@ -104,17 +104,19 @@
104
104
 
105
105
  ```Kotlin
106
106
 
107
+ import java.util.*
108
+
107
109
  class LruCache<K, V>(val capacity: Int, private val initializer: (K) -> V) {
108
110
 
109
- private val map = Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
111
+ private val map = java.util.Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
110
-
112
+
111
- override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?) = size > capacity
113
+ override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?) = size > capacity
112
-
114
+
113
- })
115
+ })
114
-
116
+
115
- operator fun get(key: K): V = map.getOrPut(key) { initializer(key) }
117
+ override operator fun get(key: K): V = map.computeIfAbsent(key, initializer)
116
-
118
+
117
- override fun toString() = map.toString()
119
+ override fun toString() = map.toString()
118
120
 
119
121
  }
120
122
 

2

ご指摘を受けての修正

2017/10/10 05:03

投稿

mosa
mosa

スコア218

test CHANGED
File without changes
test CHANGED
@@ -52,7 +52,7 @@
52
52
 
53
53
 
54
54
 
55
- ### ソースコード
55
+ ### ソースコード →バグあり
56
56
 
57
57
 
58
58
 
@@ -98,6 +98,30 @@
98
98
 
99
99
 
100
100
 
101
+ ### ご指摘を受けての修正版ソースコード
102
+
103
+
104
+
105
+ ```Kotlin
106
+
107
+ class LruCache<K, V>(val capacity: Int, private val initializer: (K) -> V) {
108
+
109
+ private val map = Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
110
+
111
+ override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?) = size > capacity
112
+
113
+ })
114
+
115
+ operator fun get(key: K): V = map.getOrPut(key) { initializer(key) }
116
+
117
+ override fun toString() = map.toString()
118
+
119
+ }
120
+
121
+ ```
122
+
123
+
124
+
101
125
 
102
126
 
103
127
 

1

速度などを追記

2017/10/10 00:30

投稿

mosa
mosa

スコア218

test CHANGED
File without changes
test CHANGED
@@ -133,3 +133,99 @@
133
133
  {2=4, 3=6, 4=8}
134
134
 
135
135
  ```
136
+
137
+
138
+
139
+
140
+
141
+ ---
142
+
143
+
144
+
145
+ ### ここから追記
146
+
147
+
148
+
149
+
150
+
151
+ ##### **■キャッシュなしの実装** の速度
152
+
153
+
154
+
155
+ ```Kotlin
156
+
157
+ // キャッシュなし
158
+
159
+ val cache = object {
160
+
161
+ operator fun get(key: Int) = Math.sin(key.toDouble()) * Math.cos(key.toDouble()) * Math.tan(key.toDouble())
162
+
163
+ }
164
+
165
+
166
+
167
+ // マルチスレッドでアクセス
168
+
169
+ val executorService = Executors.newFixedThreadPool(8)
170
+
171
+ val start = System.currentTimeMillis()
172
+
173
+ (1..10000000).forEach { cache[it / 3] }
174
+
175
+ executorService.shutdown()
176
+
177
+ println((System.currentTimeMillis() - start).toString() + "ms")
178
+
179
+ ```
180
+
181
+
182
+
183
+ →10回処理した結果 **平均:7286.2ms** ```7452ms, 7126ms, 7296ms, 7287ms, 7276ms, 7281ms, 7298ms, 7277ms, 7295ms, 7274ms```
184
+
185
+
186
+
187
+
188
+
189
+
190
+
191
+ ##### **■質問者の実装** の速度
192
+
193
+
194
+
195
+ ```Kotlin
196
+
197
+ val cache = LruCache<Int, Double>(10000) { Math.sin(it.toDouble()) * Math.cos(it.toDouble()) * Math.tan(it.toDouble()) }
198
+
199
+ // あとは一緒
200
+
201
+ ```
202
+
203
+
204
+
205
+ →10回処理した結果 **平均:2772.7ms** ```3239ms, 3069ms, 2702ms, 2666ms, 2677ms, 2667ms, 2675ms, 2677ms, 2690ms, 2665ms```
206
+
207
+
208
+
209
+
210
+
211
+
212
+
213
+
214
+
215
+
216
+
217
+ ##### **■barakbさんのライブラリ** の速度
218
+
219
+
220
+
221
+ ```Kotlin
222
+
223
+ val cache = Cache<Int, Double>({ Math.sin(it.toDouble()) * Math.cos(it.toDouble()) * Math.tan(it.toDouble()) }, 10000)
224
+
225
+ // あとは一緒
226
+
227
+ ```
228
+
229
+
230
+
231
+ →10回処理した結果 **平均:3049.1ms** ```3513ms, 3292ms, 3089ms, 2933ms, 2932ms, 2949ms, 2948ms, 2934ms, 2949ms, 2952ms```