首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用getline的慢速istringstream

使用getline的慢速istringstream
EN

Stack Overflow用户
提问于 2013-01-14 17:58:55
回答 2查看 2.5K关注 0票数 1

我有一个Wavefront .obj文件解析器,它使用getline和stringstream解析数据。起初,当模型很小的时候,没有问题,但是现在,当我尝试加载一个有大约207000行的模型时,只有在第一次计算所有元素的时候,在高端PC上需要花费大量的时间(大约4.7秒),第二次需要半分钟。另一方面,Blender仅需2秒左右即可加载整个模型。我使用的是visual studio 2012,目前处于调试模式。

我的元素计数代码如下所示:

代码语言:javascript
复制
istringstream input(obj);
string line;
while (getline(input, line)) {
    if (line.find("# ") != string::npos) {
        // Comments.
    }
    else if (line.find("f ") != string::npos) {
        faces++;
    }
    else if (line.find("v ") != string::npos) {
        vertices += 3;
    }
    else if (line.find("vn ") != string::npos) {
        normals += 3;
    }
    else if (line.find("vt ") != string::npos) {
        uvCoordinates += 2;
    }
    else if (line.find("o ") != string::npos) {
        // Count here, if needed.
    }
}

实际加载整个数据需要大约30秒的代码:

代码语言:javascript
复制
istringstream input(obj);
string line;
if (faces.capacity() > UINT_MAX / 3) {
    LOGE("Model cannot have more faces than: %d", UINT_MAX / 3);
    return false;
}
while (getline(input, line)) {
    vector<string> arr = stringSplit(line, ' ');
    string param = arr[0];
    int params = arr.size();
    if (line.length() == 0) {
        continue;
    }

    if (arr[0] == "v") { // Vertices.
        vertices.push_back(stringToFloat(arr[1].c_str()));
        vertices.push_back(stringToFloat(arr[2].c_str()));
        vertices.push_back(stringToFloat(arr[3].c_str()));
    }
    else if (arr[0] == "vn") { // Normals.
        normals.push_back(stringToFloat(arr[1].c_str()));
        normals.push_back(stringToFloat(arr[2].c_str()));
        normals.push_back(stringToFloat(arr[3].c_str()));
    }
    else if (arr[0] == "f") { // Faces.
        if (params < 4) {
            //LOGI("LINE: %s", line.c_str());
            continue;
        }
        else if (params > 4) {
            LOGI("Line: %s", line.c_str());
            LOGE("Obj models must only contain triangulated faces.");
            return false;
        }
        Face face;
        parseFace(face, line);
        faces.push_back(face);
    }
    else if (arr[0] == "vt") { // UV coordinates.
        uvCoordinates.push_back(stringToFloat(arr[1].c_str()));
        uvCoordinates.push_back(stringToFloat(arr[2].c_str()));
    }
    else if (arr[0] == "mtllib") { // Material.
        material = arr[1];
    }
    else if (arr[0] == "o") { // Sub-model.
        // Separate models here, if needed.
    }
}

obj变量是包含整个文件内容的字符串。删除第一个循环内部的所有内容不会对时间影响产生任何影响。对如何优化这个有什么想法吗?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2013-01-14 19:23:43

第零号,侧写!

首先,如果您使用istringstream只是为了调用getline()从字符串中取出一行,那么可以创建您自己的函数,该函数将简单地向前搜索下一个'\n'并给出字符串。这样你就可以避免很多开销了。

第二,避免多通道算法。为什么需要提前清点对象?

第三,避免不必要的重复内存分配/构造和释放/销毁。

arr变量移出循环。修改stringSplit()以拆分成现有向量的现有元素,以避免重新分配向量和其中的字符串:

代码语言:javascript
复制
vector<string> arr = stringSplit(line, ' ');

除非您正在修改向量的元素,并且在这里确实需要字符串的副本,否则请避免复制,而使用对常量字符串的引用:

代码语言:javascript
复制
string param = arr[0];

这里,不是变量、初始化、push_back(),而是首先调整向量的大小,然后在它的最后一个元素上调用parseFace()

代码语言:javascript
复制
Face face;
parseFace(face, line);
faces.push_back(face);

避免这些长的if/else if链,或者至少对它们进行排序,以便最频繁的实体位于链的顶部。更好的做法是,仅在switch-case块中使用第一个字母和完全比较进行切换。编译器可以将switch语句优化为平衡决策树或跳转表。

代码语言:javascript
复制
if (arr[0] == "v") { // Vertices.
//...
}
else if (arr[0] == "vn") { // Normals.
//...
}
else if (arr[0] == "f") { // Faces.
//...
}
else if (arr[0] == "vt") { // UV coordinates.
//...
}
else if (arr[0] == "mtllib") { // Material.
//...
}
else if (arr[0] == "o") { // Sub-model.
//...
}

编辑:

至于第一次传递,如果你没有它,并且向量在运行中调整大小,它会对性能产生什么影响?

如果你提前在你的向量中预留空间,比如说,1000个面,1000条法线,3000个顶点(假设这些实体之间的比例是1:1:3 )等,那么你的向量将增长得更快,并且与从空向量开始相比,将避免调整大小的大部分复制开销。

至于面孔,我的意思是改变这个:

代码语言:javascript
复制
Face face;
parseFace(face, line);
faces.push_back(face);

进入这个(如果你保持apprach的push_back()风格):

代码语言:javascript
复制
std::size_t const faces_size = faces.size();
faces.resize(faces_size + 1);
parseFace(faces.back());

在所有情况下,请确保

  1. benchmark至少运行3次,并进行一项旨在提高things
  2. benchmark

的更改

票数 1
EN

Stack Overflow用户

发布于 2013-01-14 18:28:49

首先,尝试一个发布版本。调试版本应该是可调试的,而不是快速的。

另一件事是,使用stringstream和getline会导致大量的复制和堆分配。为了获得最佳性能,您可以尝试仅使用索引遍历字符串,从原始字符串本身解析内容,而不是从提取的片段中解析内容,等等。当然,您需要替换标准库中的一些功能。

票数 4
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/14316264

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档