24 марта 2011 г.

IO performance

Гонял тесты производительности ввода-вывода. Обнаружил интересную деталь -- чтение файлов с использованием обычного streaming-io (Input/OutputStream) замедляется, по мере увеличения буфера чтения. У меня на сервере (linux, RAID5) чтение замедляется почти вдвое при увеличении буфера с 128Кб до 10Мб. Причем это достаточно достоверно заметно во всей серии экспериментов с разными размерами файлов.

А вот чтение с использованием NIO (channels) так же вполне достоверно ускоряется, с увеличением размера буфера (под размером буфера здесь я подразумеваю аргумент count в вызове
FileChannel.transferTo( position, count, target )
. В целом, для чтения NIO быстрее old-school IO примерно на 10-20%

C записью положение более "универсальное" -- максимум скорости в районе размера буфера 128-512Кб, независимо от способа. Разница в скорости IO streams/NIO channels практически отсутствует.

В итоге (для нашего сервера) можно считать, что оптимальное значение "для всех миров" лежит где-то в районе 64-512Кб.

P.S. Да, эти значения актуальны для больших файлов -- > 10Гб. Для мелких файлов планка оптимального размера буфера смещается вниз -- 8-32Кб

6 комментариев:

  1. Кстати в сетевом взаимодействии в OpenJDK в зависимости от размера буффера выбирается где его аллоцировать - на стеке или динамически

    SocketOutputStream.с

    #define MAX_BUFFER_LEN 2048
    #define MAX_HEAP_BUFFER_LEN 65536

    char *bufP;
    char BUF[MAX_BUFFER_LEN];
    int buflen;
    int fd;

    if (len <= MAX_BUFFER_LEN) {
    // use stack allocations for small buffers
    bufP = BUF;
    buflen = MAX_BUFFER_LEN;
    } else {
    // use heap allocations for small buffers
    buflen = min(MAX_HEAP_BUFFER_LEN, len);
    bufP = (char *)malloc((size_t)buflen);
    }

    Это виндовый код - в линуксе MAX_BUFFER_LEN побольше и зависит от 32/64 jdk

    ОтветитьУдалить
  2. Любопытно. То есть вы хотите сказать, что если посылать в сокет большие массивы, то будет добавляться стоимость аллокации/деаллокации?

    ОтветитьУдалить
  3. По крайней мере в OpenJDK это так - я правда совершенно не понимаю зачем они так сделали

    ОтветитьУдалить
  4. Пруфлинк:

    http://hg.openjdk.java.net/jdk7/jdk7/jdk/file/00cd9dc3c2b5/src/windows/native/java/net/SocketOutputStream.c

    ОтветитьУдалить
  5. Действительно. Странная реализация, я не понимаю смысла. Почему бы просто не отправлять кусками по 32К? Аллоцировать буфер в куче только для того, чтобы скопировать в него данные, и отдать в сискол?..

    ОтветитьУдалить
  6. FileChannel.transferTo работает через sendFile - то есть это в теории zero-copy операция (http://en.wikipedia.org/wiki/Zero-copy)

    У меня в тестах также вышло +20% производительности на NIO при отправке данных из памяти, что достаточно странно.
    http://bedrin.livejournal.com/204307.html

    Буду продолжать разбираться

    ОтветитьУдалить