我在Java中实现了一个GapBuffer列表,但我无法弄清楚为什么它会有如此大的性能损失。用C#编写的类似代码表现正常:将元素插入到列表中部比C#的List实现要快得多。但是Java版本的表现非常奇怪。
以下是一些基准测试信息:
Adding/removing 10,000,000 items @ the end of the dynamic array...
ArrayList: 683 milliseconds
GapBufferList: 416 milliseconds
Adding/removing 100,000 items @ a random spot in the dynamic array...
- ArrayList add: 721 milliseconds
- ArrayList remove: 612 milliseconds
ArrayList: 1333 milliseconds
- GapBufferList add: 1293 milliseconds
- GapBufferList remove: 2775 milliseconds
GapBufferList: 4068 milliseconds
Adding/removing 100,000 items @ the beginning of the dynamic array...
ArrayList: 2422 milliseconds
GapBufferList: 13 milliseconds
Clearly, the GapBufferList is the better option.
你可以看到,在列表的开头插入时,间隙缓冲区表现如预期的那样:它比ArrayList好得多,好得多,好得多。但是,在列表的任意位置插入和删除时,间隙缓冲区会有奇怪的性能惩罚,我无法解释。更奇怪的是,从GapBufferList中删除项目比添加项目要慢 - 根据我目前运行的每个测试,删除一个项目需要花费大约三倍的时间,尽管它们的代码几乎相同:
@Override
public void add(int index, T t)
{
if (index < 0 || index > back.length - gapSize) throw new IndexOutOfBoundsException();
if (gapPos > index)
{
int diff = gapPos - index;
for (int q = 1; q <= diff; q++)
back[gapPos - q + gapSize] = back[gapPos - q];
}
else if (index > gapPos)
{
int diff = gapPos - index;
for (int q = 0; q < diff; q++)
back[gapPos + q] = back[gapPos + gapSize + q];
}
gapPos = index;
if (gapSize == 0) increaseSize();
back[gapPos++] = t; gapSize--;
}
@Override
public T remove(int index)
{
if (index < 0 || index >= back.length - gapSize) throw new IndexOutOfBoundsException();
if (gapPos > index + 1)
{
int diff = gapPos - (index + 1);
for (int q = 1; q <= diff; q++)
back[gapPos - q + gapSize] = back[gapPos - q];
}
else
{
int diff = (index + 1) - gapPos;
for (int q = 0; q < diff; q++)
back[gapPos + q] = back[gapPos + gapSize + q];
}
gapSize++;
return back[gapPos = index];
}
缓冲区代码可以在这里找到,基准入口的代码可以在这里找到。(您可以将任何对Flow.***Exception
的引用替换为RuntimeException
。)
remove
函数为什么比add
函数慢三倍。你知道是为什么吗? - leviathanbadgerremove
与add
之间的奇怪惩罚就消失了。我仍然想知道为什么一开始会有这样的惩罚,但现在问题已经解决了。感谢您的回答! - leviathanbadger