为了账号安全,请及时绑定邮箱和手机立即绑定

如何快速解析C ++中以空格分隔的浮点数?

如何快速解析C ++中以空格分隔的浮点数?

C++
大话西游666 2019-08-27 13:45:28
如何快速解析C ++中以空格分隔的浮点数?我有一个包含数百万行的文件,每行有3个以空格分隔的浮点数。读取文件需要花费大量时间,因此我尝试使用内存映射文件读取它们,但发现问题不在于IO的速度,而在于解析速度。我当前的解析是获取流(称为文件)并执行以下操作float x,y,z;file >> x >> y >> z;Stack Overflow中的某些人建议使用Boost.Spirit,但我找不到任何简单的教程来解释如何使用它。我正在尝试找到一种简单有效的方法来解析看起来像这样的行:"134.32 3545.87 3425"我真的很感激一些帮助。我想用strtok来分割它,但我不知道如何将字符串转换为浮点数,我不太确定它是最好的方法。我不介意解决方案是否会提升。我不介意它是不是有史以来最有效的解决方案,但我确信它可以加倍速度。提前致谢。
查看完整描述

3 回答

?
牛魔王的故事

TA贡献1830条经验 获得超3个赞

如果转换是瓶颈(这很可能),您应该首先使用标准中的不同可能性。从逻辑上讲,人们会期望它们非常接近,但实际上,它们并不总是:

  • 你已经确定std::ifstream太慢了。

  • 将内存映射数据转换为a std::istringstream 几乎肯定不是一个好的解决方案; 您首先必须创建一个字符串,它将复制所有数据。

  • 编写自己streambuf的内容直接从内存中读取,无需复制(或使用已弃用的std::istrstream)可能是一种解决方案,但如果问题确实是转换......这仍然使用相同的转换例程。

  • 您可以随时尝试fscanf,或scanf在您的内存映射流上。根据实现,它们可能比各种istream实现更快。

  • 可能比任何这些都要快strtod。无需为此进行标记:strtod跳过前导空格(包括'\n'),并且具有out参数,其中放置第一个未读取的字符的地址。结束条件有点棘手,你的循环可能看起来有点像:

    char * begin; //设置为指向mmap的数据...
                    //你还必须安排一个'\ 0'
                    //跟踪数据 这可能是
                    //最困难的问题
    char * end;
    errno = 0;
    double tmp = strtod(开始,结束);
    while(errno == 0 && end!= begin){
        //用tmp做任何事情......
        begin = end;
        tmp = strtod(开始,结束);
    }

如果这些都不够快,您将不得不考虑实际数据。它可能有一些额外的约束,这意味着你可以编写一个比一般更快的转换例程; 例如strtod,必须处理固定和科学,即使有17位有效数字,它必须100%准确。它还必须是特定于语言环境的。所有这些都增加了复杂性,这意味着需要添加代码来执行。但要注意:编写一个有效且正确的转换例程,即使对于一组有限的输入,也是非常重要的; 你真的必须知道你在做什么。

编辑:

出于好奇,我进行了一些测试。除了前面提到的解决方案之外,我还编写了一个简单的自定义转换器,它只处理固定点(不科学),小数点后最多五位数,小数点前的值必须符合int

doubleconvert( char const* source, char const** endPtr ){
    char* end;
    int left = strtol( source, &end, 10 );
    double results = left;
    if ( *end == '.' ) {
        char* start = end + 1;
        int right = strtol( start, &end, 10 );
        static double const fracMult[] 
            = { 0.0, 0.1, 0.01, 0.001, 0.0001, 0.00001 };
        results += right * fracMult[ end - start ];
    }
    if ( endPtr != nullptr ) {
        *endPtr = end;
    }
    return results;}

(如果你真的使用它,你肯定应该添加一些错误处理。这只是为了实验目的而迅速被淘汰,读取我生成的测试文件,没有 别的。)

接口正好是strtod简化编码的接口。

我在两个环境中运行基准测试(在不同的机器上,所以任何时候的绝对值都不相关)。我得到了以下结果:

在Windows 7下,使用VC 11(/ O2)编译:

Testing Using fstream directly (5 iterations)...
    6.3528e+006 microseconds per iterationTesting Using fscan directly (5 iterations)...
    685800 microseconds per iterationTesting Using strtod (5 iterations)...
    597000 microseconds per iterationTesting Using manual (5 iterations)...
    269600 microseconds per iteration

在Linux 2.6.18下,使用g ++ 4.4.2(-O2,IIRC)编译:

Testing Using fstream directly (5 iterations)...
    784000 microseconds per iterationTesting Using fscanf directly (5 iterations)...
    526000 microseconds per iterationTesting Using strtod (5 iterations)...
    382000 microseconds per iterationTesting Using strtof (5 iterations)...
    360000 microseconds per iterationTesting Using manual (5 iterations)...
    186000 microseconds per iteration

在所有情况下,我正在读取554000行,每行有3个随机生成的浮点数[0...10000)

最引人注目的是之间的巨大的差异 fstreamfscanWindows下(与之间的差异相对较小fscanstrtod)。第二件事是两个平台上简单的自定义转换功能获得了多少。必要的错误处理会使其减慢一点,但差异仍然很大。我期待一些改进,因为它不能处理标准转换例程所做的很多事情(比如科学格式,非常非常小的数字,Inf和NaN,i18n等),但不是这么多。


查看完整回答
反对 回复 2019-08-27
?
紫衣仙女

TA贡献1839条经验 获得超15个赞

在开始之前,请确认这是应用程序的慢速部分并获得测试工具,以便您可以衡量改进。

boost::spirit在我看来,这对你来说太过分了。尝试fscanf

FILE* f = fopen("yourfile");if (NULL == f) {
   printf("Failed to open 'yourfile'");
   return;}float x,y,z;int nItemsRead = fscanf(f,"%f %f %f\n", &x, &y, &z);if (3 != nItemsRead) {
   printf("Oh dear, items aren't in the right format.\n");
   return;}


查看完整回答
反对 回复 2019-08-27
  • 3 回答
  • 0 关注
  • 673 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信