【问题标题】:c++ stringstream is too slow, how to speed up? [duplicate]c++ stringstream 太慢了,怎么提速? [复制]
【发布时间】:2011-04-29 10:26:09
【问题描述】:

可能重复:
Fastest way to read numerical values from text file in C++ (double in this case)

#include <ctime>
#include <cstdlib>
#include <string>
#include <sstream>
#include <iostream>
#include <limits>

using namespace std;

static const double NAN_D = numeric_limits<double>::quiet_NaN();

void die(const char *msg, const char *info)
{
    cerr << "** error: " << msg << " \"" << info << '\"';
    exit(1);
}

double str2dou1(const string &str)
{
    if (str.empty() || str[0]=='?') return NAN_D;
    const char *c_str = str.c_str();
    char *err;
    double x = strtod(c_str, &err);
    if (*err != 0) die("unrecognized numeric data", c_str);
    return x;
}

static istringstream string_to_type_stream;

double str2dou2(const string &str)
{
    if (str.empty() || str[0]=='?') return NAN_D;
    string_to_type_stream.clear();
    string_to_type_stream.str(str);
    double x = 0.0;
    if ((string_to_type_stream >> x).fail())
        die("unrecognized numeric data", str.c_str());
    return x;
}

int main()
{
    string str("12345.6789");

    clock_t tStart, tEnd;

    cout << "strtod: ";
    tStart=clock();

    for (int i=0; i<1000000; ++i)
        double x = str2dou1(str);

    tEnd=clock();
    cout << tEnd-tStart << endl;

    cout << "sstream: ";
    tStart=clock();

    for (int i=0; i<1000000; ++i)
        double x = str2dou2(str);

    tEnd=clock();
    cout << tEnd-tStart << endl;

    return 0;
}

strtod:405
流:1389

更新:删除下划线,环境:win7+vc10

【问题讨论】:

  • 尝试使用 boost::spirit 代替。
  • 那些双下划线名称在用户编写的代码中是非法的。如果 stringstreams 对你来说太慢 - 你有答案 - 使用 strtod。字符串流主要是为了方便和类型安全,而不是速度。
  • 流将收集输入,然后最终调用 strtold 进行转换。让它变得更快!
  • 它是哪个编译器?也许 STL 的 stlport 实现会比附带的更快(但不要指望击败 strtod,这是不可能的)。
  • @unapersson 双下划线名字是从别处抄来的,懒得修改

标签: c++ performance parsing


【解决方案1】:

C/C++ 文本转数字格式非常慢。流非常慢,但即使是 C 数字解析也很慢,因为很难将其正确到最后一个精度位。

在一个阅读速度很重要并且已知数据最多包含三个十进制数字且没有科学记数法的生产应用程序中,我通过手动编码一个仅处理符号、整数部分和任何数字的浮动解析函数获得了巨大的改进小数位数(“巨大”是指与 strtod 相比快 10 倍)。

如果你不需要指数并且这个函数的精度足够,这是一个类似于我当时写的解析器的代码。在我的 PC 上,它现在比 strtod 快 6.8 倍,比 sstream 快 22.6 倍。

double parseFloat(const std::string& input)
{
    const char *p = input.c_str();
    if (!*p || *p == '?')
        return NAN_D;
    int s = 1;
    while (*p == ' ') p++;

    if (*p == '-') {
        s = -1; p++;
    }

    double acc = 0;
    while (*p >= '0' && *p <= '9')
        acc = acc * 10 + *p++ - '0';

    if (*p == '.') {
        double k = 0.1;
        p++;
        while (*p >= '0' && *p <= '9') {
            acc += (*p++ - '0') * k;
            k *= 0.1;
        }
    }
    if (*p) die("Invalid numeric format");
    return s * acc;
}

【讨论】:

    【解决方案2】:

    字符串流慢。相当缓慢。如果您正在编写任何对大型数据集的性能至关重要的内容(例如在游戏期间更改关卡后加载资产),请不要使用字符串流。我建议使用老式的 c 库解析函数来提高性能,尽管我不能说它们与 boost spirit 相比如何。

    但是,与 c 库函数相比,字符串流非常优雅、可读且可靠,因此如果您所做的不是性能关键,我建议坚持使用流。

    【讨论】:

      【解决方案3】:

      一般来说,如果你需要速度,可以考虑这个库:

      http://www.fastformat.org/

      (不过,我不确定它是否包含将字符串或流转换为其他类型的函数,因此它可能无法回答您当前的示例)。

      为了记录,请注意您在这里将苹果与橙子进行比较。 strtod() 是一个具有单一目的的简单函数(将字符串转换为双精度),而 stringstream 是一种更复杂的格式化机制,远未针对特定目的进行优化。更公平的比较是将 stringstream 与 sprintf/sscanf 函数行进行比较,这将比 strtod() 慢,但仍比 stringstream 快。我不确定是什么让 stringstream 的设计比 sprintf/sscanf 慢,但似乎就是这样。

      【讨论】:

      • 为什么 STL 比 fastformat 慢
      • @hjbreg,因为它必须支持语言环境。
      • @hjbreg:有几个原因。部分原因可能与流设计考虑和未优化的实现有关。另一个原因是 STL 的灵活性,它可能包括语言环境支持和对 IO 操纵器的支持(我不确定 fastformat 是否有这些或等效的东西)。
      • strtod 并不是特别简单。它处理语言环境(例如千位分隔符与小数点,以及挑剔千位分隔符),并且非常仔细地进行舍入。使 iostreams 变慢的是虚函数调度的疯狂程度,以及导致实现者完全放弃任何优化的正确性问题。
      • @hjbreg: fastformat 支持语言环境(除了大量的尖叫宣传)
      【解决方案4】:

      您是否考虑过使用 boost 中的lexical_cast

      http://www.boost.org/doc/libs/1_46_1/libs/conversion/lexical_cast.htm

      编辑:顺便说一句,clear() 应该是多余的。

      【讨论】:

      • +1 for lexical_cast 但由于它在下面使用字符串流,因此不会更快。
      • 在我的测试中 lexical_cast 太慢了
      • @Let_Me_Be: lexical_cast 是使用字符串流实现的 ;-)
      • boost lexical_cast 比 std::stringstream 快得多。这是性能比较:boost.org/doc/libs/1_49_0/doc/html/boost_lexical_cast/…
      • @JasperBekkers 仅在备用情况下。它专门用于许多原始类型
      猜你喜欢
      • 1970-01-01
      • 2023-03-23
      • 1970-01-01
      • 2018-06-10
      • 2014-10-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多