我编写了一个波前 Obj 解析器类,将 obj 模型导入到我的 OpenGL 项目中。我在调试模式下测试了这个类,发现它慢得让人难以忍受。
代码有效,我进行了明显的调整,以确保它在合理实用的范围内尽可能高效。
尽管如此,加载我的测试文件,一个 12mb 的 obj 文件,运行大约 330,000 行文本,需要一分钟才能解析。
沮丧,我有一个谷歌,果然,我不是第一个遇到这个问题的人。
这个在 gamedev.net 上发布查询的人只是在发布模式下运行他的算法,在 Visual Studio IDE 和 whammo 之外运行,性能可以接受。这也对我有用,我的 ~70 秒减少到 ~3 秒。
我对算法进行了一些分析,瓶颈在于对 std::getline 的调用,如下所示:
sstream >> sToken;
其中 sstream 是 std::stringstream 而 sToken 是 std::string (预先保留空间)。
问题
为什么 IDE 在运行我的解析算法时速度如此之慢(即使在发布模式下) - 在通过 IDE 运行代码时我能做些什么来加快速度(F5 - 运行项目)?这使得调试变得非常缓慢。IDE 是否将代码/挂钩注入到可执行文件中以通过 IDE 运行,或者这是否可以归结为缓存未命中或其他原因?
优化
我对文件进行两次传递,在传递一次时,我只计算令牌类型 - 这样我就可以保留空间(而不是迭代地增长存储顶点、法线、texcoords、面等的向量)
sLineBuffer.reserve( 100 );
sToken.reserve(10);
while( sstream.good() )
{
sstream >> sToken;
getline( sstream, sLineBuffer );
if( sToken.compare("f") == 0 )
nFaces ++;
else if( sToken.compare("v") == 0 )
nVertices ++;
else if( sToken.compare("vn") == 0 )
nNormals ++;
else if( sToken.compare("vt") == 0 )
nTextures ++;
else if( sToken.compare("g") == 0 )
nGroups ++;
}
m_Vertices.reserve( nVertices );
m_Normals.reserve( nNormals );
m_TexCoords.reserve( nTextures );
m_Faces.reserve( nFaces );
m_Groups.reserve( nGroups );
第一次通过的成本很低(在调试模式下约为 8 秒,或在 IDE 外的发布模式下约为 0.3 秒),并且效率节省很大(将解析时间从调试模式下的 ~180 秒减少到 ~60 秒)。
我还将整个文件读入一个字符串流,以便将磁盘访问排除在等式之外:
// Read entire file from disk into memory
fstream stream;
stringstream sstream;
stream.open( m_sFilename.c_str(), std::ios::in );
sstream << stream.rdbuf();
stream.close();
此外,在可能的情况下,在整个算法中,我尝试提前为 std::strings 保留空间,以便它们不会基于每个字符调整大小:
sLineBuffer.reserve( 100 );
sToken.reserve(10); // etc