質問するログイン新規登録

質問編集履歴

6

誤字など修正

2017/10/12 00:33

投稿

mosa
mosa

スコア218

title CHANGED
File without changes
body CHANGED
@@ -67,7 +67,7 @@
67
67
 
68
68
  キャッシュクラスを5種類用意。(今回はJavaで)
69
69
  * キャッシュなし
70
- * LRUしないキャッシュ
70
+ * LRUで削除しないキャッシュ
71
71
  * Collections.syncronizedMap() で排他するLRUキャッシュ
72
72
  * StampedLock.writeLock() で排他するLRUキャッシュ
73
73
  * daisuke7さんに教えていただいた[barakbさんのライブラリ](https://github.com/barakb/Cache)
@@ -85,53 +85,53 @@
85
85
 
86
86
  // キャッシュなし
87
87
  class NonCache<K, V> implements Calculator<K, V>{
88
- private Function<K, V> initializer;
88
+ private Function<K, V> initializer;
89
- NonCache(Function<K, V> initializer){ this.initializer = initializer; }
89
+ NonCache(Function<K, V> initializer){ this.initializer = initializer; }
90
- @Override public V get(K key){ return initializer.apply(key); }
90
+ @Override public V get(K key){ return initializer.apply(key); }
91
91
  }
92
92
 
93
93
  // LRUしないキャッシュ
94
94
  class NonLruCache<K, V> implements Calculator<K, V>{
95
- private int capacity;
95
+ private int capacity;
96
- private Function<K, V> initializer;
96
+ private Function<K, V> initializer;
97
- private Map<K, V> map = new LinkedHashMap<>(capacity, 1.0f, true);
97
+ private Map<K, V> map = new LinkedHashMap<>(capacity, 1.0f, true);
98
- NonLruCache(int capacity, Function<K, V> initializer){
98
+ NonLruCache(int capacity, Function<K, V> initializer){
99
- this.capacity = capacity;
99
+ this.capacity = capacity;
100
- this.initializer = initializer;
100
+ this.initializer = initializer;
101
- }
101
+ }
102
- @Override public V get(K key){ return map.computeIfAbsent(key, initializer); }
102
+ @Override public V get(K key){ return map.computeIfAbsent(key, initializer); }
103
103
  }
104
104
 
105
105
  // Collections.syncronizedMap() で排他するLRUキャッシュ
106
106
  class LruCache1<K, V> implements Calculator<K, V>{
107
- private int capacity;
107
+ private int capacity;
108
- private Function<K, V> initializer;
108
+ private Function<K, V> initializer;
109
- private Map<K, V> map = Collections.synchronizedMap(new LinkedHashMap<K, V>(capacity, 1.0f, true){
109
+ private Map<K, V> map = Collections.synchronizedMap(new LinkedHashMap<K, V>(capacity, 1.0f, true){
110
- @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
110
+ @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
111
- });
111
+ });
112
- LruCache1(int capacity, Function<K, V> initializer){
112
+ LruCache1(int capacity, Function<K, V> initializer){
113
- this.capacity = capacity;
113
+ this.capacity = capacity;
114
- this.initializer = initializer;
114
+ this.initializer = initializer;
115
- }
115
+ }
116
- @Override public V get(K key){ return map.computeIfAbsent(key, initializer); }
116
+ @Override public V get(K key){ return map.computeIfAbsent(key, initializer); }
117
117
  }
118
118
 
119
119
  // StampedLock.writeLock() で排他するLRUキャッシュ
120
120
  class LruCache2<K, V> implements Calculator<K, V>{
121
- private int capacity;
121
+ private int capacity;
122
- private StampedLock lock = new StampedLock();
122
+ private StampedLock lock = new StampedLock();
123
- private Function<K, V> initializer;
123
+ private Function<K, V> initializer;
124
- private Map<K, V> map = new LinkedHashMap<K, V>(capacity, 1.0f, true){
124
+ private Map<K, V> map = new LinkedHashMap<K, V>(capacity, 1.0f, true){
125
- @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
125
+ @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
126
- };
126
+ };
127
- LruCache2(int capacity, Function<K, V> initializer){
127
+ LruCache2(int capacity, Function<K, V> initializer){
128
- this.capacity = capacity;
128
+ this.capacity = capacity;
129
- this.initializer = initializer;
129
+ this.initializer = initializer;
130
- }
130
+ }
131
- @Override public V get(K key){
131
+ @Override public V get(K key){
132
- long stamp = lock.writeLock();
132
+ long stamp = lock.writeLock();
133
- try{ return map.computeIfAbsent(key, initializer); } finally{ lock.unlockWrite(stamp); }
133
+ try{ return map.computeIfAbsent(key, initializer); } finally{ lock.unlockWrite(stamp); }
134
- }
134
+ }
135
135
 
136
136
  }
137
137
 
@@ -158,51 +158,51 @@
158
158
 
159
159
  public class LruCacheTest{
160
160
 
161
- public static final int COST = 1000; // 計算コスト
161
+ public static final int COST = 1000; // 計算コスト
162
- public static final int SIZE = 1000; // 要素数
162
+ public static final int SIZE = 1000; // 要素数
163
- public static final double DUPLICATION_RATE = 0.0; // 重複率
163
+ public static final double DUPLICATION_RATE = 0.0; // 重複率
164
- public static final double CAPACITY_RATE = 1.0; // 容量率
164
+ public static final double CAPACITY_RATE = 1.0; // 容量率
165
- public static final int CAPACITY = (int) (Math.round(SIZE * (1.0 - DUPLICATION_RATE) * CAPACITY_RATE)); // 容量
165
+ public static final int CAPACITY = (int) (Math.round(SIZE * (1.0 - DUPLICATION_RATE) * CAPACITY_RATE)); // 容量
166
- public static final List<Integer> LIST = IntStream.rangeClosed(0, SIZE - 1).boxed()
166
+ public static final List<Integer> LIST = IntStream.rangeClosed(0, SIZE - 1).boxed()
167
- .map(it -> (int) ((double) it * (1.0 - DUPLICATION_RATE))).collect(Collectors.toList());
167
+ .map(it -> (int) ((double) it * (1.0 - DUPLICATION_RATE))).collect(Collectors.toList());
168
- public static final int UNIQUE = (int) LIST.stream().distinct().count(); // ユニーク数
168
+ public static final int UNIQUE = (int) LIST.stream().distinct().count(); // ユニーク数
169
169
 
170
- public static void main(String... args){
170
+ public static void main(String... args){
171
- Function<Integer, Integer> calc = it -> it + IntStream.rangeClosed(1, COST * 10000).sum();
171
+ Function<Integer, Integer> calc = it -> it + IntStream.rangeClosed(1, COST * 10000).sum();
172
- Collections.shuffle(LIST); // リストをシャッフル
172
+ Collections.shuffle(LIST); // リストをシャッフル
173
- System.out.println("計算コスト:" + COST + " 要素数:" + SIZE + " ユニーク数:" + UNIQUE + " 重複率:" + DUPLICATION_RATE + " 容量:" + CAPACITY + " 容量率:" + CAPACITY_RATE);
173
+ System.out.println("計算コスト:" + COST + " 要素数:" + SIZE + " ユニーク数:" + UNIQUE + " 重複率:" + DUPLICATION_RATE + " 容量:" + CAPACITY + " 容量率:" + CAPACITY_RATE);
174
- seri("キャッシュなし", new NonCache<>(calc));
174
+ seri("キャッシュなし", new NonCache<>(calc));
175
- para("キャッシュなし", new NonCache<>(calc));
175
+ para("キャッシュなし", new NonCache<>(calc));
176
- seri("LRUなし", new NonLruCache<>(CAPACITY, calc));
176
+ seri("LRUなし", new NonLruCache<>(CAPACITY, calc));
177
- para("LRUなし", new NonLruCache<>(CAPACITY, calc));
177
+ para("LRUなし", new NonLruCache<>(CAPACITY, calc));
178
- seri("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
178
+ seri("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
179
- para("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
179
+ para("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
180
- seri("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
180
+ seri("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
181
- para("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
181
+ para("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
182
- seri("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
182
+ seri("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
183
- para("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
183
+ para("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
184
- }
184
+ }
185
185
 
186
- private static void seri(String name, Calculator<Integer, Integer> calculator){
186
+ private static void seri(String name, Calculator<Integer, Integer> calculator){
187
- long start = System.currentTimeMillis();
187
+ long start = System.currentTimeMillis();
188
- LIST.stream().mapToInt(calculator::get).sum();
188
+ LIST.stream().mapToInt(calculator::get).sum();
189
- System.out.println(name + " シリアル " + (System.currentTimeMillis() - start) + "ms. ");
189
+ System.out.println(name + " シリアル " + (System.currentTimeMillis() - start) + "ms. ");
190
- }
190
+ }
191
191
 
192
- private static void para(String name, Calculator<Integer, Integer> calculator){
192
+ private static void para(String name, Calculator<Integer, Integer> calculator){
193
- long start = System.currentTimeMillis();
193
+ long start = System.currentTimeMillis();
194
- LIST.parallelStream().mapToInt(calculator::get).sum();
194
+ LIST.parallelStream().mapToInt(calculator::get).sum();
195
- System.out.println(name + " パラレル " + (System.currentTimeMillis() - start) + "ms. ");
195
+ System.out.println(name + " パラレル " + (System.currentTimeMillis() - start) + "ms. ");
196
- }
196
+ }
197
197
 
198
198
  }
199
199
  ```
200
200
 
201
- ### 速度計測結果
201
+ ### 速度計測結果
202
202
 
203
203
  #### 1.重複なし。キャッシュあふれなし。
204
204
  →「キャッシュなしのパラレル」が当然最速。「LRUなしのパラレル」も排他がないため高速。
205
- →**「barakbさんのライブラリパラレル」も高速。すごい。**
205
+ →**「barakbさんのライブラリパラレル」も高速。すごい。**
206
206
 
207
207
  ```
208
208
  計算コスト:1000 要素数:1000 ユニーク数:1000 重複率:0.0 容量:1000 容量率:1.0
@@ -219,7 +219,7 @@
219
219
  ```
220
220
 
221
221
  ### 2.2割重複。キャッシュあふれなし。
222
- →当然、「キャッシュなし」以外すべてくなる。
222
+ →当然、「キャッシュなし」以外すべてくなる。
223
223
 
224
224
  ```
225
225
  計算コスト:1000 要素数:1000 ユニーク数:800 重複率:0.2 容量:800 容量率:1.0
@@ -236,8 +236,8 @@
236
236
  ```
237
237
 
238
238
  #### 3.8割重複。キャッシュあふれなし。
239
- →すごくくなる。キャッシュの効果が大きい。
239
+ →すごくくなる。キャッシュの効果が大きい。
240
- →「barakbさんのライブラリもパラレル」があいかわらずすごい。
240
+ **「barakbさんのライブラリもパラレル」があいかわらずすごい。**
241
241
 
242
242
  ```
243
243
  計算コスト:1000 要素数:1000 ユニーク数:200 重複率:0.8 容量:200 容量率:1.0
@@ -254,8 +254,8 @@
254
254
  ```
255
255
 
256
256
  #### 4.2割重複。キャッシュあふれあり。
257
- →LRUがあると当然、やや遅くなる。
257
+ →LRUでの削除があると当然、やや遅くなる。
258
- →**それでも「barakbさんのライブラリもパラレル」が速い。**
258
+ →**それでも「barakbさんのライブラリもパラレル」が速い。** → 「LRUなしのパラレル」に迫る勢い。
259
259
 
260
260
  ```
261
261
  計算コスト:1000 要素数:1000 ユニーク数:800 重複率:0.2 容量:400 容量率:0.5

5

Javaでの速度計測を追記

2017/10/12 00:33

投稿

mosa
mosa

スコア218

title CHANGED
File without changes
body CHANGED
@@ -1,7 +1,7 @@
1
1
  いつもありがとうございます。
2
2
  Kotlin、もしくはJavaで以下の要件を満たすプログラムを考えています。
3
3
 
4
- ### 要件
4
+ ### 要件
5
5
 
6
6
  1.与えられたキーに対して処理を行い結果を返す。
7
7
  2.一度処理した結果は記憶し、同じキーに対してキャッシュが削除されるまで再度同じ計算はしない。
@@ -15,7 +15,7 @@
15
15
 
16
16
 
17
17
 
18
- ### 質問
18
+ ### 質問
19
19
 
20
20
  ・上記の要件を「全て」満たすJava・Kotlinライブラリなどはありますか?(できればAndroidではないやつで)
21
21
  ・下記のソースコードを考えたのですが、正しくない点・懸念される点・もっと良い実装などはありますか?
@@ -25,32 +25,9 @@
25
25
 
26
26
 
27
27
 
28
- ### ソースコード →バグあ
28
+ ### ソースコード (ご指摘によ一部修正)
29
29
 
30
30
  ```Kotlin
31
- class LruCache<K, V>(val capacity: Int, private val initializer: (K) -> V) {
32
-
33
- private val map = Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
34
- override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?): Boolean {
35
- return size > capacity
36
- }
37
- })
38
-
39
- operator fun get(key: K): V {
40
- return map.getOrPut(key) {
41
- if (!map.containsKey(key)) map[key] = initializer(key)
42
- return map[key]!!
43
- }
44
- }
45
-
46
- override fun toString() = map.toString()
47
-
48
- }
49
- ```
50
-
51
- ### ご指摘を受けての修正版ソースコード
52
-
53
- ```Kotlin
54
31
  import java.util.*
55
32
  class LruCache<K, V>(val capacity: Int, private val initializer: (K) -> V) {
56
33
  private val map = java.util.Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
@@ -63,7 +40,7 @@
63
40
 
64
41
 
65
42
 
66
- ### 使用例
43
+ ### 使用例
67
44
 
68
45
  ```Kotlin
69
46
  // 容量が3で2倍の値を返し、計算結果をmemoizeするキャッシュ
@@ -73,57 +50,223 @@
73
50
  ```
74
51
 
75
52
 
76
- ### 実行結果
53
+ ### 実行結果
77
54
 
78
55
  ```
79
56
  2,4,6,8
80
57
  {2=4, 3=6, 4=8}
81
58
  ```
82
59
 
60
+ ---
83
61
 
62
+ ### ========追記========
63
+
84
64
  ---
85
65
 
86
- ### ここから追記
66
+ ### ■速度計測
87
67
 
68
+ キャッシュクラスを5種類用意。(今回はJavaで)
69
+ * キャッシュなし
70
+ * LRUしないキャッシュ
71
+ * Collections.syncronizedMap() で排他するLRUキャッシュ
72
+ * StampedLock.writeLock() で排他するLRUキャッシュ
73
+ * daisuke7さんに教えていただいた[barakbさんのライブラリ](https://github.com/barakb/Cache)
88
74
 
75
+ ```Java
89
- ##### **■キャッシュなしの実装** の速度
76
+ import org.async.utils.cache.Cache;
90
77
 
78
+ import java.util.Collections;
79
+ import java.util.LinkedHashMap;
91
- ```Kotlin
80
+ import java.util.Map;
81
+ import java.util.concurrent.locks.StampedLock;
82
+ import java.util.function.Function;
83
+
84
+ interface Calculator<K, V>{ V get(K key); }
85
+
92
86
  // キャッシュなし
87
+ class NonCache<K, V> implements Calculator<K, V>{
93
- val cache = object {
88
+ private Function<K, V> initializer;
94
- operator fun get(key: Int) = Math.sin(key.toDouble()) * Math.cos(key.toDouble()) * Math.tan(key.toDouble())
89
+ NonCache(Function<K, V> initializer){ this.initializer = initializer; }
90
+ @Override public V get(K key){ return initializer.apply(key); }
95
91
  }
96
92
 
97
- // マルチスレドでアクセス
93
+ // LRUしないキャシュ
94
+ class NonLruCache<K, V> implements Calculator<K, V>{
95
+ private int capacity;
96
+ private Function<K, V> initializer;
97
+ private Map<K, V> map = new LinkedHashMap<>(capacity, 1.0f, true);
98
+ NonLruCache(int capacity, Function<K, V> initializer){
99
+ this.capacity = capacity;
100
+ this.initializer = initializer;
101
+ }
102
+ @Override public V get(K key){ return map.computeIfAbsent(key, initializer); }
103
+ }
104
+
105
+ // Collections.syncronizedMap() で排他するLRUキャッシュ
106
+ class LruCache1<K, V> implements Calculator<K, V>{
107
+ private int capacity;
108
+ private Function<K, V> initializer;
109
+ private Map<K, V> map = Collections.synchronizedMap(new LinkedHashMap<K, V>(capacity, 1.0f, true){
110
+ @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
111
+ });
112
+ LruCache1(int capacity, Function<K, V> initializer){
113
+ this.capacity = capacity;
114
+ this.initializer = initializer;
115
+ }
116
+ @Override public V get(K key){ return map.computeIfAbsent(key, initializer); }
117
+ }
118
+
119
+ // StampedLock.writeLock() で排他するLRUキャッシュ
120
+ class LruCache2<K, V> implements Calculator<K, V>{
121
+ private int capacity;
98
- val executorService = Executors.newFixedThreadPool(8)
122
+ private StampedLock lock = new StampedLock();
123
+ private Function<K, V> initializer;
124
+ private Map<K, V> map = new LinkedHashMap<K, V>(capacity, 1.0f, true){
125
+ @Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
126
+ };
127
+ LruCache2(int capacity, Function<K, V> initializer){
128
+ this.capacity = capacity;
129
+ this.initializer = initializer;
130
+ }
131
+ @Override public V get(K key){
99
- val start = System.currentTimeMillis()
132
+ long stamp = lock.writeLock();
100
- (1..10000000).forEach { cache[it / 3] }
101
- executorService.shutdown()
102
- println((System.currentTimeMillis() - start).toString() + "ms")
133
+ try{ return map.computeIfAbsent(key, initializer); } finally{ lock.unlockWrite(stamp); }
134
+ }
135
+
136
+ }
137
+
138
+ // daisuke7さんに教えていただいたbarakbさんのライブラリ https://github.com/barakb/Cache
139
+ class LruCache3<K, V> implements Calculator<K, V>{
140
+ private Cache<K, V> cache;
141
+ LruCache3(int capacity, Function<K, V> initializer){ cache = new Cache<>(initializer::apply, capacity); }
142
+ @Override public V get(K key){
143
+ try{ return cache.get(key); } catch(Throwable throwable){ throw new RuntimeException(throwable); }
144
+ }
145
+
146
+ }
103
147
  ```
104
148
 
105
- →10回処理した結果 **平均:7286.2ms** ```7452ms, 7126ms, 7296ms, 7287ms, 7276ms, 7281ms, 7298ms, 7277ms, 7295ms, 7274ms```
149
+ 以下のプログラムで実行。(メモリやGCに注意が必要)
106
150
 
107
151
 
152
+ ```Java
153
+ import java.util.Collections;
154
+ import java.util.List;
155
+ import java.util.function.Function;
156
+ import java.util.stream.Collectors;
157
+ import java.util.stream.IntStream;
108
158
 
109
- ##### **■質問者の実装** の速度
159
+ public class LruCacheTest{
110
160
 
161
+ public static final int COST = 1000; // 計算コスト
111
- ```Kotlin
162
+ public static final int SIZE = 1000; // 要素数
163
+ public static final double DUPLICATION_RATE = 0.0; // 重複率
164
+ public static final double CAPACITY_RATE = 1.0; // 容量率
165
+ public static final int CAPACITY = (int) (Math.round(SIZE * (1.0 - DUPLICATION_RATE) * CAPACITY_RATE)); // 容量
166
+ public static final List<Integer> LIST = IntStream.rangeClosed(0, SIZE - 1).boxed()
112
- val cache = LruCache<Int, Double>(10000) { Math.sin(it.toDouble()) * Math.cos(it.toDouble()) * Math.tan(it.toDouble()) }
167
+ .map(it -> (int) ((double) it * (1.0 - DUPLICATION_RATE))).collect(Collectors.toList());
168
+ public static final int UNIQUE = (int) LIST.stream().distinct().count(); // ユニーク数
169
+
170
+ public static void main(String... args){
171
+ Function<Integer, Integer> calc = it -> it + IntStream.rangeClosed(1, COST * 10000).sum();
113
- // あとは一緒
172
+ Collections.shuffle(LIST); // リストをシャッフル
173
+ System.out.println("計算コスト:" + COST + " 要素数:" + SIZE + " ユニーク数:" + UNIQUE + " 重複率:" + DUPLICATION_RATE + " 容量:" + CAPACITY + " 容量率:" + CAPACITY_RATE);
174
+ seri("キャッシュなし", new NonCache<>(calc));
175
+ para("キャッシュなし", new NonCache<>(calc));
176
+ seri("LRUなし", new NonLruCache<>(CAPACITY, calc));
177
+ para("LRUなし", new NonLruCache<>(CAPACITY, calc));
178
+ seri("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
179
+ para("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
180
+ seri("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
181
+ para("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
182
+ seri("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
183
+ para("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
184
+ }
185
+
186
+ private static void seri(String name, Calculator<Integer, Integer> calculator){
187
+ long start = System.currentTimeMillis();
188
+ LIST.stream().mapToInt(calculator::get).sum();
189
+ System.out.println(name + " シリアル " + (System.currentTimeMillis() - start) + "ms. ");
190
+ }
191
+
192
+ private static void para(String name, Calculator<Integer, Integer> calculator){
193
+ long start = System.currentTimeMillis();
194
+ LIST.parallelStream().mapToInt(calculator::get).sum();
195
+ System.out.println(name + " パラレル " + (System.currentTimeMillis() - start) + "ms. ");
196
+ }
197
+
198
+ }
114
199
  ```
115
200
 
116
- →10回処理した結果 **平均:2772.7ms** ```3239ms, 3069ms, 2702ms, 2666ms, 2677ms, 2667ms, 2675ms, 2677ms, 2690ms, 2665ms```
201
+ ### 速度計測結果
117
202
 
203
+ #### 1.重複なし。キャッシュあふれなし。
204
+ →「キャッシュなしのパラレル」が当然最速。「LRUなしのパラレル」も排他がないため高速。
205
+ →**「barakbさんのライブラリもパラレル」も高速。すごい。**
118
206
 
207
+ ```
208
+ 計算コスト:1000 要素数:1000 ユニーク数:1000 重複率:0.0 容量:1000 容量率:1.0
209
+ キャッシュなし シリアル 3000ms.
210
+ キャッシュなし パラレル 519ms.
211
+ LRUなし シリアル 2998ms.
212
+ LRUなし パラレル 530ms.
213
+ LRUキャッシュ1 シリアル 3004ms.
214
+ LRUキャッシュ1 パラレル 3030ms.
215
+ LRUキャッシュ2 シリアル 3009ms.
216
+ LRUキャッシュ2 パラレル 3029ms.
217
+ barakbさんのライブラリ シリアル 3007ms.
218
+ barakbさんのライブラリ パラレル 514ms.
219
+ ```
119
220
 
221
+ ### 2.2割重複。キャッシュあふれなし。
222
+ →当然、「キャッシュなし」以外すべて早くなる。
120
223
 
224
+ ```
225
+ 計算コスト:1000 要素数:1000 ユニーク数:800 重複率:0.2 容量:800 容量率:1.0
226
+ キャッシュなし シリアル 2958ms.
227
+ キャッシュなし パラレル 517ms.
228
+ LRUなし シリアル 2369ms.
229
+ LRUなし パラレル 471ms.
230
+ LRUキャッシュ1 シリアル 2358ms.
231
+ LRUキャッシュ1 パラレル 2393ms.
232
+ LRUキャッシュ2 シリアル 2365ms.
233
+ LRUキャッシュ2 パラレル 2382ms.
234
+ barakbさんのライブラリ シリアル 2351ms.
235
+ barakbさんのライブラリ パラレル 389ms.
236
+ ```
121
237
 
238
+ #### 3.8割重複。キャッシュあふれなし。
239
+ →すごく早くなる。キャッシュの効果が大きい。
122
- ##### **■barakbさんのライブラリ** の速度
240
+ →「barakbさんのライブラリもパラレル」があいかわらずすごい。
123
241
 
124
- ```Kotlin
125
- val cache = Cache<Int, Double>({ Math.sin(it.toDouble()) * Math.cos(it.toDouble()) * Math.tan(it.toDouble()) }, 10000)
126
- // あとは一緒
127
242
  ```
243
+ 計算コスト:1000 要素数:1000 ユニーク数:200 重複率:0.8 容量:200 容量率:1.0
244
+ キャッシュなし シリアル 3028ms.
245
+ キャッシュなし パラレル 525ms.
246
+ LRUなし シリアル 596ms.
247
+ LRUなし パラレル 136ms.
248
+ LRUキャッシュ1 シリアル 615ms.
249
+ LRUキャッシュ1 パラレル 624ms.
250
+ LRUキャッシュ2 シリアル 611ms.
251
+ LRUキャッシュ2 パラレル 614ms.
252
+ barakbさんのライブラリ シリアル 595ms.
253
+ barakbさんのライブラリ パラレル 99ms.
254
+ ```
128
255
 
256
+ #### 4.2割重複。キャッシュあふれあり。
257
+ →LRUがあると当然、やや遅くなる。
258
+ →**それでも「barakbさんのライブラリもパラレル」が速い。**
259
+
260
+ ```
129
- →10回処理した結果 **平均:3049.1ms** ```3513ms, 3292ms, 3089ms, 2933ms, 2932ms, 2949ms, 2948ms, 2934ms, 2949ms, 2952ms```
261
+ 計算コスト:1000 要素数:1000 ユニーク数:800 重複率:0.2 容量:400 容量率:0.5
262
+ キャッシュなし シリアル 2965ms.
263
+ キャッシュなし パラレル 516ms.
264
+ LRUなし シリアル 2381ms.
265
+ LRUなし パラレル 413ms.
266
+ LRUキャッシュ1 シリアル 2546ms.
267
+ LRUキャッシュ1 パラレル 2577ms.
268
+ LRUキャッシュ2 シリアル 2549ms.
269
+ LRUキャッシュ2 パラレル 2521ms.
270
+ barakbさんのライブラリ シリアル 2525ms.
271
+ barakbさんのライブラリ パラレル 456ms.
272
+ ```

4

誤記修正

2017/10/11 11:31

投稿

mosa
mosa

スコア218

title CHANGED
File without changes
body CHANGED
@@ -56,7 +56,7 @@
56
56
  private val map = java.util.Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
57
57
  override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?) = size > capacity
58
58
  })
59
- override operator fun get(key: K): V = map.computeIfAbsent(key, initializer)
59
+ operator fun get(key: K): V = map.computeIfAbsent(key, initializer)
60
60
  override fun toString() = map.toString()
61
61
  }
62
62
  ```

3

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

2017/10/10 05:04

投稿

mosa
mosa

スコア218

title CHANGED
File without changes
body CHANGED
@@ -51,12 +51,13 @@
51
51
  ### ご指摘を受けての修正版ソースコード
52
52
 
53
53
  ```Kotlin
54
+ import java.util.*
54
55
  class LruCache<K, V>(val capacity: Int, private val initializer: (K) -> V) {
55
- private val map = Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
56
+ private val map = java.util.Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
56
- override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?) = size > capacity
57
+ override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?) = size > capacity
57
- })
58
+ })
58
- operator fun get(key: K): V = map.getOrPut(key) { initializer(key) }
59
+ override operator fun get(key: K): V = map.computeIfAbsent(key, initializer)
59
- override fun toString() = map.toString()
60
+ override fun toString() = map.toString()
60
61
  }
61
62
  ```
62
63
 

2

ご指摘を受けての修正

2017/10/10 05:03

投稿

mosa
mosa

スコア218

title CHANGED
File without changes
body CHANGED
@@ -25,7 +25,7 @@
25
25
 
26
26
 
27
27
 
28
- ### ソースコード
28
+ ### ソースコード →バグあり
29
29
 
30
30
  ```Kotlin
31
31
  class LruCache<K, V>(val capacity: Int, private val initializer: (K) -> V) {
@@ -48,8 +48,20 @@
48
48
  }
49
49
  ```
50
50
 
51
+ ### ご指摘を受けての修正版ソースコード
51
52
 
53
+ ```Kotlin
54
+ class LruCache<K, V>(val capacity: Int, private val initializer: (K) -> V) {
55
+ private val map = Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
56
+ override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?) = size > capacity
57
+ })
58
+ operator fun get(key: K): V = map.getOrPut(key) { initializer(key) }
59
+ override fun toString() = map.toString()
60
+ }
61
+ ```
52
62
 
63
+
64
+
53
65
  ### 使用例
54
66
 
55
67
  ```Kotlin

1

速度などを追記

2017/10/10 00:30

投稿

mosa
mosa

スコア218

title CHANGED
File without changes
body CHANGED
@@ -65,4 +65,52 @@
65
65
  ```
66
66
  2,4,6,8
67
67
  {2=4, 3=6, 4=8}
68
- ```
68
+ ```
69
+
70
+
71
+ ---
72
+
73
+ ### ここから追記
74
+
75
+
76
+ ##### **■キャッシュなしの実装** の速度
77
+
78
+ ```Kotlin
79
+ // キャッシュなし
80
+ val cache = object {
81
+ operator fun get(key: Int) = Math.sin(key.toDouble()) * Math.cos(key.toDouble()) * Math.tan(key.toDouble())
82
+ }
83
+
84
+ // マルチスレッドでアクセス
85
+ val executorService = Executors.newFixedThreadPool(8)
86
+ val start = System.currentTimeMillis()
87
+ (1..10000000).forEach { cache[it / 3] }
88
+ executorService.shutdown()
89
+ println((System.currentTimeMillis() - start).toString() + "ms")
90
+ ```
91
+
92
+ →10回処理した結果 **平均:7286.2ms** ```7452ms, 7126ms, 7296ms, 7287ms, 7276ms, 7281ms, 7298ms, 7277ms, 7295ms, 7274ms```
93
+
94
+
95
+
96
+ ##### **■質問者の実装** の速度
97
+
98
+ ```Kotlin
99
+ val cache = LruCache<Int, Double>(10000) { Math.sin(it.toDouble()) * Math.cos(it.toDouble()) * Math.tan(it.toDouble()) }
100
+ // あとは一緒
101
+ ```
102
+
103
+ →10回処理した結果 **平均:2772.7ms** ```3239ms, 3069ms, 2702ms, 2666ms, 2677ms, 2667ms, 2675ms, 2677ms, 2690ms, 2665ms```
104
+
105
+
106
+
107
+
108
+
109
+ ##### **■barakbさんのライブラリ** の速度
110
+
111
+ ```Kotlin
112
+ val cache = Cache<Int, Double>({ Math.sin(it.toDouble()) * Math.cos(it.toDouble()) * Math.tan(it.toDouble()) }, 10000)
113
+ // あとは一緒
114
+ ```
115
+
116
+ →10回処理した結果 **平均:3049.1ms** ```3513ms, 3292ms, 3089ms, 2933ms, 2932ms, 2949ms, 2948ms, 2934ms, 2949ms, 2952ms```