我生成一组数据文件。由于文件应该是可读的,因此它们文本文件(与二进制文件相对)。
为了向我的文件输出信息,我使用了非常舒服的 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();
答案 0 :(得分:4)
如果您不想使用C文件I / O,那么您可以尝试一下; FastFormat。查看比较以获取更多信息。
答案 1 :(得分:3)
假设你在足够大的块中执行它,直接调用write()可能会更快;也就是说,你最大的瓶颈更可能与std :: ofstream没有任何直接关系。最明显的是确保你没有使用std :: endl(因为频繁刷新流会杀死性能)。除此之外,我建议您分析您的应用,看看它实际花费的时间。
答案 2 :(得分: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 );
VS
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);
但是,更大的问题是你如何使用字符串作为模板;您正在寻找给定的文本字符串(并且每次需要时计算 needle 字符串的长度!)一遍又一遍。
请考虑以下事项:您正在执行查找和替换操作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字符串。
答案 3 :(得分:1)
ostream
的表现可能不是您的实际问题;我建议使用分析器来确定您的真正瓶颈在哪里。如果ostream
原来是您的实际问题,您可以下拉到<cstdio>
并使用fprintf(FILE*, const char*, ...)
格式化输出到文件句柄。
答案 4 :(得分:1)
最佳答案取决于您生成的文本类型以及生成方式。 C ++流可能很慢,但主要是因为它们还可以为您做更多的事情,例如依赖于语言环境的格式化等等。
您可以通过绕过某些格式(例如ostream::write
)或直接将字符写入streambuf(streambuf::sputn
)来查找带流的加速。有时增加相关streambuf上的缓冲区大小有助于(通过streambuf::pubsetbuf
)。
如果这还不够好,您可能想尝试C风格的stdio文件,例如fopen
,fprintf
等。需要一点时间来习惯文本的方式如果您不习惯该方法,则会被格式化,但性能通常非常好。
对于绝对顶级性能,您通常必须转到特定于操作系统的例程。有时直接的低级文件例程明显优于C stdio,但有时候没有 - 例如,我看到有人说Win32上的WriteFile是Windows上最快的方法,而一些谷歌命中报告它比STDIO。另一种方法可能是内存映射文件,例如。 mmap
+ msync
- 这主要使用您的系统内存作为磁盘,并将实际数据以大块写入磁盘,这可能接近最佳状态。但是,如果由于某种原因导致中途崩溃,您可能会丢失所有数据,这对您来说可能是也可能不是问题。