的Windows XP SP3。 酷睿2 2.0千兆赫。 我发现了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)
我看到的时间是
C ++:6700毫秒
Java的:1178毫秒
蟒蛇:6702毫秒
C ++是作为蟒慢和比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毫秒
蟒蛇:30780毫秒
因此,对于双打C ++实际上是蟒蛇的速度的一半,比Java解决方案慢20倍!。 改善了boost :: lexical_cast的性能有任何想法? 从穷人的stringstream实现这是否干或者我们可以期待使用Boost库的性能一般10倍下降。
编辑2012-04-11
RVE相当正确地评论了关于lexical_cast的性能,提供了一个链接:
http://www.boost.org/doc/libs/1_49_0/doc/html/boost_lexical_cast/performance.html
我没有接,现在提高1.49,但我记得做我的代码上的旧版本快。 所以我想:
- 下面的答案仍然是有效的(如果只是为了学习目的)
- 有可能是两个版本之间的某个地方出台的优化(我会寻觅那)
- 这意味着提升却依然向好
原来的答案
我想补充的信息在巴里和Motti的优秀的答案:
一些背景知识
请记住升压是通过在这个星球上最好的C ++开发人员编写的,并且由同一个最好的开发人员审查。 如果lexical_cast
真的错了,会有人入侵图书馆要么批评或代码。
我猜你错过了点lexical_cast
人的真正价值?
比较苹果和桔子。
在Java中,你是铸造整成一个Java字符串。 你会注意到,我不是在谈论一个字符数组或用户定义的字符串。 你会注意到,太,我不是在谈论你的用户定义的整数。 我说的是严格的Java整数和严格的Java字符串。
在Python中,你或多或少做同样的。
正如其他职位说,你是在本质上,使用的Java和Python当量sprintf
(或不太标准的itoa
)。
在C ++中,您使用的一个非常强大的演员。 在原始性能速度感不强(如果你想要的速度,也许sprintf
会更适合),但在可扩展性的意义强大。
比较苹果。
如果要比较一个Java Integer.toString
方法,那么你应该把它与无论是C比较sprintf
或C ++ ostream
设施。
在C ++流的解决办法是超过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 ;
}
的C sprintf
解决方案将是8倍的速度(在我的G ++),比lexical_cast
反而少了很多安全:
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
?
词汇铸件的主要用途是:
- 易于使用(哎,一个C ++演员,其中的一切是一个价值的作品!)
- 它与模板重码,你的类型参数化的,因此你不想处理的细节相结合,你不想知道的类型。
- 不过可能比较有效,如果你有基本的模板知识,下面我会演示
点2是非常非常重要的位置,因为这意味着我们有一个且只有一个接口/功能投下类型的值转换成另一种类型的相同或类似的价值。
这是你错过了真正的问题,这就是,在性能方面的成本点。
但它是如此slooooooowwww!
如果你想生高速性能,还记得你处理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
通过使字符串和整数(通过定义宏观这种专业化的lexical_cast SPECIALIZE_BOOST_LEXICAL_CAST_FOR_STRING_AND_INT
),我的代码就在我的G ++编译器,这意味着,根据您的数据,其性能应类似于Java的5个时间更快。
我花了10分钟看升压代码,并写入远程高效和正确的32位版本。 并与一些工作,它也许可以走得更快,更安全(如果我们不得不直接写访问std::string
内部缓冲区,我们能够避免暂时的外部缓冲器,例如)。
你可以专注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
应解析格式字符串。
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
}
}
在运行时,使用JDK 1.6.0-14,服务器VM:
10MM Time taken: 688 ms
10MM Time taken: 547 ms
而在客户端虚拟机:
10MM Time taken: 687 ms
10MM Time taken: 610 ms
尽管理论上,逃逸分析可以允许在堆栈上分配,和内联可能引入所有代码(包括复制)到所述本地方法,从而允许消除冗余拷贝,这样的分析可能需要相当大量的时间,并导致相当多的代码空间,它在代码缓存其他费用不自圆其说实时代码,而不是像这里看到微基准。
什么词汇投在你的代码做可以简化为这样的:
string Cast( int i ) {
ostringstream os;
os << i;
return os.str();
}
有不幸的是,很多事情你每次调用转换时间():
- 字符串流被创建可能分配存储器
- 运营商<<对于整数i被称为
- 结果存储在流中,可能分配存储器
- 一个字符串拷贝从物流中取出
- 字符串的副本(可能)创建归还。
- 内存被释放
THN在自己的代码:
s = Cast( i );
转让涉及进行进一步的分配和释放。 您可以通过使用稍微降低这样的:
string s = Cast( i );
代替。
然而,如果性能是真的importanrt给你,你应该considerv使用不同的机制。 你可以写你自己的演员的版本(),它(例如)创建了一个静态的字符串流。 这样的版本将不会是线程安全的,但可能并不重要您的特定需求。
总之,lexical_cast的是一种方便实用的功能,但这种便利性是(因为它总是必须)与其他领域的权衡。
不幸的是,我没有足够的代表尚未就此发表评论......
lexical_cast
,因为它是通用的(模板查找发生在编译时间,所以虚函数调用或其他查找/解引用是没有必要的)是不是主要慢。 lexical_cast
是,在我看来,缓慢的,因为它建立在C ++输入输出流,它主要用于流操作,而不是单次转换,因为lexical_cast
必须检查并转换iostream的错误信号。 从而:
- 一个流对象必须被创建和销毁
- 在上面的字符串输出的情况下,请注意,C ++编译器也很难避免缓冲器副本(一种替代方法是直接格式化到输出缓冲器,像
sprintf
那样,虽然sprintf
不会安全地处理的缓冲区溢出) -
lexical_cast
必须检查stringstream
错误( ss.fail()
以扔在转换失败例外)
lexical_cast
是不错的,因为(IMO)例外允许捕捉不用额外的努力所有的错误和因为它有一个统一的原型。 我个人并不明白为什么这两种性质的必要慢操作(当没有发生转换错误),但我不知道这样的C ++功能,这是快速(可能是精神或升压:: xpressive中?)。
编辑:我刚刚发现了一个消息,提使用BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
,使一个“itoa”优化: http://old.nabble.com/lexical_cast-optimization-td20817583.html 。 还有一个链接的文章有一些详细信息。
lexical_cast的可能或可能不会相对于Java和Python作为你的bencharks表明慢,因为您的基准测量,可能有一个微妙的问题。 词法铸造或它使用iostream方法进行的任何办公地点的分配/释放操作是由您的基准测量,因为C ++不推迟这些操作。 然而,在Java和Python的情况下,相关的解除分配可实际上都被简单地推迟到将来的垃圾收集周期和基准测量错过。 (除非而基准是在进步,在这种情况下,你会衡量太多时偶然有一个GC周期)。 因此,它是很难知道肯定不检查Java和Python实现多少“成本”应归因于可能(也可能不会)最终被征收递延GC负担的细节。
这种问题显然可以适用于许多其他的C ++ VS垃圾回收语文基准。
正如巴里说, lexical_cast
很一般,你应该使用更具体的替代方案,例如看看itoa ( int->string
)和的atoi ( string -> int
)。
如果速度是一个问题,或者你是在这样的强制类型转换的速度有多快可以用C ++只是感兴趣,有感兴趣的线程把它。
Boost.Spirit 2.1(这是与升压1.40被释放)似乎是非常快,甚至更快比C当量(与strtol(),的atoi()等等)。
我用POD类型的这种非常快速的解决方案...
namespace DATATYPES {
typedef std::string TString;
typedef char* TCString;
typedef double TDouble;
typedef long THuge;
typedef unsigned long TUHuge;
};
namespace boost {
template<typename TYPE>
inline const DATATYPES::TString lexical_castNumericToString(
const TYPE& arg,
const DATATYPES::TCString fmt) {
enum { MAX_SIZE = ( std::numeric_limits<TYPE>::digits10 + 1 ) // sign
+ 1 }; // null
char buffer[MAX_SIZE] = { 0 };
if (sprintf(buffer, fmt, arg) < 0) {
throw_exception(bad_lexical_cast(typeid(TYPE),
typeid(DATATYPES::TString)));
}
return ( DATATYPES::TString(buffer) );
}
template<typename TYPE>
inline const TYPE lexical_castStringToNumeric(const DATATYPES::TString& arg) {
DATATYPES::TCString end = 0;
DATATYPES::TDouble result = std::strtod(arg.c_str(), &end);
if (not end or *end not_eq 0) {
throw_exception(bad_lexical_cast(typeid(DATATYPES::TString),
typeid(TYPE)));
}
return TYPE(result);
}
template<>
inline DATATYPES::THuge lexical_cast(const DATATYPES::TString& arg) {
return (lexical_castStringToNumeric<DATATYPES::THuge>(arg));
}
template<>
inline DATATYPES::TString lexical_cast(const DATATYPES::THuge& arg) {
return (lexical_castNumericToString<DATATYPES::THuge>(arg,"%li"));
}
template<>
inline DATATYPES::TUHuge lexical_cast(const DATATYPES::TString& arg) {
return (lexical_castStringToNumeric<DATATYPES::TUHuge>(arg));
}
template<>
inline DATATYPES::TString lexical_cast(const DATATYPES::TUHuge& arg) {
return (lexical_castNumericToString<DATATYPES::TUHuge>(arg,"%lu"));
}
template<>
inline DATATYPES::TDouble lexical_cast(const DATATYPES::TString& arg) {
return (lexical_castStringToNumeric<DATATYPES::TDouble>(arg));
}
template<>
inline DATATYPES::TString lexical_cast(const DATATYPES::TDouble& arg) {
return (lexical_castNumericToString<DATATYPES::TDouble>(arg,"%f"));
}
} // end namespace boost