[英]Very poor boost::lexical_cast performance
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)
我看到的時間是
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倍。
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中,您或多或少都在做同樣的事情。
正如其他帖子所說,從本質上講,你使用的是sprintf
的Java和Python等價物(或者標准較低的itoa
)。
在C ++中,您使用的是非常強大的演員。 在原始速度性能方面並不強大(如果你想要速度,也許sprintf
會更合適),但在可擴展性方面卻很強大。
如果要比較Java Integer.toString
方法,則應將其與C sprintf
或C ++ ostream
工具進行比較。
C ++流解決方案比lexical_cast
快6倍(在我的g ++上),並且可擴展性更低:
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
解決方案比lexical_cast
快8倍(在我的g ++上),但安全性要低得多:
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
通過為字符串和整數啟用lexical_cast專有化(通過定義宏SPECIALIZE_BOOST_LEXICAL_CAST_FOR_STRING_AND_INT
),我的代碼在g ++編譯器上的速度提高了5倍,這意味着,根據您的數據,其性能應該與Java類似。
我花了10分鍾看看增強代碼,並編寫了一個遠程高效且正確的32位版本。 通過一些工作,它可能會更快更安全(如果我們對std::string
內部緩沖區有直接寫訪問權限,我們可以避免使用臨時外部緩沖區)。
你可以為int
和double
類型專門化lexical_cast
。 在你的專業中使用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中使用的特定代碼更通用。 毫無疑問,在許多情況下(詞法轉換只是流出然后返回到臨時流中)的一般方法最終會比特定例程慢。
(順便說一句,你可以使用靜態版本Integer.toString(int)
從Java中獲得更好的性能。[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
在客戶端VM中:
10MM Time taken: 687 ms
10MM Time taken: 610 ms
盡管從理論上講,轉義分析可能允許在堆棧上進行分配,並且內聯可能會將所有代碼(包括復制)引入本地方法,從而允許消除冗余復制,這種分析可能需要花費大量時間並導致相當多的代碼空間,在代碼緩存中有其他成本,在實際代碼中不能證明自己是正確的,而不像這里看到的微基准測試那樣。
lexical演員在你的代碼中做了什么可以簡化為:
string Cast( int i ) {
ostringstream os;
os << i;
return os.str();
}
不幸的是,每次調用Cast()時都會發生很多事情:
在你自己的代碼中:
s = Cast( i );
該任務涉及進一步的分配和解除分配。 您可以使用以下方法稍微減少這一點:
string s = Cast( i );
代替。
但是,如果性能對您來說真的很重要,那么您應該考慮使用不同的機制。 您可以編寫自己的Cast()版本(例如)創建靜態字符串流。 這樣的版本不是線程安全的,但這可能對您的特定需求無關緊要。
總而言之,lexical_cast是一個方便實用的功能,但在其他方面需要權衡這種便利(一如既往)。
不幸的是我還沒有足夠的代表評論......
lexical_cast
主要不是很慢,因為它是通用的(模板查找在編譯時發生,因此不需要虛函數調用或其他查找/解引用)。 在我看來, lexical_cast
很慢,因為它建立在C ++ iostream之上,它主要用於流操作而不是單個轉換,並且因為lexical_cast
必須檢查並轉換iostream錯誤信號。 從而:
sprintf
一樣,盡管sprintf
不能安全地處理緩沖區溢出) lexical_cast
必須檢查stringstream
錯誤( ss.fail()
)以便在轉換失敗時拋出異常 lexical_cast
很好,因為(IMO)異常允許捕獲所有錯誤而無需額外的努力,因為它有一個統一的原型。 我個人不明白為什么這些屬性中的任何一個都需要慢速操作(當沒有發生轉換錯誤時),雖然我不知道這些快速的C ++函數(可能是Spirit或boost :: xpressive?)。
編輯:我剛剛發現一條消息,提到使用BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
來啟用“itoa”優化: http : BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
。 還有一篇鏈接文章 ,內容更詳細。
lexical_cast可能會或可能不會像你的benchark所指出的那樣與Java和Python相關,因為你的基准測量可能有一個微妙的問題。 由詞法轉換或它使用的iostream方法完成的任何工作空間分配/解除分配都是由您的基准測量的,因為C ++不會推遲這些操作。 但是,在Java和Python的情況下,相關的解除分配實際上可能只是被推遲到未來的垃圾收集周期並被基准測量所遺漏。 (除非在基准測試進行過程中偶然發生GC循環,在這種情況下,您將測量太多)。 因此,如果不仔細研究Java和Python實現的細節,很難確定應該將多少“成本”歸因於可能(或可能不)最終強加的延遲GC負擔。
這種問題顯然可能適用於許多其他C ++與垃圾收集語言基准測試。
如果速度是一個問題,或者你只是對C ++的演員陣容感興趣,那么就會有一個感興趣的話題 。
Boost.Spirit 2.1(將與Boost 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
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.