JAVA - 大量调用后构造函数中的数组复制意外变慢
JAVA - Array copy in constructor unexpectedly slow after large number of calls
我目前正在尝试提高 Java 代码的性能。在深入了解需要优化的地方后,我得到了以下设置(为清楚起见进行了简化)。
被调用次数多(~200k 到 2M)的 Board 构造函数:
public Board(Board board) {
long now = System.currentTimeMillis();
this.macroBoard = new int[9];
int [] boardToCopy = board.getMacroBoard();
for (int i = 0; i < 9; i++){
this.macroBoard[i] = boardToCopy[i];
}
long duration = System.currentTimeMillis() - now;
if (duration > THRESHOLD){
System.err.println(duration);
}
}
在另一个 class:
long end = System.currentTimeMillis() + SIMULATION_DURATION;
while (System.currentTimeMillis() < end) {
...
...
Board board = new Board(otherBoard);
...
...
}
结果让我很困惑。事实上我观察到两件事:
- 越大SIMULATION_DURATION,越大max(duration);
- 当SIMULATION_DURATION = 10s时,max(duration)的值可以达到2s(是秒,没有错字)。如果 SIMULATION_DURATION = 100ms,我观察到 max(duration) 大约为 30ms。
我的问题如下:
- 一个 9 整数数组的副本怎么会花这么长时间?
- 为什么 duration 在 99% 的时间里小于 0.1ms 而剩下的 1% 时间真的很高?
- 为什么取决于SIMULATION_DURATION的值?
- 我对这种基准使用 System.currentTimeMillis() 是否犯了错误,因此结果完全不准确?
- 当我创建大量 Board 对象时,GC 是否参与了这种奇怪的行为?
听起来您的 VM 运行内存不足并且正在尝试 GC 以便它可以为新数组分配内存。您可以在此 link 找到信息以启用 GC 日志记录并获取有关我们 VM 的 GCing 行为的更多详细信息:https://dzone.com/articles/enabling-and-analysing-the-garbage-collection-log
此外,我建议使用 System.nanoTime()
来衡量性能。更多详情:System.currentTimeMillis vs System.nanoTime
直接回答问题:
为什么复制一个 9 整数数组需要这么长时间?
当然不应该。检查 GC 日志以确认 GC 正在减慢 VM。
为什么 99% 的时间持续时间小于 0.1 毫秒而其余 1% 的时间非常高?
在 99% 的时间里,您没有 运行 内存不足,因此为新的 Board
对象分配 space 没有问题。
为什么要依赖SIMULATION_DURATION的值?
SIMULATION_DURATION
的值直接控制Board
对象的数量。
我对这种基准使用 System.currentTimeMillis() 是否犯了错误,因此结果完全不准确?
检查link上面的其他堆栈溢出问题。
在我创建大量 Board 对象时,GC 是否参与了这种奇怪的行为?
检查上面的答案。
pranavmalhotra 的回答比我的更有价值,但是有一个改进当然应该做。
为了
this.macroBoard = new int[9];
int[] boardToCopy = board.getMacroBoard();
for (int i = 0; i < 9; i++){
this.macroBoard[i] = boardToCopy[i];
}
第一个优化是
this.macroBoard = new int[9];
int[] boardToCopy = board.getMacroBoard();
System.arraycopy(boardToCopy, 0, macroBoard, 0, 9);
甚至:
int[] boardToCopy = board.getMacroBoard();
this.macroBoard = Arrays.copyOf(boardToCopy, 9);
优化可以采取多种形式。如果棋盘整数的范围是 0 .. 127,一个
可以将每 7 位 int 放入一个 long,因为 7 * 9 = 63 < 64 位的 long。
long
是原始类型。
我目前正在尝试提高 Java 代码的性能。在深入了解需要优化的地方后,我得到了以下设置(为清楚起见进行了简化)。
被调用次数多(~200k 到 2M)的 Board 构造函数:
public Board(Board board) {
long now = System.currentTimeMillis();
this.macroBoard = new int[9];
int [] boardToCopy = board.getMacroBoard();
for (int i = 0; i < 9; i++){
this.macroBoard[i] = boardToCopy[i];
}
long duration = System.currentTimeMillis() - now;
if (duration > THRESHOLD){
System.err.println(duration);
}
}
在另一个 class:
long end = System.currentTimeMillis() + SIMULATION_DURATION;
while (System.currentTimeMillis() < end) {
...
...
Board board = new Board(otherBoard);
...
...
}
结果让我很困惑。事实上我观察到两件事:
- 越大SIMULATION_DURATION,越大max(duration);
- 当SIMULATION_DURATION = 10s时,max(duration)的值可以达到2s(是秒,没有错字)。如果 SIMULATION_DURATION = 100ms,我观察到 max(duration) 大约为 30ms。
我的问题如下:
- 一个 9 整数数组的副本怎么会花这么长时间?
- 为什么 duration 在 99% 的时间里小于 0.1ms 而剩下的 1% 时间真的很高?
- 为什么取决于SIMULATION_DURATION的值?
- 我对这种基准使用 System.currentTimeMillis() 是否犯了错误,因此结果完全不准确?
- 当我创建大量 Board 对象时,GC 是否参与了这种奇怪的行为?
听起来您的 VM 运行内存不足并且正在尝试 GC 以便它可以为新数组分配内存。您可以在此 link 找到信息以启用 GC 日志记录并获取有关我们 VM 的 GCing 行为的更多详细信息:https://dzone.com/articles/enabling-and-analysing-the-garbage-collection-log
此外,我建议使用 System.nanoTime()
来衡量性能。更多详情:System.currentTimeMillis vs System.nanoTime
直接回答问题:
为什么复制一个 9 整数数组需要这么长时间?
当然不应该。检查 GC 日志以确认 GC 正在减慢 VM。
为什么 99% 的时间持续时间小于 0.1 毫秒而其余 1% 的时间非常高?
在 99% 的时间里,您没有 运行 内存不足,因此为新的 Board
对象分配 space 没有问题。
为什么要依赖SIMULATION_DURATION的值?
SIMULATION_DURATION
的值直接控制Board
对象的数量。
我对这种基准使用 System.currentTimeMillis() 是否犯了错误,因此结果完全不准确?
检查link上面的其他堆栈溢出问题。
在我创建大量 Board 对象时,GC 是否参与了这种奇怪的行为?
检查上面的答案。
pranavmalhotra 的回答比我的更有价值,但是有一个改进当然应该做。
为了
this.macroBoard = new int[9];
int[] boardToCopy = board.getMacroBoard();
for (int i = 0; i < 9; i++){
this.macroBoard[i] = boardToCopy[i];
}
第一个优化是
this.macroBoard = new int[9];
int[] boardToCopy = board.getMacroBoard();
System.arraycopy(boardToCopy, 0, macroBoard, 0, 9);
甚至:
int[] boardToCopy = board.getMacroBoard();
this.macroBoard = Arrays.copyOf(boardToCopy, 9);
优化可以采取多种形式。如果棋盘整数的范围是 0 .. 127,一个
可以将每 7 位 int 放入一个 long,因为 7 * 9 = 63 < 64 位的 long。
long
是原始类型。