Сохранение вектора в файл параллельно - PullRequest
4 голосов
/ 29 июня 2011

У меня есть отсортированный вектор полмиллиона чисел (в C ++). Сохранение его в текстовый файл занимает около 10 секунд и использует только 50% ЦП (1 ядро). Я думал о его распараллеливании, сохранении двух отдельных файлов (первой и второй половины вектора) и последующем объединении этих файлов.

Проблема в том, что я не могу найти какой-либо другой способ объединения, кроме чтения побайтовых байтов и присоединения к первому файлу ... Есть ли какой-либо платформо-независимый способ (Boost или Windows-специфичный) для эффективно объединить файлы?

Ответы [ 4 ]

4 голосов
/ 29 июня 2011

То, что вы говорите, тем не менее, явно указывает на очень неэффективный способ написания вашего текстового файла.Возможно, вы используете endl, что вызывает flush.Замените это на \n.Далее, если это не ускорит процесс, рассмотрите более эффективное преобразование числа в текст, чем просто использование <<.sprintf приходит на ум.Наконец, если вы все еще находитесь в диапазоне 10 секунд вместо диапазона 1/10 секунды, подумайте о более серьезной оптимизации (например, на компьютере под управлением Windows вы можете выделить файл с правильным размером в начале и т. Д.)

Приветствия и hth.,

0 голосов
/ 29 июня 2011

Форматирование невероятно дорого. Запись 128M чисел двойной точности на диск с помощью fprintf () и fwrite () может легко занять 10 раз дольше из-за форматирования и большого количества вызовов (по сравнению с одним большим fwrite ()); попробуйте приведенный ниже код и посмотрите, получите ли вы похожие тайминги. Текстовые файлы не способ справиться со значительными объемами данных; если вы на самом деле не собираетесь сесть и прочитать все сами, это не должно быть в ascii.

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

#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <time.h>
/* Jonathan Dursi, SciNet */

#define FILESIZE 1024*1024*128

int write_file_bin(const char *fname, const double *data, const int ndata) {

    FILE *fp;
    time_t start, end;

    fp=fopen(fname,"wb");
    assert(fp);
    start = time(NULL);
    fwrite(data, sizeof(double), ndata, fp);
    end = time(NULL);
    fclose(fp);

    return (int)(end-start);
}

int write_file_ascii(const char *fname, const double *data, const int ndata) {

    FILE *fp;
    time_t start, end;
    int i;

    fp=fopen(fname,"wb");
    assert(fp);
    start = time(NULL);
    for (i=0;i<ndata;i++) {
        fprintf(fp,"%lf\n",data[i]);
    }
    end = time(NULL);
    fclose(fp);

    return (int)(end-start);
}

int main(int argc, char **argv) {
    double *data;
    int i;
    int asciitime, bintime;

    data = (double *)malloc(FILESIZE * sizeof(double));
    assert(data);
    for (i=0;i<FILESIZE;i++) {
        data[i] = i*(double)i/2.;
    }

    asciitime = write_file_ascii("data.txt",data,FILESIZE); 
    bintime   = write_file_bin("data.dat",data,FILESIZE); 

    printf("Time to write files: ASCII: %d, Binary: %d\n",asciitime, bintime);

    return 0;
}
0 голосов
/ 29 июня 2011

Я бы обычно соглашался с тем, что ваш диск является узким местом - НО, если загрузка ЦП точно 50% в двухъядерной системе, это означало бы, что ЦП действительно является проблемой. В этом случае это преобразование числа в строку замедляется. См. Ответ Альфа за советами по оптимизации.

Чтобы распараллелить, чтобы дать каждому потоку кусок вектора и ostream. Первый поток получает файл как его ostream, но другие получают потоки памяти. После завершения первого потока и завершения каждого другого потока (по порядку) запишите каждый поток памяти в файл.

Форматирование теперь выполняется параллельно, при этом фактическая запись в файлы сериализуется.

0 голосов
/ 29 июня 2011

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

ХотяЕсть несколько способов записи файлов с использованием нескольких ядер. Скорее всего, очень хорошо, что узким местом является ваша скорость дискового ввода-вывода.Вы можете запустить vmstat 1 в системе Linux и многих системах Unix, чтобы увидеть только скорость записи на диск.(Как и многие другие аккуратные меры.) В Windows есть аналогичный инструмент, но я никогда не могу вспомнить название этой вещи.Если ваша скорость записи близка к скорости вашего диска, вы, вероятно, не сможете повысить производительность, добавив больше ядер.

Если вы все равно хотите попробовать, есть три подхода, которые могут работать:

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

Первоедва подхода требуют, чтобы данные, которые вы пишете, имели размер предсказуемый ;если вы действительно пишете числа по 500 тысяч, то для каждого из них потребуется 4, 8 или некоторого другого фиксированного размера , что делает его довольно простым - просто назначьте первые 256 тысяч номеров первому потоку и следующей стопкечисел к следующему потоку, начиная с 256*1024*8 байтов в файле.

Edit

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

...