Что происходит за занавесом во время дискового ввода-вывода?

Когда я пытаюсь найти какую-то позицию в файле и записать небольшой объем данных (20 байт), что происходит за кулисами?

Мое понимание

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

Это то, что я ожидаю от небуферизованного ввода-вывода. Я также ожидаю, что буферизованный ввод / вывод будет делать примерно то же самое, но будьте умны в отношении своего кеша. Таким образом, я бы подумал, что если я выбрасываю локальность из окна, выполняя произвольные операции поиска и записи, буферизованный и небуферизованный ввод-вывод должен иметь одинаковую производительность ... может быть, небуферизованный получится немного лучше.

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

Мое заявление

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

Тест перед внедрением

Чтобы проверить, что я неМне не нужно придумывать какое-то блестяще изощренное решение, я провел тест с использованием нескольких миллионов случайных записей, записанных в файл, который может содержать в общей сложности 200 000 записей. Каждый тест запускает генератор случайных чисел с одинаковым значением, чтобы быть справедливым. Сначала я стираю файл и дополняю его до общей длины (около 7,6 мегабайт), затем зацикливаюсь несколько миллионов раз, передавая случайное смещение файла и некоторые данные одной из двух тестовых функций:

void WriteOldSchool( void *context, long offset, Data *data )
{
    int fd = (int)context;
    lseek( fd, offset, SEEK_SET );
    write( fd, (void*)data, sizeof(Data) );
}

void WriteStandard( void *context, long offset, Data *data )
{
    FILE *fp = (FILE*)context;
    fseek( fp, offset, SEEK_SET );
    fwrite( (void*)data, sizeof(Data), 1, fp );
    fflush(fp);
}

Может быть, никаких сюрпризов?

OldSchool Метод вышел на первое место - очень много. Это было более чем в 6 раз быстрее (1,48 миллиона против 232000 записей в секунду). Чтобы убедиться, что я неНе обращая внимания на аппаратное кэширование, я увеличил размер своей базы данных до 20 миллионов записей (размер файла 763 мегабайта) и получил те же результаты.

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

И что'происходит?

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

Кроме того (и я нене знаю деталей аппаратного кэширования на диске), если буферизованный ввод / вывод пытается записать группу секторов, когда я изменяю только один, это снизит эффективность аппаратного кэша.

Есть ли эксперты по диску, которые могут прокомментировать и объяснить это лучше, чем мои экспериментальные результаты? знак равно

Ответы на вопрос(2)

Ваш ответ на вопрос