Абсолютно быстрый способ хранения 32-разрядного целого числа на диске? - PullRequest
2 голосов
/ 27 января 2011

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

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

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

Число обычно не превышает 10 000 000, поэтому 40 МБ не так уж и плохо.

Какой-нибудь совет относительно того, как достичь минимальной задержки без ущерба для целостности?

C или C ++ в Linux 2.6 +

Ответы [ 8 ]

8 голосов
/ 27 января 2011

Я думаю, что самым быстрым / простым способом сделать это было бы использование mmap / msync - mmap 1 страницы файла в памяти и сохранение значения на этой странице.Каждый раз, когда значение изменяется, вызывайте msync (2), чтобы принудительно вернуть страницу на диск.Таким образом, вам нужен только один системный вызов для магазина

2 голосов
/ 27 января 2011

Измерение.

Насколько вы контролируете аппаратное обеспечение?Если что-то меньше, чем full , вы не получите никаких гарантий.

В Linux я бы, вероятно, попытался бы создать драйвер ядра, который делал бы свои записи с самым высоким приоритетом, возможно даже без использованияфайловая система.

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

И вывсегда можете попросить пользователя использовать флешку.

2 голосов
/ 27 января 2011

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

int len = sizeof(unsigned);
int fildes = open(...)
void* address = mmap(0, len, PROT_READ, MAP_PRIVATE, fildes, 0)
unsigned* mappedNumber = (unsigned*)(address);

* mappedNumber теперь может содержать ваше целое число.

1 голос
/ 27 января 2011

Почему вашему приложению вообще нужно ждать завершения записи?

Пишите свои данные асинхронно или, возможно, из другого потока.

У вас на самом деле мало низкого уровняконтроль над жестким диском.Пока вы пишете так мало данных за раз, вы будете подвергаться большому количеству дорогостоящих поисков.Но поскольку вы используете его только как «контрольные точки» для восстановления в случае сбоя, похоже, нет никаких причин, по которым запись не может выполняться асинхронно.

1 голос
/ 27 января 2011

Самый быстрый способ записать файл - отобразить этот файл в память и обработать его как массив символов.

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

Изменение размера этого файла требует ftruncate()/remap() последовательности, которая может занять слишком много времени, поэтому вы можете захотеть минимизировать изменение размера, увеличив размер файла с коэффициентом,как std::vector<> увеличивается в 1,5 раза по сравнению с push_back() при переполнении.В зависимости от ваших требований к пропускной способности и задержке может быть применена определенная оптимизация.

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

0 голосов
/ 08 февраля 2011

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

Я предлагаю вам рассмотреть возможность хранения целого числа где-то, кроме диска. Например:

  • запишите его в какой-нибудь nvram, которым вы управляете (например, во встроенной системе).(Если ваш RAID-контроллер имеет nvram для записи, он может сделать это для вас. Но если вы задаете этот вопрос, он, вероятно, не делает.)

  • записать его в свободные байтыв системной памяти CMOS (например, на оборудовании ПК).

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

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

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

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

0 голосов
/ 27 января 2011

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

Таким образом, самый быстрый способ - добавить 4 байта. Вы должны перезаписать 4 существующих байта. Желательно в секторе, который у вас уже есть в памяти. Другие уже отметили, что вы можете достичь этого с mmap/msync.

Очевидно, что с учетом текущих цен на SSD и разработчиков и вашего лимита в 40 МБ вы будете использовать SSD. Это окупается, если вы экономите час. Поэтому время поиска не имеет значения; У SSD нет физических головок.

0 голосов
/ 27 января 2011

Хранение целого занимает только один блок на диске, независимо от размера блока. Поэтому вам нужно синхронизировать один блок с диском, и это займет столько времени, сколько вам нужно, и вы ничего не сможете сделать, чтобы сделать его быстрее.

Что бы вы ни делали, fdatasync () будет убийцей с точки зрения времени. Он будет синхронизировать один блок с контроллером RAID (с батарейным питанием).

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

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

...