Потоковое чтение файлов в C ++

0

Я написал программу (используя FFTW) для выполнения преобразований Фурье некоторых файлов данных, написанных в OpenFOAM.

Программа сначала находит пути к каждому файлу данных (501 файлов в моем текущем примере), затем разделяет пути между потоками, так что thread0 получает пути 0-> 61, thread1 получает 62-> 123 или около того и т.д. И затем запускает оставшиеся файлы в последовательном порядке в конце.

Я использовал таймеры во всем коде, чтобы попытаться увидеть, где это узкие места, поскольку каждый раз запускать каждый файл занимает около 3.5 секунд, а для 8 файлов параллельно время составляет около 21 секунды (сокращение от 28s для 8x3.5 (серийное время), но не столько)

Проблемный раздел моего кода ниже

if (DIAG_timers) {readTimer = timerNow();}
for (yindex=0; yindex<ycells; yindex++)
{
    for (xindex=0; xindex<xcells; xindex++)
    {
        getline(alphaFile, alphaStringValue);
        convertToNumber(alphaStringValue, alphaValue[xindex][yindex]);
    }
}
if (DIAG_timers) {endTimerP(readTimer, tid, "reading value and converting", false);}

Здесь timerNow() возвращает значение часов, а endTimerP вычисляет время, прошедшее через ms. (Остальные аргументы связаны с тем, что он работает в параллельном потоке, чтобы не выводить 8 строк для каждого цикла и т.д. И описание того, что измеряет таймер).

convertToNumber принимает значение в alphaStringValue и преобразует его в double, который затем сохраняется в массиве alphaValue.

alphaFile - это объект std :: ifstream, а alphaStringValue - std :: string, который сохраняет текст в каждой строке.

Файлы, которые нужно прочитать, составляют приблизительно 40 МБ каждый (всего несколько строк более 5120000, каждый из которых содержит только одно значение, от 0 до 1 (в большинстве случаев == (0 || 1)), и у меня 16 ГБ ОЗУ, поэтому копирование всех файлов в память, конечно, было бы возможно, так как только 8 (1 на поток) должны быть открыты сразу. Я не уверен, что mmap будет делать это лучше? Несколько потоков в stackoverflow спорят о достоинствах mmap и более простых операциях чтения, в частности для последовательного доступа, поэтому я не знаю, будет ли это выгодно.

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

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

Редактировать:

template<class T> inline void convertToNumber(std::string const& s, T &result)
{
    std::istringstream i(s);
    T x;
    if (!(i >> x))
        throw BadConversion("convertToNumber(\"" + s + "\")");
    result = x;
}

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

  • 0
    Использовали ли вы time (встроенная оболочка, или /usr/bin/time ) для тестирования вашей программы (особенно однопоточного). Вы уверены, что это не I / O или системный процессор? Готовы ли вы потратить часы работы на несколько% улучшения?
  • 0
    Функции таймера используют gettimeofday а затем просто вычисляют разницу, возвращая unsigned long long s. Остальная часть программы занимает порядка 0,5 с на цикл, по сравнению с 3,5 на поток для этого раздела, так что не думайте, что это связано с процессором. Я менее уверен насчет ввода-вывода, но, похоже, мой iotop dstat издает мало шума, а iotop и dstat показывают случайный iotop dstat , но в основном вывод, когда программа пишет позже (этот шаг занимает всего ~ 0,1 секунды и защищен блокировкой мьютекса, поскольку все процессы пишут в один файл)
Показать ещё 8 комментариев
Теги:
io
pthreads
getline

2 ответа

1
Лучший ответ

Файлы, которые нужно прочитать, составляют приблизительно 40 МБ каждый (всего несколько строк более 5120000, каждый из которых содержит только одно значение, от 0 до 1 (в большинстве случаев == (0 || 1)), и у меня 16 ГБ ОЗУ, поэтому копирование всех файлов в память, безусловно, было бы возможно,

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

так как только 8 (1 на поток) должны быть открыты сразу.

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

Я не уверен, что mmap сделает это лучше?

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

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

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


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

  • 0
    Спасибо вам за это - я протестирую его и рассмотрю, можно ли ожидать дальнейших улучшений через время ввода-вывода, но, как вы говорите, я не получу много (что-нибудь?), Учитывая, что файлы читаются только один раз в настоящее время ,
  • 0
    Отмечать это как ответ, так как я не могу пометить комментарии как ответы, и так как в нем описываются функции, которые необходимо учитывать повторно. mmap , а также способ измерения getrusage(2) в программе с использованием getrusage(2)
0

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

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

Ещё вопросы

Сообщество Overcoder
Наверх
Меню