Быстрое чтение определенных байтов нескольких файлов в C/C++

Я искал в сети об этом вопросе и хотя существует много подобных вопросов о чтении-записи в C/C++, я не нашел об этой определенной задаче.

Я хочу смочь читать из нескольких файлов (256x256 файлы) только sizeof(double) байты, расположенные в определенном положении каждого файла. Прямо сейчас мое решение для каждого файла:

  1. Откройте файл (чтение, режим двоичного счета):

    fstream fTest("current_file", ios_base::out | ios_base::binary);

  2. Ищите положение, которое я хочу считать:

    fTest.seekg(position*sizeof(test_value), ios_base::beg);

  3. Считайте байты:

    fTest.read((char *) &(output[i][j]), sizeof(test_value));

  4. И близко файл:

    fTest.close();

Это сопровождает 350 ms работать внутри a for{ for {} } структура с 256x256 повторения (один для каждого файла).


Q: Вы думаете, что существует лучший способ реализовать эту операцию? Как Вы сделали бы это?

7
задан Alejandro Cámara 20 May 2010 в 14:20
поделиться

6 ответов

Если вы действительно хотите оптимизировать это, вы, вероятно, захотите отказаться от C++ fstream или, по крайней мере, отключить буферизацию для него. fstream делает много выделения и удаления памяти, и буферизация может прочитать больше данных, чем нужно. ОС, скорее всего, придется прочитать всю страницу, чтобы получить несколько нужных байт, но fstream, вероятно, захочет скопировать по крайней мере столько же (а может и больше, что потребует дополнительных чтений) в свои буферы, что займет время.

Теперь мы можем перейти к более крупным победам. Вероятно, вы хотите использовать подпрограммы ввода-вывода ОС напрямую. Если вы используете систему POSIX (например, Linux), то open, lseek, read и close будут хорошим первым шагом в этом направлении, и могут потребоваться, если у вас нет следующих системных вызовов.

Если все файлы, из которых вы пытаетесь читать, находятся в одном каталоге (папке) или под одним каталогом, то вы можете обнаружить, что открытие каталога с помощью opendir или open("имя_каталога", O_DIRECTORY) (в зависимости от того, нужно ли вам читать записи каталога самостоятельно), а затем вызов openat, который принимает в качестве одного из аргументов дескриптор файла записи каталога, ускорит открытие каждого файла, так как ОС не придется так много работать, чтобы каждый раз искать файл, который вы пытаетесь открыть (эти данные, вероятно, будут в кэше файловой системы ОС, но это все равно занимает время и имеет много тестов).

Тогда вы сможете считывать данные, используя системный вызов pread, без необходимости делать какие-либо поиски местоположения нужных вам данных. pread принимает смещение, а не использует представление ОС о текущей точке поиска. Это сэкономит вам как минимум один системный вызов.

edit

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

1
ответ дан 7 December 2019 в 12:16
поделиться

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

Ограничивающим фактором здесь является ввод-вывод, а не алгоритм.

0
ответ дан 7 December 2019 в 12:16
поделиться

Инвертировать порядок итераций. Или, по крайней мере, считывать целую страницу данных с диска (скажем, 4 кБ на файл) и хранить ее в памяти до следующего прохода. Тогда вам нужно будет обращаться к файловой системе только при каждом 512-м проходе. Это будет стоить 256 Мб оперативной памяти, но сэкономит сотни Гб файлового ввода-вывода (даже когда вы запрашиваете только 8 байт, диск должен передать в кэш всю страницу). А алгоритм замены дискового кэша в вашей ОС, скорее всего, удалит файлы, возраст которых составляет 65k вызовов для открытия, так что не доверяйте ему делать оптимизацию за вас.

0
ответ дан 7 December 2019 в 12:16
поделиться

Разве fstream API не включает буферизацию по умолчанию? Интересно, может ли переключение API на тот, который не использует буферизацию, или отключение буферизации с помощью setvbuf , может привести к ускорению. Операции с кешем базовой ОС вполне могут означать, что разницы нет, но было бы интересно узнать.

0
ответ дан 7 December 2019 в 12:16
поделиться

Возможно, многопоточность поможет.

Но сначала вы можете попробовать что-нибудь попроще. Сделайте две копии вашей программы, одна из которых читает первые 32768 файлов, а другая - вторую половину. Запустите обе программы одновременно. Это займет менее 14 часов?

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

Добавлено : 14 часов явно неверно, так как это почти 1 секунда на файл. В приведенном выше комментарии Алехандро говорится, что с твердотельным накопителем время составляет всего 0,1 мс на файл, всего 6,5 с. Что мне кажется быстрым.

Я предполагаю, что Алехандро должен повторить это примерно 7000 раз, каждый раз с другим фрагментом данных из 65536 файлов. Если это так, два дополнительных предложения:

  • Напишите программу, которая будет размещать файлы в новый файл. У тебя наверное достаточно на вашем SSD для этого, так как ваш Другой вопрос SO указывает на 32 ГБ данных, а SSD, вероятно, несколько раз это. Тогда каждый запуск использует только этот единственный огромный файл, который удаляет 65535 открытий и закрытий.

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

Дальнейшее дополнение : Вы, наверное, уже обсуждали это с вашей фразой «запись считанных данных в один файл».

2
ответ дан 7 December 2019 в 12:16
поделиться

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

Если вам нужно запускать программу несколько раз, а данные не меняются, возможно, вам стоит создать инструмент, который сначала оптимизирует данные.

Проблема производительности с файлами заключается в накладных расходах:

  1. {накладные расходы}Размещение жесткого диска.
  2. {накладные расходы}Определение местоположения файла.
  3. Позиционирование внутри файла.
  4. Чтение данных.
  5. {Закрытие файла очень мало добавляет к производительности.}

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

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

  • Поток 1 открывает файл.
  • Поток 2 открывает файл, пока поток 1 позиционируется.
  • Поток 3 открывает файл, пока поток 2 позиционируется, а поток 1 считывает данные.
  • Нить 4 открывает файл, нить 3 позиционирует, поток 2 читает, поток 1 закрывает.

Надеюсь, эти потоки могут поддерживать жесткий диск достаточно занятым, чтобы не замедляться; непрерывная активность. Вы можете попробовать это сначала в одном потоке. Если вам нужна более высокая производительность, вы можете рассмотреть возможность отправки команд непосредственно на диск (сначала упорядочьте их).

2
ответ дан 7 December 2019 в 12:16
поделиться