Я написал программу (используя 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 предположительно устраняет возможность обнаружения ошибки, но экономит огромное количество (по крайней мере, в этом контролируемом случае) ненужных операций.
Файлы, которые нужно прочитать, составляют приблизительно 40 МБ каждый (всего несколько строк более 5120000, каждый из которых содержит только одно значение, от 0 до 1 (в большинстве случаев == (0 || 1)), и у меня 16 ГБ ОЗУ, поэтому копирование всех файлов в память, безусловно, было бы возможно,
Да. Но загрузка их там по-прежнему будет учитываться в отношении времени настенных часов вашего процесса, если только они не были прочитаны другим процессом раньше.
так как только 8 (1 на поток) должны быть открыты сразу.
Так как любые файлы, которые не были загружены в память до начала процесса, должны быть загружены, и, таким образом, загрузка будет отсчитываться в направлении времени на стене процесса, неважно, сколько из них открыто одновременно. Любые, которые не являются кешем, замедляют процесс.
Я не уверен, что mmap сделает это лучше?
Нет, это не так. mmap
быстрее, но поскольку он сохраняет копию из буфера ядра в буфер приложения и некоторые служебные данные системного вызова (при чтении вы делаете запись ядра для каждой страницы, а страницы mmap, которые считываются с чтением вперед, не будут вызывать дальнейших ошибок страницы). Но это не сэкономит вам время на чтение файлов с диска, если они еще не кэшированы.
mmap
не загружает ничего в память. Ядро загружает данные с диска во внутренние буферы, кэш страниц. read
копирует данные оттуда в буфер приложения, а mmap
предоставляет части кеша страницы непосредственно в вашем адресном пространстве. Но в любом случае данные извлекаются при первом доступе и остаются там до тех пор, пока диспетчер памяти не сбросит их, чтобы повторно использовать память. Кэш страницы является глобальным, поэтому, если один процесс приводит к кэшированию некоторых данных, следующий процесс ускорит их работу. Но если это первый доступ после более длительного времени, данные должны быть прочитаны, и это будет влиять на read
и mmap
точно так же.
Поскольку распараллеливание процесса не улучшило время, похоже, что в большинстве случаев это фактический ввод-вывод. Таким образом, вы можете оптимизировать немного больше, и mmap
может помочь, но не ожидайте многого. Единственный способ улучшить время ввода-вывода - получить более быстрый диск.
Вы должны иметь возможность попросить систему рассказать вам, сколько времени было потрачено на процессор и сколько было потрачено на ожидание ввода данных (ввода-вывода) с помощью getrusage
(2) (вызвать его в конце каждого потока, чтобы получить данные для этого нить). Таким образом, вы можете подтвердить, сколько времени было потрачено на ввод-вывод.
mmap
, а также способ измерения getrusage(2)
в программе с использованием getrusage(2)
mmap
- это, безусловно, самый эффективный способ получить большие объемы данных в память. Основное преимущество здесь в том, что нет дополнительного копирования.
Однако он делает код несколько более сложным, поскольку вы не можете напрямую использовать функции ввода-вывода файлов для использования mmap
(и основное преимущество - это потеря, если вы используете режим "m"
для функций stdio, так как вы сейчас получив хотя бы одну копию). Из прошлых экспериментов, которые я сделал, mmap
превосходит все другие варианты чтения файлов на какую-то сумму. Сколько зависит от того, какая доля общего времени тратится на ожидание диска и сколько времени тратится на обработку содержимого файла.
time
(встроенная оболочка, или/usr/bin/time
) для тестирования вашей программы (особенно однопоточного). Вы уверены, что это не I / O или системный процессор? Готовы ли вы потратить часы работы на несколько% улучшения?gettimeofday
а затем просто вычисляют разницу, возвращаяunsigned long long
s. Остальная часть программы занимает порядка 0,5 с на цикл, по сравнению с 3,5 на поток для этого раздела, так что не думайте, что это связано с процессором. Я менее уверен насчет ввода-вывода, но, похоже, мойiotop
dstat
издает мало шума, аiotop
иdstat
показывают случайныйiotop
dstat
, но в основном вывод, когда программа пишет позже (этот шаг занимает всего ~ 0,1 секунды и защищен блокировкой мьютекса, поскольку все процессы пишут в один файл)