我必须在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 :(得分:3)
我怀疑你的答案就在这里:
if(bytesInRow + bytesUsedInBuffer <= sizeOfBuffer)
{
strcat(buffer, rowInLog); // <--- riiiight here.
bytesUsedInBuffer += bytesInRow;
}
strcat()
函数将扫描整个buffer
,以便在您调用它时找到结束。如果buffer
很大并且大部分已满,那可能会很慢。行为大致为buffer
的O(N 2 )。随着缓冲区大小的增加,性能会迅速下降。这与你想要的缓冲区完全相反。 (编辑:你在评论中提到你的缓冲区是1GB。我希望上面的代码非常,非常慢,因为缓冲区填充。)
但是,您已经知道完全结尾的位置,以及要复制的字节数。所以这样做:
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是一个很好的整数这一点。)
答案 1 :(得分:1)
每次计算整个字符串的长度!这意味着整个不断增长的字符串需要通过处理器进行洗牌。这样做大致是最坏的情况!偶尔将字符串写入文件会更好 。此外,您应该跟踪上一个写入位置并将字符串附加到那里:
size_t size = sprintf(rowInLog + rowInLogSize, "%d,%d,%d,%d,%d\n", a, b, c, d, e);
rowInLogSize += size;