Nav*_*een 45 c++ boost lexical-cast
Windows XP SP3.Core 2 Duo 2.0 GHz.我发现boost :: lexical_cast性能非常慢.想找出加速代码的方法.在visual c ++ 2008上使用/ O2优化并与java 1.6和python 2.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)
Run Code Online (Sandbox Code Playgroud)
我看到的时间是
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)
Run Code Online (Sandbox Code Playgroud)
我看到的时间是
c ++:56129毫秒
java:2852毫秒
python:30780毫秒
所以对于双打,c ++实际上是python速度的一半,比java解决方案慢20倍!! 有关改进boost :: lexical_cast性能的任何想法?这是源于糟糕的字符串流实现还是我们可以预期使用boost库会导致性能降低10倍.
pae*_*bal 78
rve对lexical_cast的表现做了非常正确的评论,提供了一个链接:
http://www.boost.org/doc/libs/1_49_0/doc/html/boost_lexical_cast/performance.html
我现在无法访问以提升1.49,但我确实记得在旧版本上使代码更快.所以我想:
只是为了添加关于Barry和Motti的优秀答案的信息:
请记住,Boost是由这个星球上最好的C++开发人员编写的,并由相同的最佳开发人员审核.如果lexical_cast
错了,有人会批评或用代码攻击图书馆.
我猜你错过了lexical_cast
真正的价值......
在Java中,您将整数转换为Java String.你会注意到我不是在谈论一个字符数组或一个用户定义的字符串.你也会注意到,我不是在谈论你的用户定义的整数.我在谈论严格的Java Integer和严格的Java String.
在Python中,您或多或少都在做同样的事情.
正如其他帖子所说,从本质上讲,您使用的是Java和Python等价物sprintf
(或者标准较低itoa
).
在C++中,您使用的是非常强大的演员.在原始速度性能方面并不强大(如果你想要速度,也许sprintf
更适合),但在可扩展性方面却很强大.
如果要比较Java Integer.toString
方法,则应将其与C sprintf
或C++ ostream
工具进行比较.
C++流解决方案比我的g ++快6倍lexical_cast
,而且可扩展性更低:
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 ;
}
Run Code Online (Sandbox Code Playgroud)
C sprintf
解决方案比我的g ++快8倍,lexical_cast
但安全性要低得多:
inline void toString(const int value, char * output)
{
sprintf(output, "%i", value) ;
}
Run Code Online (Sandbox Code Playgroud)
这两种解决方案都比Java解决方案快(或快)(根据您的数据).
如果要比较C++ lexical_cast
,则应将其与此Java伪代码进行比较:
Source s ;
Target t = Target.fromString(Source(s).toString()) ;
Run Code Online (Sandbox Code Playgroud)
源和目标都是你想要的任何类型,包括类似的内置类型,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
Run Code Online (Sandbox Code Playgroud)
通过为字符串和整数启用lexical_cast的这种特化(通过定义宏SPECIALIZE_BOOST_LEXICAL_CAST_FOR_STRING_AND_INT
),我的代码在我的g ++编译器上的速度提高了5倍,这意味着,根据您的数据,它的性能应该类似于Java.
我花了10分钟看看增强代码,并编写了一个远程高效且正确的32位版本.通过一些工作,它可能会更快更安全(如果我们对std::string
内部缓冲区有直接写访问权限,我们可以避免使用临时外部缓冲区).
Kir*_*sky 20
你可以专注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;
}
Run Code Online (Sandbox Code Playgroud)
此变体将比使用默认实现更快,因为在默认实现中存在重流对象的构造.它应该比它快一点printf
,因为printf
应该解析格式字符串.
Bar*_*lly 14
lexical_cast
比您在Java和Python中使用的特定代码更通用.在许多场景中运行的一般方法(词法转换只是流出然后返回到临时流中)并不比特定例程慢,这并不奇怪.
(顺便说一下,使用静态版本可以从Java中获得更好的性能,Integer.toString(int)
.[1])
最后,字符串解析和deparsing通常不是性能敏感的,除非正在编写编译器,在这种情况下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
}
}
Run Code Online (Sandbox Code Playgroud)
运行时,使用JDK 1.6.0-14,服务器VM:
10MM Time taken: 688 ms
10MM Time taken: 547 ms
Run Code Online (Sandbox Code Playgroud)
在客户端VM中:
10MM Time taken: 687 ms
10MM Time taken: 610 ms
Run Code Online (Sandbox Code Playgroud)
尽管从理论上讲,转义分析可能允许在堆栈上进行分配,并且内联可能会将所有代码(包括复制)引入本地方法,从而允许消除冗余复制,这种分析可能需要花费大量时间并导致相当多的代码空间,在代码缓存中有其他成本,在实际代码中不能证明自己是正确的,而不像这里看到的微基准测试那样.
小智 9
lexical演员在你的代码中做了什么可以简化为:
string Cast( int i ) {
ostringstream os;
os << i;
return os.str();
}
Run Code Online (Sandbox Code Playgroud)
不幸的是,每次调用Cast()时都会发生很多事情:
在你自己的代码中:
s = Cast( i );
Run Code Online (Sandbox Code Playgroud)
该任务涉及进一步的分配和解除分配.您可以使用以下方法稍微减少这一点:
string s = Cast( i );
Run Code Online (Sandbox Code Playgroud)
代替.
但是,如果性能对您来说真的很重要,那么您应该考虑使用不同的机制.您可以编写自己的Cast()版本(例如)创建静态字符串流.这样的版本不是线程安全的,但这可能对您的特定需求无关紧要.
总而言之,lexical_cast是一个方便实用的功能,但在其他方面需要权衡这种便利(一如既往).
不幸的是我还没有足够的代表评论......
lexical_cast
主要是因为它是通用的(模板查找在编译时发生,因此不需要虚函数调用或其他查找/解引用).lexical_cast
在我看来,它很慢,因为它建立在C++ iostream之上,它主要用于流操作而不是单个转换,因为lexical_cast
必须检查和转换iostream错误信号.从而:
sprintf
这样,但sprintf
不会安全地处理缓冲区溢出)lexical_cast
必须检查stringstream
errors(ss.fail()
)以便在转换失败时抛出异常lexical_cast
很好,因为(IMO)例外允许捕获所有错误而无需额外的努力,因为它有一个统一的原型.我个人不明白为什么这些属性中的任何一个都需要慢速操作(当没有发生转换错误时),虽然我不知道这些快速的C++函数(可能是Spirit或boost :: xpressive?).
编辑:我刚刚发现一条消息,提到使用BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
"itoa"优化:http://old.nabble.com/lexical_cast-optimization-td20817583.html.还有一篇链接文章,内容更详细.
lexical_cast可能会或可能不会像你的benchark所指出的那样与Java和Python相关,因为你的基准测量可能有一个微妙的问题.由词法转换或它使用的iostream方法完成的任何工作空间分配/解除分配都是由您的基准测量的,因为C++不会推迟这些操作.但是,在Java和Python的情况下,相关的解除分配实际上可能只是被推迟到未来的垃圾收集周期并被基准测量所遗漏.(除非在基准测试进行过程中偶然发生GC循环,在这种情况下,您将测量太多).因此,如果不仔细研究Java和Python实现的细节,很难确定应该将多少"成本"归因于可能(或可能不)最终强加的延迟GC负担.
这种问题显然可能适用于许多其他C++与垃圾收集语言基准测试.