Windows XPSP3。Core2 Duo 2.0GHz。boost:: lexical_castのパフォーマンスが非常に遅いことがわかりました。コードを高速化する方法を見つけたいと思っていました。/O2最適化の使用Visual C++ 2008と比較して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倍遅い!!)。ブースト:: lexical_castパフォーマンスを改善するためのアイデア?これは文字列ストリームの実装が不十分なためか、ブーストライブラリを使用するとパフォーマンスが一般的に10倍低下することが予想されますか?.
rve lexical_castのパフォーマンスについてかなり正しくコメントし、リンクを提供します。
http://www.boost.org/doc/libs/1_49_0/doc/html/boost_lexical_cast/performance.html
現在1.49をブーストするためのアクセス権はありませんが、以前のバージョンでコードを高速化したことを覚えています。だから私は推測します:
バリーとモッティの優れた答えに関する情報を追加するだけです。
Boostはこの惑星で最高のC++開発者によって書かれ、同じ最高の開発者によってレビューされていることを覚えておいてください。 _lexical_cast
_が非常に間違っていた場合、誰かが批判またはコードのいずれかでライブラリをハッキングしたでしょう。
_lexical_cast
_の真の価値のポイントを逃したと思います...
Javaでは、整数をJava文字列にキャストします。文字の配列やユーザー定義の文字列について話しているのではないことに注意してください。また、ユーザー定義の整数ではなく、厳密なJava整数と厳密なJava文字列です。
Pythonでは、多かれ少なかれ同じことをしています。
他の投稿で述べたように、あなたは本質的に、JavaおよびPython sprintf
と同等のもの(または標準的ではない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
ソリューションは_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
型に変換します。
つまり、オブジェクトをストリームに出力したり、ストリームから入力したりできる限り、コードを1行も変更することなく、そのオブジェクトに対して_lexical_cast
_を使用できます。
lexical_cast
_の用途は何ですか?字句キャスティングの主な用途は次のとおりです。
ここでポイント2は非常に重要です。これは、型の値を別の型の同等または類似の値にキャストするためのインターフェース/関数が1つしかないことを意味するためです。
これはあなたが見逃した本当のポイントであり、これはパフォーマンスの点でコストがかかるポイントです。
本来の速度のパフォーマンスが必要な場合は、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倍速くなりました。
そして、ブーストコードを確認するのに10分かかり、リモートで効率的で正しい32ビットバージョンを記述しました。そして、いくつかの作業により、おそらくより速く安全になります(たとえば、_std::string
_内部バッファーへの直接書き込みアクセス権がある場合、一時的な外部バッファーを回避できます)。
あなたはlexical_cast
for 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で使用している特定のコードよりも一般的です。多くのシナリオで機能する一般的なアプローチ(字句キャストはストリーミングよりも少しだけ多い)であることは当然のことです一時的なストリームとの間でやり取りを繰り返すと、特定のルーチンよりも遅くなります。
(ところで、静的バージョンInteger.toString(int)
を使用すると、Javaからパフォーマンスが向上する可能性があります。[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
_
理論的には、エスケープ分析はスタックへの割り当てを許可し、インライン化はすべてのコード(コピーを含む)をローカルメソッドに導入し、冗長なコピーの排除を許可する場合があります。このような分析にはかなりの時間がかかり、かなりの量になる可能性があります。ここに見られるようなマイクロベンチマークとは対照的に、コードスペースには、実際のコードで正当化されないコードキャッシュの他のコストがあります。
コードで字句キャストが行っていることは、次のように簡略化できます。
string Cast( int i ) {
ostringstream os;
os << i;
return os.str();
}
残念ながら、Cast()を呼び出すたびに多くのことが行われています。
あなた自身のコードでThn:
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()
)をチェックする必要があります(IMO)例外は余計な手間をかけずにすべてのエラーをトラップできるため、プロトタイプが統一されているため、_lexical_cast
_はすばらしいです。高速なC++関数(Spiritまたはboost :: xpressive?)については知りませんが、これらのプロパティのいずれかが(変換エラーが発生しないときに)低速な操作を必要とする理由を個人的には知りません。
編集:「itoa」最適化を有効にするための_BOOST_LEXICAL_CAST_ASSUME_C_LOCALE
_の使用について言及しているメッセージを見つけました: http://old.nabble.com/lexical_cast-optimization-td20817583.html 。リンクされた article もあり、もう少し詳細があります。
lexical_castは、ベンチマークの測定値が次のようになるため、JavaおよびPython微妙な問題。字句キャストまたはそれが使用するiostreamメソッドによって行われるワークスペースの割り当て/割り当て解除は、C++がこれらの操作を遅延しないため、ベンチマークによって測定されます。ただし、JavaおよびPython 、関連する割り当て解除は、実際には単に将来のガベージコレクションサイクルまで延期され、ベンチマーク測定によって見落とされている可能性があります(ベンチマークの進行中に偶然にGCサイクルが発生し、その場合、測定が多すぎる場合を除く)。 。したがって、JavaおよびPython実装の詳細を調べない限り、どれだけの「コスト」が遅延GCの負担に起因するかを確認することなく、確実に知ることは困難です。 (またはそうでない場合もあります)最終的に課せられます。
この種の問題は、明らかに他の多くのC++とガベージコレクションされた言語のベンチマークに当てはまる可能性があります。
速度が気になる場合、またはC++でのそのようなキャストの速度に興味がある場合は、それに関連する thread があります。
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