Как fseek() реализован в файловой системе?

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

Мне интересно, насколько эффективно вставлять данные в середину файла. Предположим, у меня есть файл с данными размером 1 МБ, а затем я вставляю что-то со смещением 512 КБ. Насколько это будет эффективно по сравнению с добавлением моих данных в конец файла? Просто чтобы сделать пример полным, допустим, я хочу вставить 16 КБ данных.

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


person pajton    schedule 13.03.2010    source источник
comment
Используя fseek() и др., вы просто не можете вставлять данные в середину файла, поэтому ваш вопрос спорный.   -  person    schedule 13.03.2010
comment
Вы можете просто не вставлять данные в середину файла (так же, как вы не можете удалить что-то из начала или середины файла). Лучшее, что вы можете сделать, это перезаписать данные в середине файла.   -  person dmeister    schedule 14.03.2010


Ответы (6)


(отказ от ответственности: я хочу просто добавить несколько намеков на это интересное обсуждение) ИМХО, есть некоторые вещи, которые следует учитывать:

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

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

3) Современные ОС имеют очень агрессивные алгоритмы кэширования. Файл "fseeked", скорее всего, уже присутствует в кеше, поэтому операции выполняются намного быстрее. Но они могут сильно ухудшиться, если общая активность файловой системы, создаваемая другими процессами, станет важной.

Любые комментарии?

person Giuseppe Guerrini    schedule 13.03.2010
comment
Конечно, вставка, как правило, дороже, чем добавление, потому что для вставки, по крайней мере, нужно также переместить предыдущее содержимое, т.е. добавить в конец файла! Но интересная часть вопроса Пайтона касается производительности операции fseek. Любой комментарий? - person Giuseppe Guerrini; 13.03.2010

В качестве примера возьмем ext2 FS и ОС Linux. Я не думаю, что между вставкой и добавлением будет значительная разница в производительности. В обоих случаях должны быть прочитаны узел файлов и таблица смещений, соответствующий сектор диска отображается в память, данные обновляются, а в какой-то момент данные записываются обратно на диск. Что будет иметь большое значение в производительности в этом примере, так это хорошая временная и пространственная локальность при доступе к частям файла, поскольку это уменьшит количество комбинаций загрузки/сохранения.

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

person PinkyNoBrain    schedule 13.03.2010

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

ОС, скорее всего, кэширует файл в своей памяти ядра, то есть прямое смещение к месту на диске, где хранятся 1 и 0, это через уровни ядра ОС, более чем вероятно, самый верхний уровень в ядре, который будет иметь моментальный снимок того, из чего состоит файл, т. е. данные независимо от того, что он содержит (в любом случае ему все равно, пока «указатели» на структуру диска для этого смещения информация на диске действительна!)...

Когда происходит fseek(..), было бы много накладных расходов, косвенно, ядро ​​делегировало задачу чтения с диска, в зависимости от того, насколько фрагментирован файл, теоретически это может быть «повсюду», что может быть значительные накладные расходы с точки зрения необходимости, с точки зрения пользователя, то есть кода C, выполняющего fseek(...), он может рассеиваться повсюду, чтобы собрать данные в «одно непрерывное представление данных» и отныне вставка в середину файла (помните, что на этом этапе ядру пришлось бы корректировать местоположение/смещения в фактической дисковой пластине для данных) будет считаться медленнее, чем добавление в конец файла.

Причина довольно проста, ядро ​​​​«знает», каким было последнее смещение, и просто стирает маркер EOF и вставляет больше данных, за кулисами ядро ​​​​выделяет еще один блок памяти для диска-буфера с скорректированное смещение к месту на диске после маркера EOF после завершения добавления данных.

person t0mm13b    schedule 13.03.2010
comment
Хороший совет, разброс данных может быть серьезной причиной плохой производительности. Спасибо! - person Giuseppe Guerrini; 13.03.2010

Одно наблюдение, которое я сделал по поводу fseek в Solaris, заключается в том, что каждый вызов его сбрасывает буфер чтения FILE. При следующем чтении всегда будет считываться полный блок (по умолчанию 8 КБ). Поэтому, если у вас много случайного доступа с небольшими операциями чтения, хорошей идеей будет сделать это небуферизованным (setvbuf с NULL буфером) или даже использовать прямые системные вызовы (lseek+read или даже лучше pread, что означает только 1 системный вызов вместо 2). Я полагаю, что это поведение будет похоже на другие ОС.

person Patrick Schlüter    schedule 29.08.2010

Эффективно вставлять данные в середину файла можно только в том случае, если размер данных кратен сектору ФС, но ОС не предоставляет таких функций, поэтому приходится использовать низкоуровневый интерфейс к драйверу ФС.

person Ha.    schedule 13.03.2010

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

person goedson    schedule 13.03.2010