Windows SP3。核心2双2.0 GHz。我发现boost::lexical_cast的性能非常慢。想找出加速代码的方法。在Visual/O2 2008上使用/O2优化,并与Java1.6和python2.6.2进行比较,我看到了以下结果。
整数铸造:
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倍。
双浇铸:
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倍。
发布于 2009-08-09 09:50:55
编辑2012-04-11
雷夫非常正确地评论了词法_cast的性能,提供了一个链接:
cast/playce.html
我现在没有权限来提升1.49,但我确实记得在旧版本上使我的代码更快。所以我猜:
原始答案
我想补充一下巴里和莫蒂的好答案:
一些背景
请记住,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倍,扩展性也差很多:
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倍,但安全性要低得多:
inline void toString(const int value, char * output)
{
sprintf(output, "%i", value) ;
}这两种解决方案都与Java解决方案一样快或更快(根据您的数据)。
比较橘子。
如果您想比较一个C++ lexical_cast,那么您应该将它与这个Java伪代码进行比较:
Source s ;
Target t = Target.fromString(Source(s).toString()) ;源和目标是任意类型的,包括内置类型(如boolean或int ),这在C++中是可能的,因为有模板。
可扩展性?这是个下流词吗?
不,但它有一个众所周知的代价:当由同一个编码器编写时,对特定问题的一般解决方案通常比为其特定问题编写的特定解决方案要慢。
在当前的情况下,在朴素的观点中,lexical_cast将使用流工具将类型A转换为字符串流,然后从该字符串流转换为类型B。
这意味着,只要可以将对象输出到流中,并从流中输入,就可以在其上使用lexical_cast,而无需触及任何一行代码。
那么,lexical_cast的用途是什么?
词汇铸造的主要用途如下:
第2点在这里非常重要,因为它意味着我们只有一个接口/函数将一个类型的值转换为另一个类型的相同或相似的值。
这才是你错过的真正要点,而这一点在性能上是需要付出代价的。
但它太血腥了!
如果您想要获得原始的速度性能,请记住您正在处理C++,并且您有很多工具可以有效地处理转换,而且仍然要保持lexical_cast易于使用的特性。
我花了几分钟的时间查看了lexical_cast源代码,并提出了一个可行的解决方案。在C++代码中添加以下代码:
#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内部缓冲区,就可以避免临时外部缓冲区)。
发布于 2009-08-09 09:43:29
您可以将lexical_cast专门用于int和double类型。在您的专门化中使用strtod和strtol。
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应该解析格式字符串。
发布于 2009-08-09 06:23:06
lexical_cast比您在Java和Python中使用的特定代码更通用。不足为奇的是,在许多场景中起作用的通用方法(词法转换只不过是流出来,然后从临时流返回)最终比特定的例程慢。
(顺便说一句,使用静态版本Integer.toString(int)可以获得更好的性能。1)
最后,字符串解析和退出通常并不对性能敏感,除非一个人正在编写编译器,在这种情况下,lexical_cast可能过于通用,整数等将在扫描每一个数字时被计算出来。
1评论员"stepancheg“怀疑我的暗示,即静态版本可能会提供更好的性能。以下是我使用的来源:
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:
10MM Time taken: 688 ms
10MM Time taken: 547 ms在客户端VM中:
10MM Time taken: 687 ms
10MM Time taken: 610 ms即使从理论上讲,转义分析可能允许在堆栈上分配,内联可能会将所有代码(包括复制)引入到本地方法中,从而允许消除冗余复制,这种分析可能会花费相当多的时间,并导致相当大的代码空间,这在代码缓存中还有其他代价,而不是像这里这样的微基准。
https://stackoverflow.com/questions/1250795
复制相似问题