这是一个我无法绕过的结果,尽管广泛阅读了JDK源代码并检查了内在例程。
我正在测试使用ByteBuffer
清除使用allocateDirect
分配的ByteBuffer.putLong(int index, long value)
。基于JDK代码,如果缓冲区处于“本机字节顺序”或字节交换,则会导致单个8字节写入,如果不是,则会导致相同的写入。
所以我希望原生字节顺序(对我来说是小端)至少和非原生一样快。然而,事实证明,非本地人的速度要快2倍。
这是我在Caliper 0.5x中的基准:
...
public class ByteBufferBench extends SimpleBenchmark {
private static final int SIZE = 2048;
enum Endian {
DEFAULT,
SMALL,
BIG
}
@Param Endian endian;
private ByteBuffer bufferMember;
@Override
protected void setUp() throws Exception {
super.setUp();
bufferMember = ByteBuffer.allocateDirect(SIZE);
bufferMember.order(endian == Endian.DEFAULT ? bufferMember.order() :
(endian == Endian.SMALL ? ByteOrder.LITTLE_ENDIAN : ByteOrder.BIG_ENDIAN));
}
public int timeClearLong(int reps) {
ByteBuffer buffer = bufferMember;
while (reps-- > 0) {
for (int i=0; i < SIZE / LONG_BYTES; i+= LONG_BYTES) {
buffer.putLong(i, reps);
}
}
return 0;
}
public static void main(String[] args) {
Runner.main(ByteBufferBench.class,args);
}
}
结果是:
benchmark type endian ns linear runtime
ClearLong DIRECT DEFAULT 64.8 =
ClearLong DIRECT SMALL 118.6 ==
ClearLong DIRECT BIG 64.8 =
这是一致的。如果我将putLong
换成putFloat
,那么本机订单的速度大约快4倍。如果你看看putLong
是如何工作的,它在非原生的情况下做了更多的工作:
private ByteBuffer putLong(long a, long x) {
if (unaligned) {
long y = (x);
unsafe.putLong(a, (nativeByteOrder ? y : Bits.swap(y)));
} else {
Bits.putLong(a, x, bigEndian);
}
return this;
}
请注意,unaligned
在任何一种情况下均为真。本机和非本机字节顺序之间的唯一区别是Bits.swap
,它有利于本机情况(little-endian)。
答案 0 :(得分:4)
总结机械同情邮件列表中的讨论:
1. OP描述的异常在我的设置(JDK7u40 / Ubuntu13.04 / i7)上无法重现,导致所有情况下堆和直接缓冲区的性能保持一致,直接缓冲提供了巨大的性能优势:
BYTE_ARRAY DEFAULT 211.1 ==============================
BYTE_ARRAY SMALL 199.8 ============================
BYTE_ARRAY BIG 210.5 =============================
DIRECT DEFAULT 33.8 ====
DIRECT SMALL 33.5 ====
DIRECT BIG 33.7 ====
Bits.swap(y)方法被固有地嵌入到单个指令中,因此不能/不应该真正解释差异/开销的大部分。
2.上述结果(即与OP经验相矛盾)由一个天真的手工基准和另一个参与者编写的JMH基准独立证实。
这让我相信你要么遇到一些本地问题,要么遇到某种基准框架问题。如果其他人可以运行实验并看看它们是否可以重现您的结果,那将是很有价值的。
答案 1 :(得分:0)
即使在小端系统上,默认也是大端。你能尝试ByteOrder.nativeOrder(),因为这对你来说应该更快。
直接ByteBuffers对IO更快,因为必须将堆缓冲区复制到直接缓冲区或从直接缓冲区复制。
顺便说一句,您可能希望将此与直接使用Unsafe进行比较,因为这会检查边界是否有差异。