高效的文件记录方式

时间:2013-12-22 19:50:46

标签: c++ c visual-studio-2008 flush disk-io

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

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;