首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >非常差的boost::lexical_cast性能

非常差的boost::lexical_cast性能
EN

Stack Overflow用户
提问于 2009-08-09 06:17:06
回答 9查看 30.7K关注 0票数 47

Windows SP3。核心2双2.0 GHz。我发现boost::lexical_cast的性能非常慢。想找出加速代码的方法。在Visual/O2 2008上使用/O2优化,并与Java1.6和python2.6.2进行比较,我看到了以下结果。

整数铸造:

代码语言:javascript
复制
c++: 
std::string s ;
for(int i = 0; i < 10000000; ++i)
{
    s = boost::lexical_cast<string>(i);
}

java:
String s = new String();
for(int i = 0; i < 10000000; ++i)
{
    s = new Integer(i).toString();
}

python:
for i in xrange(1,10000000):
    s = str(i)

我看到的时间是

c++:6700毫秒

java: 1178毫秒

python: 6702毫秒

c++和python一样慢,比java慢6倍。

双浇铸:

代码语言:javascript
复制
c++:
std::string s ;
for(int i = 0; i < 10000000; ++i)
{
    s = boost::lexical_cast<string>(d);
}

java:
String s = new String();
for(int i = 0; i < 10000000; ++i)
{
    double d = i*1.0;
    s = new Double(d).toString();
}

python:
for i in xrange(1,10000000):
    d = i*1.0
    s = str(d)

我看到的时间是

c++:56129毫秒

java: 2852毫秒

python: 30780毫秒

因此,对于双c++,它的速度实际上是python的一半,比java解决方案慢20倍!对改进boost::lexical_cast性能有什么想法吗?这是源于糟糕的字符串实现,还是由于使用boost库会导致性能普遍下降10倍。

EN

回答 9

Stack Overflow用户

回答已采纳

发布于 2009-08-09 09:50:55

编辑2012-04-11

雷夫非常正确地评论了词法_cast的性能,提供了一个链接:

cast/playce.html

我现在没有权限来提升1.49,但我确实记得在旧版本上使我的代码更快。所以我猜:

  1. 以下答案仍然有效(如果只是为了学习目的)
  2. 可能在这两个版本之间引入了一个优化(我将搜索)
  3. 这意味着助推效果越来越好

原始答案

我想补充一下巴里和莫蒂的好答案:

一些背景

请记住,Boost是由这个星球上最好的C++开发人员编写的,也是由相同的最好的开发人员编写的。如果lexical_cast是如此错误,有人会用批评或代码黑进库。

我猜你没注意到lexical_cast的真正价值.

比较苹果和橘子。

在Java中,您将整数转换为Java字符串。您将注意到,我所讨论的不是字符数组,也不是用户定义的字符串。你也会注意到,我不是说你的用户定义的整数。我说的是严格的和严格的Java字符串。

在Python中,您或多或少是在做同样的事情。

正如其他帖子所述,您实际上使用的是sprintf和Python等价物(或不太标准的itoa)。

在C++中,您使用的是非常强大的强制转换。在原始速度性能方面不是很强大(如果您想要速度,也许sprintf会更适合),但在可扩展性方面是强大的。

比较苹果。

如果您想比较一个Java Integer.toString方法,那么您应该将它与C sprintf或C++ ostream工具进行比较。

C++流解决方案(在我的g++上)比lexical_cast快6倍,扩展性也差很多:

代码语言:javascript
复制
inline void toString(const int value, std::string & output)
{
   // The largest 32-bit integer is 4294967295, that is 10 chars
   // On the safe side, add 1 for sign, and 1 for trailing zero
   char buffer[12] ;
   sprintf(buffer, "%i", value) ;
   output = buffer ;
}

C sprintf解决方案的速度(在我的g++上)是lexical_cast的8倍,但安全性要低得多:

代码语言:javascript
复制
inline void toString(const int value, char * output)
{
   sprintf(output, "%i", value) ;
}

这两种解决方案都与Java解决方案一样快或更快(根据您的数据)。

比较橘子。

如果您想比较一个C++ lexical_cast,那么您应该将它与这个Java伪代码进行比较:

代码语言:javascript
复制
Source s ;
Target t = Target.fromString(Source(s).toString()) ;

源和目标是任意类型的,包括内置类型(如booleanint ),这在C++中是可能的,因为有模板。

可扩展性?这是个下流词吗?

不,但它有一个众所周知的代价:当由同一个编码器编写时,对特定问题的一般解决方案通常比为其特定问题编写的特定解决方案要慢。

在当前的情况下,在朴素的观点中,lexical_cast将使用流工具将类型A转换为字符串流,然后从该字符串流转换为类型B

这意味着,只要可以将对象输出到流中,并从流中输入,就可以在其上使用lexical_cast,而无需触及任何一行代码。

那么,lexical_cast的用途是什么?

词汇铸造的主要用途如下:

  1. 易于使用(嘿,C++强制转换适用于一切都是值的东西!)
  2. 将其与模板繁重的代码相结合,在这种情况下,您的类型是参数化的,因此您不想处理具体问题,也不想知道类型。
  3. 如果您有基本的模板知识,我将在下面演示,这仍然是相对有效的。

第2点在这里非常重要,因为它意味着我们只有一个接口/函数将一个类型的值转换为另一个类型的相同或相似的值。

这才是你错过的真正要点,而这一点在性能上是需要付出代价的。

但它太血腥了!

如果您想要获得原始的速度性能,请记住您正在处理C++,并且您有很多工具可以有效地处理转换,而且仍然要保持lexical_cast易于使用的特性。

我花了几分钟的时间查看了lexical_cast源代码,并提出了一个可行的解决方案。在C++代码中添加以下代码:

代码语言:javascript
复制
#ifdef SPECIALIZE_BOOST_LEXICAL_CAST_FOR_STRING_AND_INT

namespace boost
{
   template<>
   std::string lexical_cast<std::string, int>(const int &arg)
   {
      // The largest 32-bit integer is 4294967295, that is 10 chars
      // On the safe side, add 1 for sign, and 1 for trailing zero
      char buffer[12] ;
      sprintf(buffer, "%i", arg) ;
      return buffer ;
   }
}

#endif

通过为字符串和ints启用lexical_cast的这种专门化(通过定义宏SPECIALIZE_BOOST_LEXICAL_CAST_FOR_STRING_AND_INT),我的代码在SPECIALIZE_BOOST_LEXICAL_CAST_FOR_STRING_AND_INT编译器上的速度要快5倍,这意味着,根据您的数据,它的性能应该与SPECIALIZE_BOOST_LEXICAL_CAST_FOR_STRING_AND_INT相似。

我花了10分钟看了boost代码,并编写了一个远程高效和正确的32位版本。通过一些工作,它可能会更快、更安全(例如,如果我们可以直接写访问std::string内部缓冲区,就可以避免临时外部缓冲区)。

票数 77
EN

Stack Overflow用户

发布于 2009-08-09 09:43:29

您可以将lexical_cast专门用于intdouble类型。在您的专门化中使用strtodstrtol

代码语言:javascript
复制
namespace boost {
template<>
inline int lexical_cast(const std::string& arg)
{
    char* stop;
    int res = strtol( arg.c_str(), &stop, 10 );
    if ( *stop != 0 ) throw_exception(bad_lexical_cast(typeid(int), typeid(std::string)));
    return res;
}
template<>
inline std::string lexical_cast(const int& arg)
{
    char buffer[65]; // large enough for arg < 2^200
    ltoa( arg, buffer, 10 );
    return std::string( buffer ); // RVO will take place here
}
}//namespace boost

int main(int argc, char* argv[])
{
    std::string str = "22"; // SOME STRING
    int int_str = boost::lexical_cast<int>( str );
    std::string str2 = boost::lexical_cast<std::string>( str_int );

    return 0;
}

此变体将比使用默认实现更快,因为在默认实现中有大量流对象的构造。而且它应该比printf快一点,因为printf应该解析格式字符串。

票数 20
EN

Stack Overflow用户

发布于 2009-08-09 06:23:06

lexical_cast比您在Java和Python中使用的特定代码更通用。不足为奇的是,在许多场景中起作用的通用方法(词法转换只不过是流出来,然后从临时流返回)最终比特定的例程慢。

(顺便说一句,使用静态版本Integer.toString(int)可以获得更好的性能。1)

最后,字符串解析和退出通常并不对性能敏感,除非一个人正在编写编译器,在这种情况下,lexical_cast可能过于通用,整数等将在扫描每一个数字时被计算出来。

1评论员"stepancheg“怀疑我的暗示,即静态版本可能会提供更好的性能。以下是我使用的来源:

代码语言:javascript
复制
public class Test
{
    static int instanceCall(int i)
    {
        String s = new Integer(i).toString();
        return s == null ? 0 : 1;
    }

    static int staticCall(int i)
    {
        String s = Integer.toString(i);
        return s == null ? 0 : 1;
    }

    public static void main(String[] args)
    {
        // count used to avoid dead code elimination
        int count = 0;

        // *** instance

        // Warmup calls
        for (int i = 0; i < 100; ++i)
            count += instanceCall(i);

        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000000; ++i)
            count += instanceCall(i);
        long finish = System.currentTimeMillis();
        System.out.printf("10MM Time taken: %d ms\n", finish - start);


        // *** static

        // Warmup calls
        for (int i = 0; i < 100; ++i)
            count += staticCall(i);

        start = System.currentTimeMillis();
        for (int i = 0; i < 10000000; ++i)
            count += staticCall(i);
        finish = System.currentTimeMillis();
        System.out.printf("10MM Time taken: %d ms\n", finish - start);
        if (count == 42)
            System.out.println("bad result"); // prevent elimination of count
    }
}

运行时,使用JDK 1.6.0-14,服务器VM:

代码语言:javascript
复制
10MM Time taken: 688 ms
10MM Time taken: 547 ms

在客户端VM中:

代码语言:javascript
复制
10MM Time taken: 687 ms
10MM Time taken: 610 ms

即使从理论上讲,转义分析可能允许在堆栈上分配,内联可能会将所有代码(包括复制)引入到本地方法中,从而允许消除冗余复制,这种分析可能会花费相当多的时间,并导致相当大的代码空间,这在代码缓存中还有其他代价,而不是像这里这样的微基准。

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

https://stackoverflow.com/questions/1250795

复制
相关文章

相似问题

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