Использование нескольких ядер для обработки большого, последовательного файла в С++

У меня есть большой файл (больше, чем оперативная память, не могу прочитать сразу), и мне нужно обработать его по строкам (в С++). Я хочу использовать несколько ядер, желательно с Intel TBB или Microsoft PPL. Я предпочел бы избежать предварительной обработки этого файла (например, его разделение на 4 части и т.д.).

Я думал о чем-то вроде использования 4 итераторов, инициализированных позициями (0, n/4, 2 * n/4 3 * n/4) в файле и т.д.

Это хорошее решение и есть ли простой способ его достижения?

Или, может быть, вы знаете некоторые библиотеки, которые поддерживают эффективное одновременное чтение потоков?

обновление:

Я делал тесты. IO - это не узкое место, а процессор. И у меня много оперативной памяти для буферов.

Мне нужно проанализировать запись (размер var, приблизительно 2000 байт каждый, записи разделены уникальным "\ 0" char), проверить его, выполнить некоторые вычисления и записать результат в другой файл (ы)

Ответ 1

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

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

Ответ 2

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

Пример такого подхода доступен в дистрибутивах TBB; см. примеры/конвейер/квадрат. Он использует "старый" интерфейс, класс tbb::pipeline и фильтры (классы, унаследованные от tbb::filter), которые передают данные указателями void*. Более удобный, удобный для использования и лямбда-дружественный "декларативный" интерфейс tbb::parallel_pipeline() может быть более удобным для использования.

Ответ 3

ianmac уже намекнул на проблему поиска. Идея вашего итератора разумна с небольшим завихрением: инициализируйте их до 0,1,2 и 3 и увеличивайте каждый на 4. Итак, первый поток работает на элементах 0,4,8 и т.д. ОС будет убедиться, что файл как можно быстрее загружается в ваше приложение. Возможно, вы сможете сообщить своей ОС, что вы будете выполнять последовательное сканирование через файл (например, в Windows, это флаг CreateFile).

Ответ 4

С точки зрения чтения из файла, я бы не рекомендовал этого. Жесткие диски, насколько я знаю, не могут читать из более чем одного места за один раз.

Однако обработка данных совсем другая вещь, и вы можете легко сделать это в нескольких потоках. (Сохранение данных в правильном порядке также не будет/не должно быть затруднительным.)

Ответ 5

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

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

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