Случайная запись файла

Если у меня есть несколько потоков, генерирующих блоки файла, как лучше всего записать блоки?

пример) 5 потоков, работающих с файлом из 500 блоков, блок 0 не обязательно завершается раньше блока 1, но выходной файл на диске должен быть в порядке. (блок 0, блок 1, блок 2, .... блок 499)

программа написана на C++, может ли fwrite() как-то "произвольно обращаться" к файлу? файл создается с нуля, то есть, когда блок 5 завершен, файл может все еще иметь размер 0, поскольку блоки 1–4 еще не завершены. Могу ли я напрямую записать блок 5? (с правильным fseek)

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

если fwrite может быть «случайным», то модуль записи вывода может просто брать выходные данные, искать и затем записывать. Однако не уверен, что этот дизайн может хорошо работать в больших масштабах.

Некоторые ограничения

  • Каждый блок имеет одинаковый размер, генерируется в памяти
  • размер блока известен заранее, но не общее количество блоков.
  • общий размер составляет несколько ГБ. Большой.
  • На одном сервере может выполняться несколько заданий. каждая работа описана выше. У них есть свои независимые генераторы/писатели, разностные процессы.
  • Сервер представляет собой машину Linux/CentOS.

person Solti    schedule 10.05.2016    source источник
comment
Блоки все одного размера?   -  person T33C    schedule 11.05.2016
comment
Почему бы просто не собрать файл в памяти и не распечатать его на диск после завершения?   -  person Baum mit Augen    schedule 11.05.2016
comment
fseek и вы найдете (хотя вы, вероятно, захотите накопить как минимум несколько килобайт последовательных данных, прежде чем выполнять поиск).   -  person Jerry Coffin    schedule 11.05.2016
comment
У вас должна быть возможность выполнить fseek + fwrite (вам, вероятно, нужно выполнить оба этих вызова атомарно, если вы хотите распараллелить их), но перед этим расширьте файл до нужного размера. Предполагается, что окончательный размер файла известен заранее. :)   -  person bialpio    schedule 11.05.2016
comment
Потенциально также хорошая идея (если блоки большие): пусть каждый поток пишет свой собственный файл, а затем использует скрипт для их объединения. Мы делаем это на производстве.   -  person Baum mit Augen    schedule 11.05.2016
comment
Да, каждый блок имеет одинаковый размер и известен заранее. Однако количество блоков заранее неизвестно.   -  person Solti    schedule 11.05.2016
comment
Размер файла составляет несколько ГБ, и на одном сервере может выполняться несколько запросов одновременно. Поэтому, вероятно, небезопасно сначала хранить блоки в памяти, а затем сбрасывать их.   -  person Solti    schedule 11.05.2016
comment
@Baum mit Augen, твоя идея интересна. вероятно, я хотел бы проверить и посмотреть, работает ли он лучше. единственное, мне нужно реализовать некоторую логику для объединения подфайлов, чтобы получить правильный порядок блоков. это выполнимо.   -  person Solti    schedule 11.05.2016
comment
Re: I need to implement some logic to merge the sub-files to get the right order of blocks - или ты? Не могли бы вы просто оставить их в нескольких файлах и вычислить имя файла вместо смещения? Также устраняет проблему с блокировкой.   -  person Vlad Feinstein    schedule 11.05.2016
comment
Я думаю, что лучший способ сделать это - использовать API ОС, какую ОС вы используете?   -  person T33C    schedule 11.05.2016
comment
Не могу помочь вам с Linux, но я обнаружил, что стандартные библиотеки медленнее и менее гибки, чем API ОС. Очевидно, вы жертвуете переносимостью. Я рекомендую прочитать о том, что предлагает Linux API, и попробовать. Файлы с отображением памяти могут быть простым и эффективным решением.   -  person T33C    schedule 11.05.2016
comment
Начните с простейшего работающего кода (запись из одного потока, постановка блоков в очередь из других потоков для записи). Измерьте его производительность. Убедитесь, что запись в файл является узким местом. Определите цель: насколько быстро вам это нужно. Обновите вопрос с информацией. Нажмите изменить.   -  person jfs    schedule 11.05.2016


Ответы (1)


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

Если вы можете записать весь блок за одну запись, вы не получите никаких преимуществ при использовании fwrite — поэтому просто используйте запись напрямую — однако вам понадобится какой-то блокирующий контроль доступа (мьютекс), если все потоки совместно используют тот же fd -- поскольку поиск+запись не может выполняться атомарно, и вы бы не хотели, чтобы один поток выполнял поиск перед тем, как второй поток собирается писать.

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

Обновление: lseek может искать за пределами конца файла, просто установите параметр where = SEEK_SET и смещение на абсолютную позицию в файле (у fseek есть такая же опция, но я никогда не использовал).

person Soren    schedule 10.05.2016
comment
если я заранее не знаю, сколько всего блоков, может ли работать fseek? скажем, если блок 5 является первым завершенным, выходной файл по-прежнему имеет размер 0, может ли он перейти к позиции блока 5 в этот момент? - person Solti; 11.05.2016
comment
просто погуглите. кажется, fseek не представляет проблемы, превышающей EOF ..!!! да~~ тогда это хороший кандидат на решение. - person Solti; 11.05.2016
comment
интересно, принесет ли добавление Memory-Mapped-File для выходных файлов дополнительную производительность? - person Solti; 11.05.2016
comment
Вы можете искать за концом файла и начать запись там — однако Linux и Windows (FAT) ведут себя немного по-разному, поскольку Linux поддерживает файлы с дырками, где Windows (FAT) записывает нули, чтобы заполнить дыры — что делает его неэффективным. . - person Soren; 11.05.2016
comment
Мне нравятся файлы с отображением памяти для многих вещей, но их может быть трудно настроить для выполнения операций записи — однако теоретически они должны иметь такую ​​же производительность, как write(2) для большинства приложений — некоторые люди утверждают, что они должны быть быстрее, чем им требуется на одну копию данных меньше (от пользователя к пространству ядра), но я никогда не видел в этом истинного преимущества, но ваш MMW - person Soren; 11.05.2016
comment
Одно существенное преимущество, которое вы получаете с файлами с отображением памяти, заключается в том, что проблема безопасности потоков между поиском и записью исчезает, однако вы ограничены файлами размером ~ 1 или 2 ГБ (забудьте точное ограничение) - person Soren; 11.05.2016
comment
Давайте продолжим это обсуждение в чате. - person Solti; 11.05.2016
comment
@Soren, обратите внимание, что, за исключением съемных дисков, NTFS сейчас почти повсеместно используется в Windows и поддерживает дыры в файлах. - person Dark Falcon; 11.05.2016