Любые советы по коду для ускорения случайного чтения из Java FileChannel?

У меня есть большой (3Gb) двоичный файл двойников, к которому я обращаюсь (более или менее) случайным образом во время итеративного алгоритма, который я написал для кластеризации данных. Каждая итерация делает около полумиллиона чтений из файла и около 100 тысяч записей новых значений.

Я создаю FileChannel следующим образом...

f = new File(_filename);
_ioFile = new RandomAccessFile(f, "rw");
_ioFile.setLength(_extent * BLOCK_SIZE);
_ioChannel = _ioFile.getChannel();

Затем я использую частный ByteBuffer размером с двойной для чтения из него

private ByteBuffer _double_bb = ByteBuffer.allocate(8);

и мой код чтения выглядит так

public double GetValue(long lRow, long lCol) 
{
    long idx = TriangularMatrix.CalcIndex(lRow, lCol);
    long position = idx * BLOCK_SIZE;
    double d = 0;
    try 
    {
        _double_bb.position(0);
        _ioChannel.read(_double_bb, position);
        d = _double_bb.getDouble(0);
    } 

    ...snip...

    return d;
}

а я вот так пишу...

public void SetValue(long lRow, long lCol, double d) 
{
    long idx = TriangularMatrix.CalcIndex(lRow, lCol);
    long offset = idx * BLOCK_SIZE;
    try 
    {
        _double_bb.putDouble(0, d);
        _double_bb.position(0);
        _ioChannel.write(_double_bb, offset);
    } 

    ...snip...

}

Время, затрачиваемое на итерацию моего кода, увеличивается примерно линейно с количеством чтений. Я добавил ряд оптимизаций в окружающий код, чтобы свести к минимуму количество чтений, но я остановился на базовом наборе, который считаю необходимым, без фундаментального изменения работы алгоритма, которого я хочу избежать в данный момент.

Итак, мой вопрос: есть ли что-нибудь в коде чтения/записи или конфигурации JVM, которое я могу сделать для ускорения чтения? Я понимаю, что могу поменять аппаратное обеспечение, но прежде чем я это сделаю, я хочу убедиться, что выжал из проблемы все до последней капли программного сока.

заранее спасибо


person Simon    schedule 22.12.2009    source источник


Ответы (5)


Пока ваш файл хранится на обычном жестком диске, вы получите максимально возможное ускорение, организовав свои данные таким образом, чтобы обеспечить локальность доступа, т. е. вызывает как можно больше вызовов get/set подряд для доступа к одному и тому же область файла.

Это более важно, чем все остальное, что вы можете сделать, потому что доступ к случайным местам на жестком диске — безусловно, самая медленная вещь, которую делает современный ПК — это занимает примерно в 10 000 раз больше времени, чем что-либо еще.

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

В качестве альтернативы, избегайте этой проблемы, сохраняя файл на SSD или (лучше) в оперативной памяти. Даже хранение его на простом флэш-накопителе может быть большим улучшением.

person Michael Borgwardt    schedule 22.12.2009

Вместо чтения в ByteBuffer я бы использовал сопоставление файлов, см.: FileChannel.map().

Кроме того, вы толком не объясняете, как ваши GetValue(row, col) и SetValue(row, col) получают доступ к хранилищу. Являются ли row и col более или менее случайными? Идея, которую я имею в виду, следующая: иногда для обработки изображений, когда вам нужно получить доступ к пикселям, таким как row + 1, row - 1, col - 1, col + 1, к средним значениям; трюк состоит в том, чтобы организовать данные в блоках 8 x 8 или 16 x 16. Это помогает сохранить различные интересующие пиксели в непрерывной области памяти (и, надеюсь, в кеше).

Вы можете применить эту идею к своему алгоритму (если это применимо): вы сопоставляете часть своего файла один раз, чтобы разные вызовы GetValue(row, col) и SetValue(row, col) работали с этой частью, которая только что была сопоставлена.

person Gregory Pakosz    schedule 22.12.2009
comment
Идея мне нравится, надо немного поковыряться, чтобы посмотреть, как она подойдет. На самом деле файл представляет собой нижний левый треугольник очень большого квадрата чисел, в котором (строка, столбец) и (столбец, строка) имеют одинаковые значения. Первоначально у меня все это было в памяти в виде одномерного массива двойников, и я индексировал их с помощью некоторой арифметики, которая позволяет мне получать их случайным образом, не беспокоясь о том, ставлю ли я сначала строку или столбец. Я пытался получить к ним доступ последовательно, но мне нравится ваша идея маленьких мета-прямоугольников. В области кода, где я чаще всего читаю, порядок не важен, так что это может сработать. - person Simon; 22.12.2009

Предположительно, если мы сможем уменьшить количество чтений, то все пойдет быстрее.

3Gb не огромно для 64-битной JVM, поэтому довольно много файла поместится в памяти.

Предположим, что вы рассматриваете файл как «страницы», которые вы кэшируете. Когда вы читаете значение, читайте страницу вокруг него и сохраняйте его в памяти. Затем, когда вы делаете больше чтений, сначала проверьте кеш.

Или, если у вас есть возможность, прочитайте все это в память в начале обработки.

person djna    schedule 22.12.2009
comment
Боюсь, только 32-битная, что является ограничением, наложенным на меня клиентами. - person Simon; 22.12.2009

  1. Побайтовый доступ всегда приводит к низкой производительности (не только в Java). Попробуйте читать/записывать большие блоки (например, строки или столбцы).

  2. Как насчет перехода на движок базы данных для обработки таких объемов данных? Он будет обрабатывать все оптимизации для вас.

Возможно, Эта статья поможет вам...

person ThinkJet    schedule 22.12.2009

Возможно, вы захотите рассмотреть возможность использования библиотеки, предназначенной для управления большими объемами данных и случайного чтения, а не для использования процедур доступа к необработанным файлам.

Формат файла HDF может подойти. Он имеет Java API, но не является чистой Java. Он распространяется под лицензией Apache Style.

person Robert Christie    schedule 22.12.2009
comment
Выглядит интересно. Каков типичный вариант использования HDF? - person Joel; 22.12.2009
comment
Эта ссылка hdfgroup.org/why_hdf может оказаться полезной — это цель ссылки HDF выше. Согласно их веб-сайту, он используется, когда данные большие и сложные; нужен быстрый или случайный ввод-вывод и т. д. - person Robert Christie; 22.12.2009
comment
Оказывается, pytables использует это, и я использую pytables в других проектах. На самом деле я недавно обдумывал повторную реализацию всего этого в python, чтобы я мог использовать numpy, scipy и pytables. Дело крепчает. - person Simon; 22.12.2009
comment
Я также столкнулся с этим через PyTables - возможно, numpy и pytables лучше подходят для этого. - person Robert Christie; 22.12.2009