Skip to content
项目
群组
代码片段
帮助
正在加载...
帮助
为 GitLab 提交贡献
登录/注册
切换导航
H
h2database
项目
项目
详情
活动
周期分析
仓库
仓库
文件
提交
分支
标签
贡献者
分枝图
比较
统计图
议题
0
议题
0
列表
看板
标记
里程碑
合并请求
0
合并请求
0
CI / CD
CI / CD
流水线
作业
计划
统计图
Wiki
Wiki
代码片段
代码片段
成员
成员
折叠边栏
关闭边栏
活动
分枝图
统计图
创建新议题
作业
提交
议题看板
打开侧边栏
Administrator
h2database
Commits
1bf1dde3
提交
1bf1dde3
authored
6月 28, 2014
作者:
Thomas Mueller
浏览文件
操作
浏览文件
下载
电子邮件补丁
差异文件
The LIRS cache now re-sizes the internal hash map if needed.
上级
c880a4da
隐藏空白字符变更
内嵌
并排
正在显示
3 个修改的文件
包含
130 行增加
和
48 行删除
+130
-48
changelog.html
h2/src/docsrc/html/changelog.html
+2
-0
CacheLongKeyLIRS.java
h2/src/main/org/h2/mvstore/cache/CacheLongKeyLIRS.java
+112
-48
TestCacheLongKeyLIRS.java
h2/src/test/org/h2/test/store/TestCacheLongKeyLIRS.java
+16
-0
没有找到文件。
h2/src/docsrc/html/changelog.html
浏览文件 @
1bf1dde3
...
@@ -17,6 +17,8 @@ Change Log
...
@@ -17,6 +17,8 @@ Change Log
<h1>
Change Log
</h1>
<h1>
Change Log
</h1>
<h2>
Next Version (unreleased)
</h2>
<h2>
Next Version (unreleased)
</h2>
<ul><li>
The LIRS cache now re-sizes the internal hash map if needed.
</li></ul>
<h2>
Version 1.4.179 Beta (2014-06-23)
</h2>
<h2>
Version 1.4.179 Beta (2014-06-23)
</h2>
<ul><li>
The license was changed to MPL 2.0 (from 1.0) and EPL 1.0.
<ul><li>
The license was changed to MPL 2.0 (from 1.0) and EPL 1.0.
...
...
h2/src/main/org/h2/mvstore/cache/CacheLongKeyLIRS.java
浏览文件 @
1bf1dde3
...
@@ -93,8 +93,8 @@ public class CacheLongKeyLIRS<V> {
...
@@ -93,8 +93,8 @@ public class CacheLongKeyLIRS<V> {
this
.
stackMoveDistance
=
stackMoveDistance
;
this
.
stackMoveDistance
=
stackMoveDistance
;
segments
=
new
Segment
[
segmentCount
];
segments
=
new
Segment
[
segmentCount
];
clear
();
clear
();
this
.
segmentShift
=
Integer
.
numberOfTrailingZeros
(
// use the high bits for the segment
segments
[
0
].
entries
.
length
);
this
.
segmentShift
=
32
-
Integer
.
bitCount
(
segmentMask
);
}
}
/**
/**
...
@@ -102,11 +102,25 @@ public class CacheLongKeyLIRS<V> {
...
@@ -102,11 +102,25 @@ public class CacheLongKeyLIRS<V> {
*/
*/
public
void
clear
()
{
public
void
clear
()
{
long
max
=
Math
.
max
(
1
,
maxMemory
/
segmentCount
);
long
max
=
Math
.
max
(
1
,
maxMemory
/
segmentCount
);
int
segmentLen
=
getSegmentLen
(
max
);
for
(
int
i
=
0
;
i
<
segmentCount
;
i
++)
{
for
(
int
i
=
0
;
i
<
segmentCount
;
i
++)
{
segments
[
i
]
=
new
Segment
<
V
>(
segments
[
i
]
=
new
Segment
<
V
>(
max
,
averageMemory
,
stackMoveDistance
);
max
,
segmentLen
,
stackMoveDistance
);
}
}
}
}
private
int
getSegmentLen
(
long
max
)
{
// calculate the size of the map array
// assume a fill factor of at most 75%
long
maxLen
=
(
long
)
(
max
/
averageMemory
/
0.75
);
// the size needs to be a power of 2
long
l
=
8
;
while
(
l
<
maxLen
)
{
l
+=
l
;
}
// the array size is at most 2^31 elements
return
(
int
)
Math
.
min
(
1L
<<
31
,
l
);
}
private
Entry
<
V
>
find
(
long
key
)
{
private
Entry
<
V
>
find
(
long
key
)
{
int
hash
=
getHash
(
key
);
int
hash
=
getHash
(
key
);
...
@@ -160,7 +174,23 @@ public class CacheLongKeyLIRS<V> {
...
@@ -160,7 +174,23 @@ public class CacheLongKeyLIRS<V> {
*/
*/
public
V
put
(
long
key
,
V
value
,
int
memory
)
{
public
V
put
(
long
key
,
V
value
,
int
memory
)
{
int
hash
=
getHash
(
key
);
int
hash
=
getHash
(
key
);
return
getSegment
(
hash
).
put
(
key
,
hash
,
value
,
memory
);
int
segmentIndex
=
getSegmentIndex
(
hash
);
Segment
<
V
>
s
=
segments
[
segmentIndex
];
// check whether resize is required:
// synchronize on s, to avoid concurrent writes also
// resize (concurrent reads read from the old segment)
synchronized
(
s
)
{
if
(
s
.
isFull
())
{
// another thread might have resized
// (as we retrieved the segment before synchronizing on it)
s
=
segments
[
segmentIndex
];
if
(
s
.
isFull
())
{
s
=
new
Segment
<
V
>(
s
,
2
);
segments
[
segmentIndex
]
=
s
;
}
}
return
s
.
put
(
key
,
hash
,
value
,
memory
);
}
}
}
/**
/**
...
@@ -211,8 +241,11 @@ public class CacheLongKeyLIRS<V> {
...
@@ -211,8 +241,11 @@ public class CacheLongKeyLIRS<V> {
}
}
private
Segment
<
V
>
getSegment
(
int
hash
)
{
private
Segment
<
V
>
getSegment
(
int
hash
)
{
int
segmentIndex
=
(
hash
>>>
segmentShift
)
&
segmentMask
;
return
segments
[
getSegmentIndex
(
hash
)];
return
segments
[
segmentIndex
];
}
private
int
getSegmentIndex
(
int
hash
)
{
return
(
hash
>>>
segmentShift
)
&
segmentMask
;
}
}
/**
/**
...
@@ -276,11 +309,6 @@ public class CacheLongKeyLIRS<V> {
...
@@ -276,11 +309,6 @@ public class CacheLongKeyLIRS<V> {
averageMemory
>
0
,
averageMemory
>
0
,
"Average memory must be larger than 0, is {0}"
,
averageMemory
);
"Average memory must be larger than 0, is {0}"
,
averageMemory
);
this
.
averageMemory
=
averageMemory
;
this
.
averageMemory
=
averageMemory
;
if
(
segments
!=
null
)
{
for
(
Segment
<
V
>
s
:
segments
)
{
s
.
setAverageMemory
(
averageMemory
);
}
}
}
}
/**
/**
...
@@ -483,7 +511,7 @@ public class CacheLongKeyLIRS<V> {
...
@@ -483,7 +511,7 @@ public class CacheLongKeyLIRS<V> {
/**
/**
* The map array. The size is always a power of 2.
* The map array. The size is always a power of 2.
*/
*/
Entry
<
V
>[]
entries
;
final
Entry
<
V
>[]
entries
;
/**
/**
* The currently used memory.
* The currently used memory.
...
@@ -501,11 +529,6 @@ public class CacheLongKeyLIRS<V> {
...
@@ -501,11 +529,6 @@ public class CacheLongKeyLIRS<V> {
*/
*/
private
long
maxMemory
;
private
long
maxMemory
;
/**
* The average memory used by one entry.
*/
private
int
averageMemory
;
/**
/**
* The bit mask that is applied to the key hash code to get the index in
* The bit mask that is applied to the key hash code to get the index in
* the map array. The mask is the length of the array minus one.
* the map array. The mask is the length of the array minus one.
...
@@ -546,32 +569,17 @@ public class CacheLongKeyLIRS<V> {
...
@@ -546,32 +569,17 @@ public class CacheLongKeyLIRS<V> {
private
int
stackMoveCounter
;
private
int
stackMoveCounter
;
/**
/**
* Create a new cache.
* Create a new cache
segment
.
*
*
* @param maxMemory the maximum memory to use
* @param maxMemory the maximum memory to use
* @param
averageMemory the average memory usage of an object
* @param
len the number of hash table buckets (must be a power of 2)
* @param stackMoveDistance the number of other entries to be moved to
* @param stackMoveDistance the number of other entries to be moved to
* the top of the stack before moving an entry to the top
* the top of the stack before moving an entry to the top
*/
*/
Segment
(
long
maxMemory
,
int
averageMemory
,
int
stackMoveDistance
)
{
Segment
(
long
maxMemory
,
int
len
,
int
stackMoveDistance
)
{
setMaxMemory
(
maxMemory
);
setMaxMemory
(
maxMemory
);
setAverageMemory
(
averageMemory
);
this
.
stackMoveDistance
=
stackMoveDistance
;
this
.
stackMoveDistance
=
stackMoveDistance
;
clear
();
}
private
void
clear
()
{
// calculate the size of the map array
// assume a fill factor of at most 80%
long
maxLen
=
(
long
)
(
maxMemory
/
averageMemory
/
0.75
);
// the size needs to be a power of 2
long
l
=
8
;
while
(
l
<
maxLen
)
{
l
+=
l
;
}
// the array size is at most 2^31 elements
int
len
=
(
int
)
Math
.
min
(
1L
<<
31
,
l
);
// the bit mask has all bits set
// the bit mask has all bits set
mask
=
len
-
1
;
mask
=
len
-
1
;
...
@@ -583,8 +591,6 @@ public class CacheLongKeyLIRS<V> {
...
@@ -583,8 +591,6 @@ public class CacheLongKeyLIRS<V> {
queue2
=
new
Entry
<
V
>();
queue2
=
new
Entry
<
V
>();
queue2
.
queuePrev
=
queue2
.
queueNext
=
queue2
;
queue2
.
queuePrev
=
queue2
.
queueNext
=
queue2
;
// first set to null - avoiding out of memory
entries
=
null
;
@SuppressWarnings
(
"unchecked"
)
@SuppressWarnings
(
"unchecked"
)
Entry
<
V
>[]
e
=
new
Entry
[
len
];
Entry
<
V
>[]
e
=
new
Entry
[
len
];
entries
=
e
;
entries
=
e
;
...
@@ -593,6 +599,74 @@ public class CacheLongKeyLIRS<V> {
...
@@ -593,6 +599,74 @@ public class CacheLongKeyLIRS<V> {
usedMemory
=
0
;
usedMemory
=
0
;
stackSize
=
queueSize
=
queue2Size
=
0
;
stackSize
=
queueSize
=
queue2Size
=
0
;
}
}
/**
* Create a new, larger cache segment from an existing one.
* The caller must synchronize on the old segment, to avoid
* concurrent modifications.
*
* @param old the old segment
* @param resizeFactor the factor to use to calculate the number of hash
* table buckets (must be a power of 2)
*/
Segment
(
Segment
<
V
>
old
,
int
resizeFactor
)
{
this
(
old
.
maxMemory
,
old
.
entries
.
length
*
resizeFactor
,
old
.
stackMoveDistance
);
Entry
<
V
>
s
=
old
.
stack
.
stackPrev
;
while
(
s
!=
old
.
stack
)
{
Entry
<
V
>
e
=
copy
(
s
);
addToMap
(
e
);
addToStack
(
e
);
s
=
s
.
stackPrev
;
}
s
=
old
.
queue
.
queuePrev
;
while
(
s
!=
old
.
queue
)
{
Entry
<
V
>
e
=
find
(
s
.
key
,
getHash
(
s
.
key
));
if
(
e
==
null
)
{
e
=
copy
(
s
);
addToMap
(
e
);
}
addToQueue
(
queue
,
e
);
s
=
s
.
queuePrev
;
}
s
=
old
.
queue2
.
queuePrev
;
while
(
s
!=
old
.
queue2
)
{
Entry
<
V
>
e
=
find
(
s
.
key
,
getHash
(
s
.
key
));
if
(
e
==
null
)
{
e
=
copy
(
s
);
addToMap
(
e
);
}
addToQueue
(
queue2
,
e
);
s
=
s
.
queuePrev
;
}
}
private
void
addToMap
(
Entry
<
V
>
e
)
{
int
index
=
getHash
(
e
.
key
)
&
mask
;
e
.
mapNext
=
entries
[
index
];
entries
[
index
]
=
e
;
usedMemory
+=
e
.
memory
;
mapSize
++;
}
private
static
<
V
>
Entry
<
V
>
copy
(
Entry
<
V
>
old
)
{
Entry
<
V
>
e
=
new
Entry
<
V
>();
e
.
key
=
old
.
key
;
e
.
value
=
old
.
value
;
e
.
memory
=
old
.
memory
;
e
.
topMove
=
old
.
topMove
;
return
e
;
}
/**
* Check whether the cache segment is full.
*
* @return true if it contains more entries than hash table buckets.
*/
public
boolean
isFull
()
{
return
mapSize
>
mask
;
}
/**
/**
* Get the memory used for the given key.
* Get the memory used for the given key.
...
@@ -982,16 +1056,6 @@ public class CacheLongKeyLIRS<V> {
...
@@ -982,16 +1056,6 @@ public class CacheLongKeyLIRS<V> {
this
.
maxMemory
=
maxMemory
;
this
.
maxMemory
=
maxMemory
;
}
}
/**
* Set the average memory used per entry. It is used to calculate the
* length of the internal array.
*
* @param averageMemory the average memory used (1 or larger)
*/
void
setAverageMemory
(
int
averageMemory
)
{
this
.
averageMemory
=
averageMemory
;
}
}
}
/**
/**
...
@@ -1047,7 +1111,7 @@ public class CacheLongKeyLIRS<V> {
...
@@ -1047,7 +1111,7 @@ public class CacheLongKeyLIRS<V> {
Entry
<
V
>
queuePrev
;
Entry
<
V
>
queuePrev
;
/**
/**
* The next entry in the map
* The next entry in the map
(the chained entry).
*/
*/
Entry
<
V
>
mapNext
;
Entry
<
V
>
mapNext
;
...
...
h2/src/test/org/h2/test/store/TestCacheLongKeyLIRS.java
浏览文件 @
1bf1dde3
...
@@ -34,6 +34,7 @@ public class TestCacheLongKeyLIRS extends TestBase {
...
@@ -34,6 +34,7 @@ public class TestCacheLongKeyLIRS extends TestBase {
}
}
private
void
testCache
()
{
private
void
testCache
()
{
testResize
();
testRandomSmallCache
();
testRandomSmallCache
();
testEdgeCases
();
testEdgeCases
();
testSize
();
testSize
();
...
@@ -45,6 +46,21 @@ public class TestCacheLongKeyLIRS extends TestBase {
...
@@ -45,6 +46,21 @@ public class TestCacheLongKeyLIRS extends TestBase {
testScanResistance
();
testScanResistance
();
testRandomOperations
();
testRandomOperations
();
}
}
private
void
testResize
()
{
// cache with 100 memory, average memory 10
// (that means 10 entries)
CacheLongKeyLIRS
<
Integer
>
t1
=
new
CacheLongKeyLIRS
<
Integer
>(
100
,
10
,
1
,
0
);
// another cache with more entries
CacheLongKeyLIRS
<
Integer
>
t2
=
new
CacheLongKeyLIRS
<
Integer
>(
100
,
1
,
1
,
0
);
for
(
int
i
=
0
;
i
<
200
;
i
++)
{
t1
.
put
(
i
,
i
,
1
);
t2
.
put
(
i
,
i
,
1
);
}
assertEquals
(
toString
(
t2
),
toString
(
t1
));
}
private
static
void
testRandomSmallCache
()
{
private
static
void
testRandomSmallCache
()
{
Random
r
=
new
Random
(
1
);
Random
r
=
new
Random
(
1
);
...
...
编写
预览
Markdown
格式
0%
重试
或
添加新文件
添加附件
取消
您添加了
0
人
到此讨论。请谨慎行事。
请先完成此评论的编辑!
取消
请
注册
或者
登录
后发表评论