0

我必须在 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 小时内都没有完成。还有其他一些基本缺陷吗?

4

2 回答 2

3

我怀疑你的答案就在这里:

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

strcat()函数将在您调用它时扫描整个buffer以查找结尾。如果buffer它很大并且大部分都是满的,那可能会很慢。行为在 的大小上大致为 O(N 2 ) buffer。随着缓冲区大小的增加,您的性能将迅速下降。这与您希望从缓冲区中获得的内容几乎相反。(编辑:你在评论中提到你的缓冲区是 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;
}

为您的 5 个整数行设置maximumLineSize一个合理的值,例如 60。(每个整数 10 个字节,包括符号加上 5 个字节的逗号和换行符是 55,所以 60 是一个很好的整数。)

于 2013-12-22T19:54:34.127 回答
1

您每次都在计算整个字符串的长度!这意味着整个和不断增长的字符串需要通过处理器进行洗牌。这样做粗略地说是最坏的情况!偶尔将字符串写入文件会更好。此外,您应该跟踪最后写入位置并在此处附加字符串:

size_t size = sprintf(rowInLog + rowInLogSize, "%d,%d,%d,%d,%d\n", a, b, c, d, e);
rowInLogSize += size;
于 2013-12-22T19:59:28.693 回答