質問編集履歴
6
誤字など修正
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
|
-
|
88
|
+
private Function<K, V> initializer;
|
89
|
-
|
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
|
-
|
95
|
+
private int capacity;
|
96
|
-
|
96
|
+
private Function<K, V> initializer;
|
97
|
-
|
97
|
+
private Map<K, V> map = new LinkedHashMap<>(capacity, 1.0f, true);
|
98
|
-
|
98
|
+
NonLruCache(int capacity, Function<K, V> initializer){
|
99
|
-
|
99
|
+
this.capacity = capacity;
|
100
|
-
|
100
|
+
this.initializer = initializer;
|
101
|
-
|
101
|
+
}
|
102
|
-
|
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
|
-
|
107
|
+
private int capacity;
|
108
|
-
|
108
|
+
private Function<K, V> initializer;
|
109
|
-
|
109
|
+
private Map<K, V> map = Collections.synchronizedMap(new LinkedHashMap<K, V>(capacity, 1.0f, true){
|
110
|
-
|
110
|
+
@Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
|
111
|
-
|
111
|
+
});
|
112
|
-
|
112
|
+
LruCache1(int capacity, Function<K, V> initializer){
|
113
|
-
|
113
|
+
this.capacity = capacity;
|
114
|
-
|
114
|
+
this.initializer = initializer;
|
115
|
-
|
115
|
+
}
|
116
|
-
|
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
|
-
|
121
|
+
private int capacity;
|
122
|
-
|
122
|
+
private StampedLock lock = new StampedLock();
|
123
|
-
|
123
|
+
private Function<K, V> initializer;
|
124
|
-
|
124
|
+
private Map<K, V> map = new LinkedHashMap<K, V>(capacity, 1.0f, true){
|
125
|
-
|
125
|
+
@Override protected boolean removeEldestEntry(Map.Entry<K, V> eldest){ return size() > capacity; }
|
126
|
-
|
126
|
+
};
|
127
|
-
|
127
|
+
LruCache2(int capacity, Function<K, V> initializer){
|
128
|
-
|
128
|
+
this.capacity = capacity;
|
129
|
-
|
129
|
+
this.initializer = initializer;
|
130
|
-
|
130
|
+
}
|
131
|
-
|
131
|
+
@Override public V get(K key){
|
132
|
-
|
132
|
+
long stamp = lock.writeLock();
|
133
|
-
|
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
|
-
|
161
|
+
public static final int COST = 1000; // 計算コスト
|
162
|
-
|
162
|
+
public static final int SIZE = 1000; // 要素数
|
163
|
-
|
163
|
+
public static final double DUPLICATION_RATE = 0.0; // 重複率
|
164
|
-
|
164
|
+
public static final double CAPACITY_RATE = 1.0; // 容量率
|
165
|
-
|
165
|
+
public static final int CAPACITY = (int) (Math.round(SIZE * (1.0 - DUPLICATION_RATE) * CAPACITY_RATE)); // 容量
|
166
|
-
|
166
|
+
public static final List<Integer> LIST = IntStream.rangeClosed(0, SIZE - 1).boxed()
|
167
|
-
|
167
|
+
.map(it -> (int) ((double) it * (1.0 - DUPLICATION_RATE))).collect(Collectors.toList());
|
168
|
-
|
168
|
+
public static final int UNIQUE = (int) LIST.stream().distinct().count(); // ユニーク数
|
169
169
|
|
170
|
-
|
170
|
+
public static void main(String... args){
|
171
|
-
|
171
|
+
Function<Integer, Integer> calc = it -> it + IntStream.rangeClosed(1, COST * 10000).sum();
|
172
|
-
|
172
|
+
Collections.shuffle(LIST); // リストをシャッフル
|
173
|
-
|
173
|
+
System.out.println("計算コスト:" + COST + " 要素数:" + SIZE + " ユニーク数:" + UNIQUE + " 重複率:" + DUPLICATION_RATE + " 容量:" + CAPACITY + " 容量率:" + CAPACITY_RATE);
|
174
|
-
|
174
|
+
seri("キャッシュなし", new NonCache<>(calc));
|
175
|
-
|
175
|
+
para("キャッシュなし", new NonCache<>(calc));
|
176
|
-
|
176
|
+
seri("LRUなし", new NonLruCache<>(CAPACITY, calc));
|
177
|
-
|
177
|
+
para("LRUなし", new NonLruCache<>(CAPACITY, calc));
|
178
|
-
|
178
|
+
seri("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
|
179
|
-
|
179
|
+
para("LRUキャッシュ1", new LruCache1(CAPACITY, calc));
|
180
|
-
|
180
|
+
seri("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
|
181
|
-
|
181
|
+
para("LRUキャッシュ2", new LruCache2(CAPACITY, calc));
|
182
|
-
|
182
|
+
seri("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
|
183
|
-
|
183
|
+
para("barakbさんのライブラリ", new LruCache3(CAPACITY, calc));
|
184
|
-
|
184
|
+
}
|
185
185
|
|
186
|
-
|
186
|
+
private static void seri(String name, Calculator<Integer, Integer> calculator){
|
187
|
-
|
187
|
+
long start = System.currentTimeMillis();
|
188
|
-
|
188
|
+
LIST.stream().mapToInt(calculator::get).sum();
|
189
|
-
|
189
|
+
System.out.println(name + " シリアル " + (System.currentTimeMillis() - start) + "ms. ");
|
190
|
-
|
190
|
+
}
|
191
191
|
|
192
|
-
|
192
|
+
private static void para(String name, Calculator<Integer, Integer> calculator){
|
193
|
-
|
193
|
+
long start = System.currentTimeMillis();
|
194
|
-
|
194
|
+
LIST.parallelStream().mapToInt(calculator::get).sum();
|
195
|
-
|
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での速度計測を追記
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
|
-
|
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
|
-
|
88
|
+
private Function<K, V> initializer;
|
94
|
-
|
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
|
-
|
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
|
-
|
132
|
+
long stamp = lock.writeLock();
|
100
|
-
(1..10000000).forEach { cache[it / 3] }
|
101
|
-
executorService.shutdown()
|
102
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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
誤記修正
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
|
-
|
59
|
+
operator fun get(key: K): V = map.computeIfAbsent(key, initializer)
|
60
60
|
override fun toString() = map.toString()
|
61
61
|
}
|
62
62
|
```
|
3
コメントでのご指摘を受けての修正
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
|
-
|
56
|
+
private val map = java.util.Collections.synchronizedMap(object : LinkedHashMap<K, V>(capacity, 1.0F, true) {
|
56
|
-
|
57
|
+
override fun removeEldestEntry(eldest: MutableMap.MutableEntry<K, V>?) = size > capacity
|
57
|
-
|
58
|
+
})
|
58
|
-
|
59
|
+
override operator fun get(key: K): V = map.computeIfAbsent(key, initializer)
|
59
|
-
|
60
|
+
override fun toString() = map.toString()
|
60
61
|
}
|
61
62
|
```
|
62
63
|
|
2
ご指摘を受けての修正
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
速度などを追記
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```
|