Многопоточность Java чтение одного большого файла

Каков эффективный способ для многопоточного приложения Java, где многим потокам нужно прочитать один и тот же файл (размером> 1 ГБ) и представить его в качестве входного потока? Я заметил, что если есть много потоков (> 32), система начинает бороться за ввод-вывод и имеет много ожиданий ввода-вывода.

Я рассмотрел загрузку файла в байтовый массив, который используется всеми потоками - каждый поток создал бы ByteArrayInputStream, но выделение массива байтов в 1 Гб просто не будет работать хорошо.

Я также рассмотрел возможность использования одного FileChannel и каждого потока, создающего поверх него InputStream с помощью Channels.newInputStream (), однако, похоже, что FileChannel поддерживает состояние InputStream.

java,concurrency,io,

7

Ответов: 4


10

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

Вам правда нужны 32 темы? Предположительно у вас не так много ядер - так что используйте меньше потоков, и вы получите меньше переключения контекста и т. Д.

Все ваши потоки обрабатывают файл от начала до конца? Если да, не могли бы вы эффективно разделить файл на куски? Считайте первые (скажем) 10 МБ данных в память, пусть все потоки обрабатывают их, затем переходят к следующим 10 МБ и т. Д.

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


Вы можете открыть файл несколько раз в режиме только для чтения. Вы можете получить доступ к файлу любым удобным вам способом. Просто оставьте кеширование на ОС. Когда он слишком медленный, вы можете рассмотреть какое-то кеширование на основе чанков, когда все потоки могут обращаться к одному и тому же кешу.


Несколько идей:

  1. Напишите пользовательскую реализацию InputStream, которая действует как представление для FileChannel. Запишите это так, чтобы оно не зависело ни от какого состояния в FileChannel. (то есть: каждый экземпляр должен отслеживать свою собственную позицию, а чтение должно использовать абсолютные чтения в базовом FileChannel.) Это, по крайней мере, поможет вам справиться с проблемой, возникшей у вас с Channels.newInputStream (), но может не решить проблемы с конфликтами ввода-вывода. ,

  2. Напишите пользовательскую реализацию InputStream, которая действует как представление для MappedByteBuffer. Отображение памяти не должно быть таким же плохим, как фактическое считывание всего объекта в память за раз, но вы все равно съедите 1 ГБ виртуального адресного пространства.

  3. То же, что № 1, но есть некоторый общий уровень кэширования. Я бы не попробовал это, если бы 1 оказался недостаточно эффективным, а 2 - неосуществимым. На самом деле, операционная система уже должна была выполнять кеширование для вас в # 1, поэтому здесь вы, по сути, пытаетесь быть умнее, чем кеширование в файловой системе ОС.


Это очень большой файл. Можете ли вы получить файл в виде меньшего набора файлов? Просто доставка этого файла будет большой работой даже в корпоративной сети.

Иногда процесс легче изменить, чем программу.

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

Java, параллелизм, ИО,
Похожие вопросы