3

尽管广泛阅读了 JDK 源代码并检查了内在例程,但我还是无法一概而论。

我正在测试清除 a ByteBuffer,分配给allocateDirectusing 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 =

这是一致的。如果我换成putLongputFloat本地订单的速度大约快 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有利于本机情况(小端)。

4

2 回答 2

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基准独立确认。

这使我相信您正在遇到一些本地问题或某种基准测试框架问题。如果其他人可以进行实验并查看他们是否可以重现您的结果,那将是很有价值的。

于 2013-10-11T13:52:09.223 回答
0

即使在小端系统上,默认值也是大端。你可以试试 ByteOrder.nativeOrder() 因为这对你来说应该更快。

对于 IO,直接 ByteBuffers 更快,因为必须将堆缓冲区复制到直接缓冲区/从直接缓冲区复制。

顺便说一句,您可能想将此与直接使用 Unsafe 进行比较,因为它确实具有边界检查以查看它有多大的不同。

于 2013-10-09T08:32:30.427 回答