Одновременно читать из одного файла - PullRequest
4 голосов
/ 03 мая 2009

У меня следующая проблемная ситуация. Куча данных разбита на 10 тыс. Небольших файлов (около 8-16 кб каждый). В зависимости от пользовательского ввода, я должен загрузить их как можно быстрее и обработать их. Точнее, каждый пакет данных может быть разбит на 100-100 тыс. Файлов, и существует приблизительно 1 тыс. Пакетов данных. Хотя большинство из них поменьше.

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

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

Вопрос заключается в следующем: как можно разрешить потокам 1..N эффективно читать из одного файла в этом сценарии? Я могу использовать асинхронный ввод-вывод в Windows, но он должен стать синхронным для операций чтения менее 64 КБ. Отображение памяти в файле не вариант, так как ожидаемый размер составляет> 1,6 ГБ, и мне все еще нужно иметь возможность работать на x86 (если я не могу эффективно отобразить какую-то крошечную часть, прочитать ее, снова отобразить ее - мой опыт работы с отображение памяти состояло в том, что оно приносит довольно много накладных расходов по сравнению с одним чтением).

Я думал об открытии каждого из пакетов данных N раз и предоставляю каждому потоку дескриптор циклическим образом, но проблема в том, что он может в конечном итоге иметь (количество файлов данных) x (максимальное количество потоков) ) открытые дескрипторы (может легко стать 8-16k), и мне пришлось бы синхронизироваться при каждом доступе к пакету данных или использовать магию без блокировки, чтобы получить следующий бесплатный дескриптор файла.

Поскольку это не кажется оригинальной проблемой (я полагаю, что любой движок базы данных имеет подобную, где вы можете иметь M таблиц (пакетов данных) с N строками (файлы в моем случае), и вы хотите разрешить как можно больше потоков для одновременного чтения строк). Так какова рекомендуемая практика здесь? Кстати, он должен работать в Windows и Linux, поэтому приветствуются переносимые подходы (или, по крайней мере, подходы, которые работают на обеих платформах, даже если они используют разные базовые API - до тех пор, пока они могут быть упакованы, я счастлив).

[ РЕДАКТИРОВАТЬ ] Дело не в скорости, а в сокрытии задержки. То есть я читаю около 100 таких крошечных файлов в секунду, может быть, я не больше 1 Мбит / с. Моя главная проблема - время поиска (поскольку моя схема доступа не предсказуема), и я хочу скрыть их, запуская показания при отображении старых данных пользователю. Вопрос в том, как разрешить нескольким потокам отправлять запросы ввода-вывода для нескольких файлов, возможно,> 1 поток обращается к одному файлу.

Это действительно не проблема, если один из вызовов занимает около 70 мс или около того, чтобы завершить, но я не могу себе позволить, если вызов вызова чтения блокируется.

Ответы [ 5 ]

2 голосов
/ 03 мая 2009

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

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

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

1 голос
/ 05 мая 2009

В Linux вообще нет пригодного для использования асинхронного ввода-вывода (да, есть aio_ *, но он работает только на O_DIRECT и имеет все виды странных ограничений), поэтому, если вы хотите что-то переносимое, вам просто придется использовать обычный читать звонки. mmap будет работать, но стоимость изменения отображения может быть немного высокой, если вы собираетесь читать только небольшое количество каждый раз.

Теперь я не знаю о Windows, но в Linux есть функция pread (), которая позволяет вам читать из файлового дескриптора с заданным смещением, не затрагивая указатель поиска файлового дескриптора. При этом вы можете иметь любое количество потоков, читающих из одного и того же файла, без необходимости блокировать дескриптор файла или что-то глупое.

0 голосов
/ 03 мая 2009

Подход mmap пригодится. Вам не нужно делать цикл mmap / unmap для каждого чтения, но есть поток, обрабатывающий все эти сопоставления и обрабатывающий указатели (фактически смещение и длину). Оперативное чтение будет запланировано ОС, когда поток получит доступ к виртуальной памяти, сопоставленной с файлом.

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

0 голосов
/ 03 мая 2009

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

0 голосов
/ 03 мая 2009

Самый быстрый способ, которым я могу представить, чтобы прочитать большой кусок данных, - это создать раздел на диске (основной или логический, но без LVM) и непосредственно прочитать устройство раздела (например, /dev/sda5) последовательно , без файловой системы, используя только один поток на диск. Важно обращаться к необработанному диску последовательно, чтобы избежать поиска диска, который намного медленнее, чем последовательное чтение.

...