2013-10-09 6 views
3

Вот результат, который я не могу обернуть вокруг, несмотря на обширное чтение источника JDK и рассмотрение внутренних процедур.ByteBuffer.putLong ~ 2x быстрее с не-родным ByteOrder

Я тестирую очистку ByteBuffer, выделенную allocateDirect с использованием ByteBuffer.putLong(int index, long value). Основываясь на коде JDK, это приводит к одной записи в 8 байт, если буфер находится в «собственном байтовом порядке» или байтовой свопе, за которым следует то же самое, если это не так.

Так что я бы ожидал, что основной порядок байтов (маленький конец для меня) будет по крайней мере так же быстро, как и неродной. однако, как выясняется, неродные быстрее на 2x.

Вот мой тест в Суппорт 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

Какая у вас система? – Kayaman

+1

Вы используете только часть (около 1/8) буфера, поскольку 'putLong' ожидает смещение в байтах. Я не понимаю, почему доступ должен быть неравнозначным (когда вы исправляете байтовое и длинное смещение). Мои [результаты] (https://microbenchmarks.appspot.com/runs/0bd9f0ea-96d4-4cfd-97ce-105a3ccc9a1d) (созданные с помощью суппорта 1.0 beta) отличаются. – maaartinus

+0

@ Kayaman - Я нахожусь на Xeon W3580, но я не ожидаю, что он будет отличаться по архитектуре x86. – BeeOnRope

ответ

0

По умолчанию большой эндиан даже в маленьких системах. Можете ли вы попробовать ByteOrder.nativeOrder(), поскольку это должно быть быстрее для вас.

direct ByteBuffers быстрее для ввода-вывода, поскольку кучи-буферы должны быть скопированы в/из прямого буфера.

Кстати, вы могли бы сравнить это с использованием небезопасного напрямую, так как у этого есть проверка границ, чтобы увидеть, какая разница.

+0

Я понимаю. Мой комментарий заключался в том, что большой эндиан (ошибка для ByteBuffer) был быстрее в моей маленькой системе endian (на практике, 99% плакатов SO и меня). Это не имеет смысла, основываясь на моем чтении кода. – BeeOnRope

+0

Согласовано. Нужно попытаться воспроизвести, когда я вернусь домой. Некоторые из операций были одинаковыми. –

4

Подводя итоги обсуждения от механического списка симпатии рассылки:

1. аномалии описывается ОР не воспроизводит-состояние на моей установке (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 (у) метод получает характеристический-Fied в одну команду и поэтому не может/не должен на самом деле объясняют большую часть разницы/накладных расходов.

2. Вышеуказанный результат (т. Е. Противоречивый опыту ОП) был независимо подтвержден наивным показателем ручной прокатки и контрольным эталоном JMH, написанным другим участником.

Это заставляет меня поверить, что вы либо испытываете какую-то локальную проблему, либо какую-то проблему с базой бенчмаркинга. Было бы полезно, если бы другие могли провести эксперимент и посмотреть, смогут ли они воспроизвести ваш результат.