Самое быстрое чтение файлов в многопоточном приложении

Мне нужно прочитать в память матрицу 8192x8192. Я хочу сделать это как можно быстрее.
Сейчас у меня есть такая структура:

char inputFile[8192][8192*4]; // I know the numbers are at max 3 digits
int8_t matrix[8192][8192]; // Matrix to be populated

// Read entire file line by line using fgets
while (fgets (inputFile[lineNum++], MAXCOLS, fp));

//Populate the matrix in parallel, 
for (t = 0; t < NUM_THREADS; t++){
    pthread_create(&threads[t], NULL, ParallelRead, (void *)t);
}

В функции ParallelRead я разбираю каждую строку, выполняю atoi и заполняю матрицу. Параллелизм является построчным, как поток t разбирает строку t, t+ 1 * NUM_THREADS..

В двухъядерной системе с 2 потоками это занимает

Loading big file (fgets) : 5.79126
Preprocessing data (Parallel Read) : 4.44083

Есть ли способ еще больше оптимизировать это?


person sud03r    schedule 20.05.2012    source источник
comment
Возможно, вы могли бы запустить заполняющие потоки параллельно с вводом-выводом, когда станет доступно достаточно данных.   -  person vanza    schedule 20.05.2012
comment
Честно говоря, я немного удивлен, что вам удалось получить любое повышение производительности за счет чтения одного и того же файла из нескольких потоков ... При тестировании вы убедитесь, что файл действительно читается из диск, а не из кеша?   -  person NPE    schedule 20.05.2012
comment
@aix Я использовал 2 потока, например. Я распараллелил часть предварительной обработки, это после считывания данных в память.   -  person sud03r    schedule 20.05.2012
comment
Лучшее, что вы можете получить, - это менее чем в два раза быстрее, даже если вы можете сделать Preprocessing нулевым временем. Стоит ли оно того? (См. Закон Амдала.)   -  person Alan Stokes    schedule 20.05.2012
comment
@AlanStokes Я не вижу вреда в экономии 4,44 секунды, если у меня есть ресурсы. На самом деле мне удалось сократить время предварительной обработки до 0,1 на машине с 40 ядрами.   -  person sud03r    schedule 20.05.2012
comment
@vanza, возможно, ты прав, я тоже об этом думал. Я мог бы позволить fgets () продолжить работу в одном потоке и запустить задачу предварительной обработки в другом потоке, но это потребует от моих потоков ожидания, если данные недоступны, и я видел при этом потери производительности.   -  person sud03r    schedule 20.05.2012
comment
Единственные известные мне способы улучшить производительность чтения с диска: 1) чтение данных из сжатого источника. 2) используйте более быстрые диски или RAID-массив. или 3) разделить данные на отдельные диски и читать по одному потоку на каждый диск. Обычно, если отдельный поток не успевает за временем чтения вашего диска, у вас возникают большие проблемы.   -  person mfa    schedule 20.05.2012
comment
Храните свои данные в двоичном формате. Если каждый элемент матрицы может принимать не более 256 различных значений, мы имеем дело с 64 МБ, которые должны быть легко обработаны современным оборудованием. Затем вы также можете отобразить файл непосредственно в вашей программе.   -  person Kerrek SB    schedule 20.05.2012
comment
@KerrekSB, вы хотите сказать, что входной файл, который я читаю, должен быть двоичным? Я не могу изменить формат входного файла.   -  person sud03r    schedule 21.05.2012
comment
@ sud03r: Вы могли бы написать инструмент преобразования, если это повысит вашу общую эффективность работы.   -  person Kerrek SB    schedule 21.05.2012


Ответы (4)


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

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

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

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

person Hans Passant    schedule 20.05.2012
comment
он не читает файл параллельно, он параллельно преобразует строку в int8_t из памяти. В этом нет ничего плохого. - person kratenko; 21.05.2012
comment
Я никогда не утверждал, что в этом что-то не так. Я рекомендовал перекрыть это с потоком, который читает данные. - person Hans Passant; 22.06.2013

Стоит подумать о выделении двух меньших входных буферов (скажем, по 200 строк в каждом).

Затем пусть один поток считывает данные во входные буферы. Когда один входной буфер заполнен, передайте его второму потоку, который выполняет синтаксический анализ. Этот второй поток может использовать пул потоков для параллельного синтаксического анализа (проверьте openMP).

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

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

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

person Community    schedule 20.05.2012

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

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

person EvilTeach    schedule 20.05.2012

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

person bobah    schedule 20.05.2012