Какие-нибудь подсказки кода для ускорения случайного чтения из Java FileChannel? - PullRequest
3 голосов
/ 22 декабря 2009

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

Я создаю 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, которое я могу сделать, чтобы ускорить чтение? Я понимаю, что могу сменить аппаратное обеспечение, но прежде чем сделать это, я хочу убедиться, что я выжал все до последней капли программного сока из проблемы.

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

Ответы [ 5 ]

4 голосов
/ 22 декабря 2009

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

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

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

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

4 голосов
/ 22 декабря 2009

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

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

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

1 голос
/ 22 декабря 2009

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

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

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

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

Может быть Эта статья поможет вам ...

1 голос
/ 22 декабря 2009

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

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

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

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

...