首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >boost::tokenizer vs boost::split

boost::tokenizer vs boost::split
EN

Stack Overflow用户
提问于 2011-10-28 22:44:11
回答 4查看 38.6K关注 0票数 32

我正在尝试将每个“^”字符上的c++字符串解析为向量标记。我一直使用boost::split方法,但我现在正在编写性能关键型代码,我想知道哪一个能提供更好的性能。

例如:

代码语言:javascript
复制
string message = "A^B^C^D";
vector<string> tokens;
boost::split(tokens, message, boost::is_any_of("^"));

代码语言:javascript
复制
boost::char_separator<char> sep("^");
boost::tokenizer<boost::char_separator<char> > tokens(text, sep);

哪一个会提供更好的性能?为什么?

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2011-12-31 16:46:46

最好的选择取决于几个因素。如果您只需要扫描一次令牌,那么在运行时和空间性能方面,boost::tokenizer都是一个很好的选择(这些令牌矢量可能会占用大量空间,具体取决于输入数据)。

如果你打算经常扫描令牌,或者需要一个具有高效随机访问的向量,那么boost::split成一个向量可能是更好的选择。

例如,在"A^B^C^...^Z“输入字符串中,标记的长度为1个字节,boost::split/vector<string>方法将至少占用2*N-1个字节。根据大多数STL实现中字符串的存储方式,您可以计算出它需要8倍以上的时间。将这些字符串存储在向量中在内存和时间方面是非常昂贵的。

我在我的机器上运行了一个快速测试,一个类似的模式,包含1000万个令牌,如下所示:

  • boost::split = 2.5s~620MB = 0.9s和split

如果您只是对令牌进行一次扫描,那么显然令牌器更好。但是,如果您想在应用程序的生命周期内将其分解到一个结构中进行重用,那么使用一个令牌向量可能更好。

如果你想走矢量路线,那么我建议不要使用vector<string>,而是使用string::迭代器的矢量。只需分解成一对迭代器,并保留您的大字符串的标记以供参考。例如:

代码语言:javascript
复制
using namespace std;
vector<pair<string::const_iterator,string::const_iterator> > tokens;
boost::split(tokens, s, boost::is_any_of("^"));
for(auto beg=tokens.begin(); beg!=tokens.end();++beg){
   cout << string(beg->first,beg->second) << endl;
}

这个改进版本在同一台服务器上运行1.6s和390MB并进行测试。最棒的是,这个向量的内存开销与令牌的数量成线性关系--与令牌的长度无关,而是由一个std::vector<string>存储每个令牌。

票数 43
EN

Stack Overflow用户

发布于 2012-12-23 00:35:48

我发现使用clang++ -O3 -std=c++11 -stdlib=libc++的结果截然不同。

首先,我将一个大约有47万个单词的文本文件提取到一个巨大的字符串中,其中用逗号分隔,没有换行符,如下所示:

代码语言:javascript
复制
path const inputPath("input.txt");

filebuf buf;
buf.open(inputPath.string(),ios::in);
if (!buf.is_open())
    return cerr << "can't open" << endl, 1;

string str(filesystem::file_size(inputPath),'\0');
buf.sgetn(&str[0], str.size());
buf.close();

然后我运行了各种定时测试,将结果存储到运行之间清除的预定大小的向量中,例如,

代码语言:javascript
复制
void vectorStorage(string const& str)
{
    static size_t const expectedSize = 471785;

    vector<string> contents;
    contents.reserve(expectedSize+1);

    ...

    {
        timed _("split is_any_of");
        split(contents, str, is_any_of(","));
    }
    if (expectedSize != contents.size()) throw runtime_error("bad size");
    contents.clear();

    ...
}

作为参考,计时器是这样的:

代码语言:javascript
复制
struct timed
{
    ~timed()
    {
        auto duration = chrono::duration_cast<chrono::duration<double, ratio<1,1000>>>(chrono::high_resolution_clock::now() - start_);

        cout << setw(40) << right << name_ << ": " << duration.count() << " ms" << endl;
    }

    timed(std::string name="") :
        name_(name)
    {}


    chrono::high_resolution_clock::time_point const start_ = chrono::high_resolution_clock::now();
    string const name_;
};

我还记录了一次迭代(无向量)。结果如下:

代码语言:javascript
复制
Vector: 
                              hand-coded: 54.8777 ms
                         split is_any_of: 67.7232 ms
                     split is_from_range: 49.0215 ms
                               tokenizer: 119.37 ms
One iteration:
                               tokenizer: 97.2867 ms
                          split iterator: 26.5444 ms
            split iterator back_inserter: 57.7194 ms
                split iterator char copy: 34.8381 ms

标记器比split慢得多,一次迭代的数字甚至不包括字符串copy:

代码语言:javascript
复制
{
    string word;
    word.reserve(128);

    timed _("tokenizer");
    boost::char_separator<char> sep(",");
    boost::tokenizer<boost::char_separator<char> > tokens(str, sep);

    for (auto range : tokens)
    {}
}

{
    string word;

    timed _("split iterator");
    for (auto it = make_split_iterator(str, token_finder(is_from_range(',', ',')));
         it != decltype(it)(); ++it)
    {
        word = move(copy_range<string>(*it));
    }
}

明确的结论:使用split

票数 13
EN

Stack Overflow用户

发布于 2015-09-22 04:42:39

这可能取决于您的boost版本以及您的功能。

在使用boost::split 1.41.0来处理数千或数十万个较小的字符串(预期少于10个令牌)的某些逻辑中,我们遇到了性能问题。当我通过性能分析器运行代码时,我们发现在boost::split上花费了令人惊讶的39%的时间。

我们尝试了一些简单的“修复”,这些“修复”对性能没有实质性的影响,比如“我们知道每次传递不会超过10个项目,所以将向量预置为10个项目”。

由于我们实际上并不需要向量,并且可以只迭代标记并完成相同的工作,因此我们将代码更改为boost::tokenize,并且相同的代码段减少到运行时的<1%。

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

https://stackoverflow.com/questions/7930796

复制
相关文章

相似问题

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