Эффективный способ регистрации файлов

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

void logInFile(int a, int b, int c, int d, int e)
{
sprintf(rowInLog,"%d,%d,%d,%d,%d\n",a,b,c,d,e);
int bytesInRow = strlen(rowInLog);
if(bytesInRow + bytesUsedInBuffer <= sizeOfBuffer)
{
strcat(buffer, rowInLog);
bytesUsedInBuffer += bytesInRow;
}
else
{
printf("flushing file to disk\n");
fwrite(buffer, bytesUsedInBuffer, 1, fp);
memset(buffer, 0, sizeOfBuffer);
bytesUsedInBuffer = 0;
strcat(buffer, rowInLog);
bytesUsedInBuffer += bytesInRow;
}
}

Но это делает выполнение намного медленным, и это не из-за сброса, потому что сообщение «сброс файла на диск» не выводится на экран. Без какого-либо вызова этой функции регистрации вся программа выполняется за считанные минуты, но при этом она не завершилась даже за 2 часа. Есть ли какой-то другой фундаментальный недостаток?

0

Решение

Ваш ответ, я подозреваю, прямо здесь:

if(bytesInRow + bytesUsedInBuffer <= sizeOfBuffer)
{
strcat(buffer, rowInLog);  // <--- riiiight here.
bytesUsedInBuffer += bytesInRow;
}

strcat() функция будет сканировать весь buffer чтобы найти конец, когда ты это называешь. Если buffer это большой и в основном полный, это может быть довольно медленно. Поведение примерно O (N2) в размере buffer, Ваша производительность будет быстро снижаться по мере увеличения размера буфера. Это в значительной степени противоположно тому, что вы хотите от вашего буфера. (Изменить: Вы упомянули в комментарии, что ваш буфер 1 ГБ. Я ожидаю, что приведенный выше код будет очень, очень медленно, как этот буфер заполняет.)

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

if(bytesInRow + bytesUsedInBuffer <= sizeOfBuffer)
{
memcpy(buffer + bytesUsedInBuffer, rowInLog, bytesInRow + 1);
bytesUsedInBuffer += bytesInRow;
}

Обратите внимание, что у меня было memcpy скопируйте один дополнительный байт, чтобы он поместил терминатор NUL в буфер, на случай, если у вас есть какие-либо другие функции strXXX, которые работают на buffer, Если вы этого не сделаете, вы можете безопасно удалить + 1 выше.

Похожая, менее вопиющая ошибка происходит в вашем else пункт. Вы можете заменить это также memcpy:

    printf("flushing file to disk\n");
fwrite(buffer, bytesUsedInBuffer, 1, fp);
memcpy(buffer, rowInLog, bytesInRow + 1);
bytesUsedInBuffer = bytesInRow;

Вы также можете сэкономить немного времени, комбинируя эти операторы:

sprintf(rowInLog,"%d,%d,%d,%d,%d\n",a,b,c,d,e);
int bytesInRow = strlen(rowInLog);

sprintf возвращает длину выходной строки, так что вы можете просто сказать:

int bytesInRow = sprintf(rowInLog,"%d,%d,%d,%d,%d\n",a,b,c,d,e);

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


РЕДАКТИРОВАТЬ: Еще лучший альтернативный подход:

Если вы хотите устранить memcpy() полностью, рассмотрим этот альтернативный подход:

bytesUsedInBuffer += snprintf( buffer + bytesUsedInBuffer, maximumLineSize,
"%d,%d,%d,%d,%d\n", a,b,c,d,e );

if (bytesUsedInBuffer >= sizeOfBuffer - maximumLineSize )
{
fwrite(buffer, bytesUsedInBuffer, 1, fp);
bytesUsedInBuffer = 0;
}

Задавать maximumLineSize до разумного значения для вашей строки из 5 целых чисел, например 60. (10 байтов для каждого целого числа, включая знак плюс 5 байтов для запятых и новой строки — 55, поэтому 60 — это хорошее круглое число выше этого).

3

Другие решения

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

size_t size = sprintf(rowInLog + rowInLogSize, "%d,%d,%d,%d,%d\n", a, b, c, d, e);
rowInLogSize += size;
1

По вопросам рекламы [email protected]