2010-09-07 9 views
13

Мне нужно написать программу сортировки в C, и было бы неплохо, если бы файл можно было отсортировать на месте, чтобы сохранить дисковое пространство. Данные ценны, поэтому мне нужно убедиться, что если процесс прерван (ctrl-c), файл не будет поврежден. Я могу гарантировать, что шнур питания на машине не будет дергаться.Прерывистый алгоритм сортировки на месте

Дополнительные детали: файл ~ 40GB, записи 128-бит, машина 64-бит, ОС POSIX

Любые намеки на совершение этого, или примечания в целом?

Спасибо!

Чтобы уточнить: Я ожидаю, что пользователь захочет выполнить ctrl-c процесс. В этом случае я хочу выйти изящно и обеспечить безопасность данных. Поэтому этот вопрос касается обработки прерываний и выбора алгоритма сортировки, который может быть быстро завершен, если потребуется.

Следующий (2 года спустя): Только для потомков я установил обработчик SIGINT, и он отлично работал. Это не защищает меня от сбоя питания, но это риск, с которым я могу справиться. Код в https://code.google.com/p/pawnsbfs/source/browse/trunk/hsort.c и https://code.google.com/p/pawnsbfs/source/browse/trunk/qsort.c

+0

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

+0

Чтобы быть в безопасности. Сделайте копию и отсортируйте копию. Я могу; t образ файловой системы будет иметь проблемы с другим 40GB –

+0

@Martin: ваше воображение не работает так, как мое. На моем основном диске я в настоящее время имею 36,4 ГБ бесплатно. –

ответ

5

Установите обработчик для SIGINT, который просто устанавливает флаг «process should exit soon».

В вашем роде проверьте флаг после каждого свопа двух записей (или после каждого N свопов). Если флаг установлен, выйдите из него.

+0

Это звучит как лучшее решение для меня. Некоторые из других рекомендаций дают мне впечатление, что Ctrl-C следует игнорировать, если он не нажат в нужное время, что кажется мне плохой работой. –

+0

Это похоже на победителя. Похоже, что это относится к быстрой сортировке и хипсорту. Благодаря! –

+0

Это не спасет ваши данные от kill -9 или сбоя питания. –

3

Используйте кучу сортировки и предотвратите прерывания (например, сигналы блока) во время каждой операции свопинга.

+0

Да. Простой и легкий. Также внесите изменения в память, а затем сразу напишите/промойте их (блокируйте сигналы для этой части). –

+0

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

+0

С другой стороны, если файл гигантский, как в вашем случае, доминирует большой-O, а не постоянный коэффициент от промахов io cache. Сорт кучи - единственный вид на месте, лучше квадратичного big-O. –

0

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

+0

-1 не соответствует критерию на месте вопроса, в котором четко указано, что существует огромный набор данных и, возможно, нет места для его резервного копирования. –

+0

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

12

Джерри прав, если это просто Ctrl-C, о котором вы беспокоитесь, вы можете игнорировать SIGINT за периоды за раз. Если вы хотите быть доказательством против смерти процесса в целом, вам нужен какой-то минимальный журнал. Чтобы поменять два элемента:

1) Добавьте запись в структуру управления в конце файла или в отдельный файл, указав, какие два элемента файла вы собираетесь обменять, A и B.

2) Скопируйте A на место царапины, запишите, что вы сделали это, заподлицо.

3) Копия B над A, а затем записать в царапанию пространстве, что вы сделали это, флеш

4) Копия с нуля пространства над В.

5) Удалить запись.

Это O (1) дополнительное пространство для всех практических целей, поэтому по большинству определений оно по-прежнему считается как место. Теоретически запись индекса равна O (log n), если n может быть сколь угодно большим: на самом деле это очень малый log n, а разумное время работы оборудования/времени ограничивает его выше на 64.

Во всех случаях, когда я говорю " flush ", я имею в виду фиксацию изменений« достаточно далеко ». Иногда ваша базовая операция флеша только очищает буферы внутри процесса, но фактически не синхронизирует физический носитель, поскольку он не очищает буферы полностью через драйверы/аппаратные уровни OS/device. Этого достаточно, когда все, о чем вы беспокоитесь, это смерть процесса, но если вы беспокоитесь о внезапных демонтажах СМИ, вам придется скрыться за водителем. Если вас беспокоит отказ от питания, вам придется синхронизировать аппаратное обеспечение, но это не так. С ИБП или если вы считаете, что отключения питания настолько редки, вы не против потери данных, это нормально.

При запуске проверьте место царапины для любых записей «swap-in-progress».Если вы найдете его, выясните, как далеко вы добрались, и завершите обмен оттуда, чтобы вернуть данные в состояние звука. Затем снова начните сортировку.

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

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

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

+0

Я бы подумал об использовании файла с отображением памяти для пространства царапин. Это должно дать вам лучшее из обоих миров. 1) Низкие накладные расходы для доступа, поскольку они кэшированы и не работают с API-интерфейсами. 2) Если процесс замирает, ОС должна по-прежнему сбрасывать измененную память на диск независимо от того, как процесс умирает. – torak

+0

@torak: Отличная новость, я не знал, что POSIX предоставил гарантию на mmap. Я все еще беспокоюсь о том, что доступ к файлу может быть переупорядочен, хотя это делает его ненадежным для восстановления. Таким образом, все указатели, возможно, должны быть «volatile» или что-то еще. –

+0

Вам нужно будет использовать 'msync()' с 'MS_SYNC' в каждой точке смыва. 'msync()' также должен подразумевать барьер компилятора, поэтому 'volatile' не требуется. – caf

5

Часть для защиты от ctrl-c довольно проста: signal(SIGINT, SIG_IGN);.

Что касается сортировки, то сортировка слияния обычно хорошо подходит для внешней сортировки. Основная идея состоит в том, чтобы прочитать как можно больше записей в памяти, отсортировать их, а затем записать их обратно на диск. Самым простым способом справиться с этим является запись каждого прогона в отдельный файл на диске. Затем вы объедините их обратно - прочитайте первую запись из каждого запуска в память и напишите наименьшее из них в исходный файл; прочитайте еще одну запись из прогона, которая предоставила эту запись, и повторите до конца. Последний этап - это единственный раз, когда вы изменяете исходный файл, так что это единственный раз, когда вам действительно нужно заверить от перерывов и т. Д.

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

+1

+1 - не соответствует потребностям на месте, но это ИМХО - самый надежный подход. Не меняет мастер-файл во время сортировки фрагментов, и если сбой процесса слияния, у вас есть отсортированные chunk-файлы в качестве резервной копии. – snemarch

+1

Вместо игнорирования 'SIGINT' вы должны ** блокировать ** его (и все другие сигналы) во время операций свопинга, но периодически разблокировать его, чтобы обрабатывать ожидающие сигналы, которые были получены при блокировке. –

-1

Предполагая, что 64-разрядная ОС (вы сказали, что это 64-битная машина, но все еще может работать с 32-разрядной ОС), вы можете использовать mmap для сопоставления файла с массивом, а затем использовать qsort для массива.

Добавить обработчик для SIGINT для вызова msync и munmap, чтобы приложение могло реагировать на Ctrl-C без потери данных.

+1

-1 для неправильного ответа. Если сигнал прерывает 'qsort', данные находятся в неопределенном состоянии, пока не закончится' qsort'. Нет абсолютно никакого способа использовать 'qsort' системы для удовлетворения потребностей OP. –