1

我生成一组数据文件。由于文件应该是可读的,它们是文本文件(与二进制文件相反)。

为了向我的文件输出信息,我使用了非常舒服的std::ofstream对象。

一开始,当要导出的数据较小时,写入文件所需的时间并不明显。但是,由于要导出的信息已经积累,现在大约需要 5 分钟才能生成它们。

当我开始被等待所困扰时,我的问题很明显:请问有没有更快的替代 std::ofstream的方法?如果有更快的替代方案,是否值得重写我的应用程序?换句话说,节省的时间可以是 +50%吗?谢谢你。


更新:

我被要求向您展示生成上述文件的代码,所以您在这里 -最耗时的循环

ofstream fout;
fout.open(strngCollectiveSourceFileName,ios::out);

fout << "#include \"StdAfx.h\"" << endl;
fout << "#include \"Debug.h\"" << endl;
fout << "#include \"glm.hpp\"" << endl;
fout << "#include \"" << strngCollectiveHeaderFileName.substr( strngCollectiveHeaderFileName.rfind(TEXT("\\")) + 1) << "\"" << endl << endl;

fout << "using namespace glm;" << endl << endl << endl;


for (unsigned int nSprite = 0; nSprite < vpTilesetSprites.size(); nSprite++ )
{
    for(unsigned int nFrameSet = 0; nFrameSet < vpTilesetSprites[nSprite]->vpFrameSets.size(); nFrameSet++)
    {

        // display index definition
        fout << "// Index Definition: " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetLongDescription() << "\n";
        string strngIndexSignature = strngIndexDefinitionSignature;
        strngIndexSignature.replace(strngIndexSignature.find(TEXT("#aIndexArrayName#")), strlen(TEXT("#aIndexArrayName#")), TEXT("a") + vpTilesetSprites[nSprite]->GetObjectName() + vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetFrameSetName() + TEXT("IndexData") );
        strngIndexSignature.replace(strngIndexSignature.find(TEXT("#ClassName#")), strlen(TEXT("#ClassName#")), strngCollectiveArrayClassName );        
        fout << strngIndexSignature << "[4] = {0, 1, 2, 3};\t\t" << "// " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetShortDescription() << ": Index Definition\n\n";


        // display vertex definition
        fout << "// Vertex Definition: " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetLongDescription() << "\n";

        string strngVertexSignature = strngVertexDefinitionSignature;
        strngVertexSignature.replace(strngVertexSignature.find(TEXT("#aVertexArrayName#")), strlen(TEXT("#aVertexArrayName#")), TEXT("a") + vpTilesetSprites[nSprite]->GetObjectName() + vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetFrameSetName() + TEXT("VertexData") );
        strngVertexSignature.replace(strngVertexSignature.find(TEXT("#ClassName#")), strlen(TEXT("#ClassName#")), strngCollectiveArrayClassName );
        fout << strngVertexSignature << "[" << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetFramesCount() << "] =\n";
        fout << "{\n";

        for (int nFrameNo = 0; nFrameNo < vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetFramesCount(); nFrameNo++)
        {
            fout << "\t" << "{{ vec4(" << fixed << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[0].vPosition.fx << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[0].vPosition.fy << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[0].vPosition.fz << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[0].vPosition.fw << "f), vec2(" << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[0].vTextureUV.fu << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[0].vTextureUV.fv << "f) },  // " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetShortDescription() << " vertex 1: vec4(x, y, z, w), vec2(u, v) \n";
            fout << "\t" << " { vec4(" << fixed << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[1].vPosition.fx << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[1].vPosition.fy << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[1].vPosition.fz << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[1].vPosition.fw << "f), vec2(" << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[1].vTextureUV.fu << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[1].vTextureUV.fv << "f) },  // " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetShortDescription() << " vertex 2: vec4(x, y, z, w), vec2(u, v) \n";
            fout << "\t" << " { vec4(" << fixed << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[2].vPosition.fx << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[2].vPosition.fy << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[2].vPosition.fz << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[2].vPosition.fw << "f), vec2(" << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[2].vTextureUV.fu << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[2].vTextureUV.fv << "f) },  // " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetShortDescription() << " vertex 3: vec4(x, y, z, w), vec2(u, v) \n";
            fout << "\t" << " { vec4(" << fixed << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[3].vPosition.fx << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[3].vPosition.fy << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[3].vPosition.fz << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[3].vPosition.fw << "f), vec2(" << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[3].vTextureUV.fu << "f, " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->vpFrames[nFrameNo]->aVertices[3].vTextureUV.fv << "f) }},  // " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetShortDescription() << " vertex 4: vec4(x, y, z, w), vec2(u, v) \n\n";
        }
        fout << "};\n\n\n\n";
    }
}

fout.close();
4

5 回答 5

4

如果您不想使用 C 文件 I/O,那么您可以尝试一下;快速格式化。查看比较以获取更多信息。

于 2011-06-08T02:48:11.493 回答
3

假设您以足够大的块执行此操作,则直接调用 write() 可能会更快;也就是说,您最大的瓶颈很可能与 std::ofstream 没有任何直接关系。最明显的事情是确保您没有使用 std::endl (因为频繁刷新流会降低性能)。除此之外,我建议分析您的应用程序以查看它实际花费的时间。

于 2011-06-08T02:47:09.797 回答
3

vpTilesetSprites又是如何vpTilesetSprites[nSprite]存储的?它们是用列表还是数组实现的?有很多对它们的索引访问,如果它们是类似列表的结构,您将花费大量额外的时间来跟踪不必要的指针。Ed S. 的评论是正确的:提供长索引的临时变量和换行符可以使其更易于阅读,也可能更快:

fout << "// Index Definition: " << vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetLongDescription() << "\n";
string strngIndexSignature = strngIndexDefinitionSignature;
strngIndexSignature.replace(strngIndexSignature.find(TEXT("#aIndexArrayName#")), strlen(TEXT("#aIndexArrayName#")), TEXT("a") + vpTilesetSprites[nSprite]->GetObjectName() + vpTilesetSprites[nSprite]->vpFrameSets[nFrameSet]->GetFrameSetName() + TEXT("IndexData") );
strngIndexSignature.replace(strngIndexSignature.find(TEXT("#ClassName#")), strlen(TEXT("#ClassName#")), strngCollectiveArrayClassName );        

对比

string idxsig = strngIndexDefinitionSignature;
sprite sp = vpTilesetSprites[nSprite];
frameset fs = sp->vpFrameSets[nFrameSet];

fout << "// Index Definition: " << fs->GetLongDescription() << "\n";
idxsig.replace(idxsig.find(TEXT("#aIndexArrayName#")), strlen(TEXT("#aIndexArrayName#")),
    TEXT("a") + sp->GetObjectName() + fs->getFrameSetName() + TEXT("IndexData"));
idxsig.replace(idxsig.find(TEXT("#ClassName#")), strlen(TEXT("#ClassName#")),
    strngCollectiveArrayClassName);

但是,更大的问题是如何使用字符串作为模板;您正在一遍又一遍地寻找给定的文本字符串(并在每次需要时计算针线的长度!) 。

考虑一下:您正在执行查找和替换操作nSprite*nFrameSet次。每次通过,这个循环:

  • 复制strngIndexDefinitionSignature
  • 连接静态和动态字符串时创建四个临时字符串对象
  • 计算strlen(TEXT("#ClassName#"))
  • 计算strlen(TEXT("#aIndexArrayName#"))
  • 找到两者的起点
  • 用新文本替换两个文本

这只是循环的前四行。

你可以strngIndexDefinitionSignature格式字符串替换你的吗?我认为它目前看起来像这样:

"flubber #aIndexArrayName# { blubber } #ClassName# blorp"

如果你像这样重写它:

"flubber a %s%sIndexData { blubber } %s blorp"

然后你的两个查找和替换行可以替换为:

sprintf(out, index_def_sig, sp->GetObjectName(), fs->getFrameSetName(),
    strngCollectiveArrayClassName);

这将删除两个find()操作,两个replace()操作,创建和销毁四个临时字符串对象,一个被两个replace()调用迅速覆盖的字符串副本,以及两个strlen()每次返回相同结果的操作(但实际上并不需要)。

<<然后,您可以像往常一样输出您的字符串。或者,您可以更改sprintf(3)fprintf(3),甚至避免使用临时 C 字符串。

于 2011-06-08T03:31:41.563 回答
1

的性能ostream可能不是您的实际问题;我建议使用分析器来确定你真正的瓶颈在哪里。如果ostream结果是您的实际问题,您可以下拉<cstdio>并使用fprintf(FILE*, const char*, ...)格式化输出到文件句柄。

于 2011-06-08T02:48:17.600 回答
1

最佳答案取决于您生成的文本类型以及生成方式。C++ 流可能很慢,但这主要是因为它们还可以为您做更多事情,例如依赖于语言环境的格式化等。

ostream::write通过绕过某些格式(例如)或将字符直接写入流缓冲区( ),您可能会发现流的加速streambuf::sputn。有时增加相关流缓冲区的缓冲区大小会有所帮助(通过streambuf::pubsetbuf)。

如果这还不够好,您可能想尝试 C 风格的 stdio 文件,例如fopen,fprintf等。如果您不习惯这种方法,则需要一点时间来适应文本的格式,但是性能通常相当不错。

为了获得绝对的最佳性能,您通常必须使用特定于操作系统的例程。有时直接的低级文件例程明显优于 C stdio,但有时则不然——例如,我看到有人说 Win32 上的 WriteFile 是 Windows 上最快的方法,而一些谷歌点击报告它比标准输出。另一种方法可能是内存映射文件,例如。mmap+ msync- 这基本上将您的系统内存用作磁盘并将实际数据以大块的形式写入磁盘,这可能接近最佳状态。但是,如果由于某种原因导致中途崩溃,您将面临丢失所有数据的风险,这对您来说可能是也可能不是问题。

于 2011-06-08T02:53:31.757 回答