читать в больших проблемах производительности файла CSV в C ++ - PullRequest
0 голосов
/ 24 апреля 2018

Мне нужно прочитать много больших CSV-файлов для обработки в C ++ (от нескольких МБ до сотен МБ) Сначала я открываю с помощью fstream, использую getline для чтения каждой строки и использую следующую функцию для разделить каждую строку "

template < class ContainerT >
void split(ContainerT& tokens, const std::string& str, const std::string& delimiters = " ", bool trimEmpty = false)
{
std::string::size_type pos, lastPos = 0, length = str.length();

using value_type = typename ContainerT::value_type;
using size_type = typename ContainerT::size_type;

while (lastPos < length + 1)
{
    pos = str.find_first_of(delimiters, lastPos);
    if (pos == std::string::npos)
    {
        pos = length;
    }

    if (pos != lastPos || !trimEmpty)
        tokens.push_back(value_type(str.data() + lastPos,
        (size_type)pos - lastPos));

    lastPos = pos + 1;
}
}

Я попробовал boost :: split, boost :: tokenizer и boost :: sprint и нашел, что приведенное выше дает лучшая производительность на данный момент. После этого я считаю, что чтение всего файла в память для обработки, а не держать файл открытым, Я использую следующую функцию для чтения всего файла со следующей функцией:

void ReadinFile(string const& filename, stringstream& result)
{
ifstream ifs(filename, ios::binary | ios::ate);
ifstream::pos_type pos = ifs.tellg();

//result.resize(pos);
char * buf = new char[pos];
ifs.seekg(0, ios::beg);
ifs.read(buf, pos);
result.write(buf,pos);
delete[]buf;

}

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

Process 2100 files with boost::split (without read in whole file) 832 sec
Process 2100 files with custom split (without read in whole file) 311 sec
Process 2100 files with custom split (read in whole file) 342 sec

Ниже приведен пример содержимого файлов одного типа, мне нужно обработать 6 типов. Но все похожи.

a1,1,1,3.5,5,1,1,1,0,0,6,0,155,21,142,22,49,1,9,1,0,0,0,0,0,0,0
a1,10,2,5,5,1,1,2,0,0,12,0,50,18,106,33,100,29,45,9,8,0,1,1,0,0,0
a1,19,3,5,5,1,1,3,0,0,18,0,12,12,52,40,82,49,63,41,23,16,8,2,0,0,0
a1,28,4,5.5,5,1,1,4,0,0,24,0,2,3,17,16,53,53,63,62,43,44,18,22,4,0,4
a1,37,5,3,5,1,1,5,0,0,6,0,157,22,129,18,57,11,6,0,0,0,0,0,0,0,0
a1,46,6,4.5,5,1,1,6,0,0,12,0,41,19,121,31,90,34,37,15,6,4,0,2,0,0,0
a1,55,7,5.5,5,1,1,7,0,0,18,0,10,9,52,36,86,43,67,38,31,15,5,7,1,0,1
a1,64,8,5.5,5,1,1,8,0,0,24,0,0,3,18,23,44,55,72,57,55,43,8,19,1,2,3
a1,73,9,3.5,5,1,1,9,1,0,6,0,149,17,145,21,51,8,8,1,0,0,0,0,0,0,0
a1,82,10,4.5,5,1,1,10,1,0,12,0,47,17,115,35,96,36,32,10,8,3,1,0,0,0,0

Мои вопросы:

1 Почему чтение во всем файле будет работать хуже, чем чтение во всем файле?

2 Есть ли какая-нибудь другая лучшая функция разделения строк?

3 Функция ReadinFile должна читать в буфер, а затем записывать в поток строк для обработки, любой способ избежать этого? то есть непосредственно в поток строк

4 Мне нужно использовать getline для разбора каждой строки (с \ n) и использовать split для токенизации каждой строки, любая функция похожа на getline для строки? например getline_str? чтобы Я могу читать в строку непосредственно

5 Как насчет прочитать весь файл в строку, а затем разбить всю строку на вектор с помощью '\ n', а затем разбить каждую строку в векторе с помощью ',' для обработки? Будет ли это работать лучше? И какой предел (максимальный размер) строки?

6 Или я должен определить такую ​​структуру (в зависимости от формата)

struct MyStruct {
  string Item1;
  int It2_3[2];
  float It4;
  int ItRemain[23];
};

а читать прямо в вектор? Как это сделать?

Большое спасибо.

Regds

LAM Chi-fung

Ответы [ 6 ]

0 голосов
/ 11 февраля 2019

Что вам нужно, так это отображение памяти.

Вы можете найти больше здесь .

0 голосов
/ 26 апреля 2018

В конце концов я использую файл с отображением памяти для решения моей проблемы, производительность намного лучше, чем я использую fscanf Так как я работаю в MS Windows, я использую «Портативное отображение памяти C ++ Class» Стефана Брамма. http://create.stephan -brumme.com / портативный-память-отображение / Поскольку мне не нужно иметь дело с файлом (ами)> 2 ГБ, моя реализация проще. Если размер файла превышает 2 ГБ, посетите веб-сайт, чтобы узнать, как с ним работать.

Ниже, пожалуйста, найдите мой кусок кода:

// may tried RandomAccess/SequentialScan
MemoryMapped MemFile(FilterBase.BaseFileName, MemoryMapped::WholeFile, MemoryMapped::RandomAccess);

// point to start of memory file
char* start = (char*)MemFile.getData();
// dummy in my case
char* tmpBuffer = start;

// looping counter
uint64_t i = 0;

// pre-allocate result vector
MyVector.resize(300000);

// Line counter
int LnCnt = 0;

//no. of field
int NumOfField=43;
//delimiter count, num of field + 1 since the leading and trailing delimiter are virtual
int DelimCnt=NoOfField+1;
//Delimiter position. May use new to allocate at run time
// or even use vector of integer
// This is to store the delimiter position in each line
// since the position is relative to start of file. if file is extremely
// large, may need to change from int to unsigner, long or even unsigned long long
static  int DelimPos[DelimCnt];

// Max number of field need to read usually equal to NumOfField, can be smaller, eg in my case, I only need 4 fields
// from first 15 field, in this case, can assign 15 to MaxFieldNeed
int MaxFieldNeed=NumOfField;
// keep track how many comma read each line
int DelimCounter=0;
// define field and line seperator
char FieldDelim=',';
char LineSep='\n';

// 1st field, "virtual Delimiter" position
DelimPos[CommaCounter]=-1
DelimCounter++;

// loop through the whole memory field, 1 and only once
for (i = 0; i < MemFile.size();i++)
{
  // grab all position of delimiter in each line
  if ((MemFile[i] == FieldDelim) && (DelimCounter<=MaxFieldNeed)){
    DelimPos[DelimCounter] = i;
    DelimCounter++;
  };

  // grab all values when end of line hit
  if (MemFile[i] == LineSep) {
    // no need to use if (DelimCounter==NumOfField) just assign anyway, waste a little bit
    // memory in integer array but gain performance 
    DelimPos[DelimCounter] = i;
    // I know exactly what the format is and what field(s) I want
    // a more general approach (as a CSV reader) may put all fields
    // into vector of vector of string
    // With *EFFORT* one may modify this piece of code so that it can parse
    // different format at run time eg similar to:
    // fscanf(fstream,"%d,%f....
    // also, this piece of code cannot handle complex CSV e.g.
    // Peter,28,157CM
    // John,26,167CM
    // "Mary,Brown",25,150CM
    MyVector.StrField = string(strat+DelimPos[0] + 1, strat+DelimPos[1] - 1);
    MyVector.IntField = strtol(strat+DelimPos[3] + 1,&tmpBuffer,10);
    MyVector.IntField2 = strtol(strat+DelimPos[8] + 1,&tmpBuffer,10);
    MyVector.FloatField = strtof(start + DelimPos[14] + 1,&tmpBuffer);
    // reset Delim counter each line
    DelimCounter=0
    // previous line seperator treat as first delimiter of next line
    DelimPos[DelimCounter] = i;
    DelimCounter++
    LnCnt++;
  }
}
MyVector.resize(LnCnt);
MyVector.shrink_to_fit();
MemFile.close();
};

С этим фрагментом кода я обрабатываю 2100 файлов (6,3 ГБ) за 57 секунд !!! (Я кодирую в нем формат CSV и получаю только 4 значения из каждой строки). Спасибо всем людям, вы все вдохновляете меня на решение этой проблемы.

0 голосов
/ 25 апреля 2018

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

Что касается размера буферабиблиотека C / C ++ обеспечит буфер чтения по умолчанию, полученный из IO_BUFSIZ в исходном коде gcc.Константа доступна как BUFSIZ в C / C ++.(с gcc это 8192 байт, с VS cl.exe это 512 байт), поэтому при чтении из файла функции ввода / вывода будут иметь BUFSIZ символов, доступных для использования, не возвращаясь кдиск.Вы можете использовать это в ваших интересах.Поэтому, независимо от того, обрабатываете ли вы символ за раз или считываете файл из буфера размером 100 КБ, количество вызовов дискового ввода-вывода будет одинаковым.(это немного нелогично)

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

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

При взгляде на ваши данные вы получаете короткий char label, а затем смешанный floatи int значений (1000 или меньше) до конца каждой записи, одна из оптимизаций, которая приходит на ум - просто обработать метку, а затем обработать оставшиеся значения как float.float представление целых чисел будет точным во всем диапазоне ваших значений, поэтому вы можете по существу обрабатывать чтение и преобразование (и хранение) в упрощенной форме.

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

Поскольку вы ищете скорость, простая процедура C для чтения и выделения хранилища может обеспечить преимущества реализации C ++ (она, безусловно, минимизирует распределение дляхранилище).

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

Короткий пример для тестирования и сравнения с другими вашими подпрограммами будет аналогичен следующему.Если вы работаете с Unix / Linux, вы можете использовать clock_gettime для синхронизации наносекунд, в Windows вам потребуется QueryPerformanceCounter для микросекунды.Сама процедура чтения может быть:

#include <stdio.h>
#include <stdlib.h>     /* for calloc, strtof */
#include <string.h>     /* for memset */
#include <errno.h>      /* strtof validation */

#define LABEL      3    /* label length (+1 for nul-character */
#define NRECS      8    /* initial number of records to allocate */
#define NFLDS  NRECS    /* initial number of fields to allocate */
#define FLDSZ     32    /* max chars per-field (to size buf) */

typedef struct {
    char label[LABEL];  /* label storage */
    float *values;      /* storage for remaining values */
} record_t;

/* realloc function doubling size allocated */
void *xrealloc (void *ptr, size_t psz, size_t *nelem);

int main (int argc, char **argv) {

    int lblflag = 1, n = 0; /* label flag, index for buf */
    size_t col = 0,         /* column index */
           idx = 0,         /* record index */
           ncol = 0,        /* fixed number of cols - 1st rec determines */
           nflds = NFLDS,   /* tracks no. of fields allocated per-rec */
           nrec = NRECS;    /* tracks no. of structs (recs) allocated */
    char buf[FLDSZ] = "";   /* fixed buffer for field parsing */
    record_t *rec = NULL;   /* pointer to record_t structs */
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin; /* file or stdin */

    if (!fp) {  /* validate file open for reading */
        fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
        return 1;
    }

    /* allocate/validate initial storage for nrec record_t */
    if (!(rec = calloc (nrec, sizeof *rec))) {
        perror ("calloc-rec");
        return 1;
    }

    /* allocate/validate initial storage for nflds values */
    if (!(rec[idx].values = calloc (nflds, sizeof *rec[idx].values))) {
        perror ("calloc-rec[idx].values");
        return 1;
    }

    for (;;) {                          /* loop continually until EOF */
        int c = fgetc (fp);             /* read char */
        if (c == EOF)                   /* check EOF */
            break;
        if (c == ',' || c == '\n') {    /* field separator or \n reached */
            char *p = buf;              /* ptr for strtof validation */
            buf[n] = 0;                 /* nul-terminate buf */
            n = 0;                      /* reset buf index zero */
            if (!lblflag) {             /* not lblflag (for branch prediction) */
                errno = 0;              /* zero errno */
                rec[idx].values[col++] = strtof (buf, &p);  /* convert buf */
                if (p == buf) {     /* p == buf - no chars converted */
                    fputs ("error: no characters converted.\n", stderr);
                    return 1;
                }
                if (errno) {        /* if errno - error during conversion */
                    perror ("strof-failed");
                    return 1;
                }
                if (col == nflds && !ncol)  /* realloc cols for 1st row a reqd */
                    rec[idx].values = xrealloc (rec[idx].values, 
                                            sizeof *rec[idx].values, &nflds);
            }
            else {                      /* lblflag set */
                int i = 0;
                do {    /* copy buf - less than 16 char, loop faster */
                    rec[idx].label[i] = buf[i];
                } while (buf[i++]);
                lblflag = 0;            /* zero lblflag */
            }
            if (c == '\n') {        /* if separator was \n */
                if (!ncol)          /* 1st record, set ncol from col */
                    ncol = col;
                if (col != ncol) {  /* validate remaining records against ncol */
                    fputs ("error: unequal columns in file.\n", stderr);
                    return 1;
                }
                col = 0;            /* reset col = 0 */
                lblflag = 1;        /* set lblflag 1 */
                idx++;              /* increment record index */
                if (idx == nrec)    /* check if realloc required */
                    rec = xrealloc (rec, sizeof *rec, &nrec);
                /* allocate values for next record based on now set ncol */
                if (!(rec[idx].values = calloc (ncol, sizeof *rec[idx].values))) {
                    perror ("calloc-rec[idx].values");
                    return 1;
                }
            }
        }
        else if (n < FLDSZ) /* normal char - check index will fit */
            buf[n++] = c;   /* add char to buf */
        else {  /* otherwise chars exceed FLDSZ, exit, fix */
            fputs ("error: chars exceed FLDSZ.\n", stdout);
        }
    }
    if (fp != stdin) fclose (fp);   /* close file if not stdin */
    /* add code to handle last field on non-POSIX EOF here */
    if (!*rec[idx].label) free (rec[idx].values);  /* free unused last alloc */

    printf ("records: %zu   cols: %zu\n\n", idx, ncol); /* print stats */

    for (size_t i = 0; i < idx; i++) {      /* output values (remove) */
        fputs (rec[i].label, stdout);
        for (size_t j = 0; j < ncol; j++)
            printf (" %3g", rec[i].values[j]);
        free (rec[i].values);               /* free values */
        putchar ('\n');
    }
    free (rec);     /* free structs */

    return 0;
}

/** realloc 'ptr' of 'nelem' of 'psz' to 'nelem * 2' of 'psz'.
 *  returns pointer to reallocated block of memory with new
 *  memory initialized to 0/NULL. return must be assigned to
 *  original pointer in caller.
 */
void *xrealloc (void *ptr, size_t psz, size_t *nelem)
{   void *memptr = realloc ((char *)ptr, *nelem * 2 * psz);
    if (!memptr) {
        perror ("realloc(): virtual memory exhausted.");
        exit (EXIT_FAILURE);
    }   /* zero new memory (optional) */
    memset ((char *)memptr + *nelem * psz, 0, *nelem * psz);
    *nelem *= 2;
    return memptr;
}

Пример использования / вывода

$ ./bin/readlargecsvbuf dat/large.csv
records: 10   cols: 26

a1   1   1 3.5   5   1   1   1   0   0   6   0 155  21 142  22  49   1   9   1   0   0   0   0   0   0   0
a1  10   2   5   5   1   1   2   0   0  12   0  50  18 106  33 100  29  45   9   8   0   1   1   0   0   0
a1  19   3   5   5   1   1   3   0   0  18   0  12  12  52  40  82  49  63  41  23  16   8   2   0   0   0
a1  28   4 5.5   5   1   1   4   0   0  24   0   2   3  17  16  53  53  63  62  43  44  18  22   4   0   4
a1  37   5   3   5   1   1   5   0   0   6   0 157  22 129  18  57  11   6   0   0   0   0   0   0   0   0
a1  46   6 4.5   5   1   1   6   0   0  12   0  41  19 121  31  90  34  37  15   6   4   0   2   0   0   0
a1  55   7 5.5   5   1   1   7   0   0  18   0  10   9  52  36  86  43  67  38  31  15   5   7   1   0   1
a1  64   8 5.5   5   1   1   8   0   0  24   0   0   3  18  23  44  55  72  57  55  43   8  19   1   2   3
a1  73   9 3.5   5   1   1   9   1   0   6   0 149  17 145  21  51   8   8   1   0   0   0   0   0   0   0
a1  82  10 4.5   5   1   1  10   1   0  12   0  47  17 115  35  96  36  32  10   8   3   1   0   0   0   0

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

0 голосов
/ 25 апреля 2018

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

Учитывая каждую структуру, которую вы хотите прочитать, например, ваш пример ...

struct MyStruct {
  string Item1;
  int It2_3[2];
  float It4;
  int ItRemain[23];
};

... youможет читать и анализировать поля, используя fscanf.К сожалению, это функция библиотеки C, которая не поддерживает std::string s, поэтому вам потребуется создать буфер массива символов для каждого строкового поля, а затем скопировать его в поле своей структуры.В общем, что-то вроде:

char Item1[4096];
MyStruct m;
std::vector<MyStruct> myStructs;
FILE* stream = fopen(filename, "r");
assert(stream);
while (fscanf(stream, "%s,%d,%d,%f,%d,%d,%d,%d...",
              Item1, &m.It2_3[0], &m.It2_3[1], &m.It4,
              &m.ItRemain[0], &m.ItRemain[1], &m.ItRemain[2], ...) == 27)
{
    myStructs.push_back(m);
    myStructs.back().Item1 = Item1;  // fix the std::strings
}
fclose(stream);

(просто введите правильное число %d s в строке формата и заполните остальные ItRemain индексы).


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

0 голосов
/ 25 апреля 2018

1 Почему чтение в целом файле будет работать хуже, чем в целом файл?

Три слова: Местонахождение ссылки .

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

Компьютерные разработчики пытаются скрыть гигантское несоответствие между скоростью процессора и оперативной памяти (и дальнейшее гигантское расхождение между скоростью оперативной памяти и скоростью диска) через кэширование ; например, когда ЦПУ сначала хочет получить доступ к данным на конкретной странице 4 КБ ОЗУ, ему придется сидеть и вертеть пальцами (что кажется ЦП) очень долго, прежде чем эти данные будут доставлены из ОЗУ в ЦПУ. Но после этого первого мучительного ожидания второй доступ ЦП к соседним данным на той же странице ОЗУ будет довольно быстрым, поскольку в этот момент страница кэшируется в кэш-памяти ЦП, и ЦП больше не нужно ждать это будет доставлено.

Но кэш-память центрального процессора (относительно) мала - далеко не достаточно для размещения всего файла размером более 100 МБ. Поэтому, когда вы загружаете гигантский файл в ОЗУ, вы заставляете ЦП делать два прохода по большой области памяти - первый проход, чтобы прочитать все данные, а затем второй проход, когда вы вернетесь, чтобы проанализировать все данные.

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

2 Любая другая лучшая функция разделения строк?

Мне всегда нравилось strtok () , так как вы можете быть уверены, что он не сделает ничего неэффективного (например, вызов malloc () / free ()) за вашей спиной. Или, если вы хотите по-настоящему сойти с ума, вы можете написать свой собственный мини-парсер, используя указатель char * и цикл for, хотя я сомневаюсь, что он все равно окажется заметно быстрее, чем цикл на strtok().

3 Функция ReadinFile должна читать в буфер, а затем записывать в stringstream для обработки, любой способ избежать этого? т.е. непосредственно в stringstream

Я бы сказал, просто поместите while () - цикл вокруг fgets () , а затем после каждого вызова fgets() прочитайте строку CSV-текста, получите внутренний while ( ) -округло strtok () , чтобы разобрать поля в этой строке. Для максимальной эффективности трудно пойти не так, как надо с добрым старомодным вводом / выводом в стиле C.

5 Как насчет прочитать весь файл в строку, а затем разбить весь строка в вектор с помощью '\ n', а затем разбить каждую строку в векторе с ',' обрабатывать ? Будет ли это работать лучше? И какой предел (макс размер) строки?

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

6 Или я должен определить такую ​​структуру (в зависимости от формата) [...] и читать прямо в вектор? Как это сделать?

Да, это хорошая идея - если вы сможете сделать все за один проход, вы выйдете вперед с точки зрения эффективности. Вы должны просто иметь возможность объявить (например, vector<struct MyStruct>) и для каждой строки, которую вы анализируете в файле, записывать проанализированные значения в объект MyStruct, когда вы их анализируете (например, с помощью atoi () * 1053). *), а затем после того, как объект MyStruct будет полностью заполнен / записан, push_back(myStruct) до конца вектора.

(Единственное, что быстрее, чем это, это избавиться от vector<struct MyStruct> и просто сделать (все, что вам нужно сделать) с данными прямо в вашем цикле синтаксического анализа, не потрудившись сохранить весь набор данных в большом векторе вообще. Это может быть вариант, например, если вам просто нужно вычислить сумму всех элементов в каждом поле, но OTOH это может быть невозможно для вашего варианта использования)

0 голосов
/ 24 апреля 2018

В основном вы хотите избежать копирования.

Если вы можете позволить памяти загрузить весь файл в массив, а затем использовать этот массив напрямую, не конвертируйте его обратно в поток строк, так как это делает другую копию, просто обработайте большой буфер!

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

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

Вы можете использовать такие методы, как strtok_r, чтобы разбить ваш файл на строки, а строки на поля, хотя вам нужно иметь дело с экранированными маркерами полей - это нужно делать в любом случае. Можно написать токенизатор, который работает как strtok, но возвращает строки, подобные string_view, вместо фактической вставки нулевых байтов.

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

Таким образом, это должно значительно сократить объем копируемых данных и ускорить процесс.

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

...