ページへ戻る

 印刷 

技術系備忘録​/C++​/最適化小手先テクニック​/try~throw~catchの乱用を避けるべし :: シンクリッジ

xpwiki:技術系備忘録/C++/最適化小手先テクニック/try~throw~catchの乱用を避けるべし

通説ですが、try~throw~catch は遅いです。
便利に使える場面もありますが、パフォーマンスが気になるところでは、なるべく使わないほうがよいでしょう。

どの程度のパフォーマンスかを実際に調べてみました。

  • List1
    class CTest
    {
    public:
    	enum{
    		TABLESIZE = 100,
    	};
    	int m_Table[TABLESIZE];
    public:
    	CTest();
    	int CaseGoto();
    	int CaseTry();
    	virtual void OnExcept(){}
    };
    CTest::CTest()
    {
    	::memset(m_Table,0,sizeof(m_Table));
    	m_Table[0] = 1;
    }
    int CTest::CaseGoto()
    {
    	int nCount = 0;
    	for(int i=0; i<1000000; i++ ){
    		for(int x=0; x<TABLESIZE; x++ ){
    			if( m_Table[x] != 0 ){
    				OnExcept();	// 最適化されないように念のため
    				goto next;	// throwと比較しやすいようにgoto使ってます
    			}
    		}
    		nCount++;
    	next:;
    	}
    	return nCount;
    }
    int CTest::CaseTry()
    {
    	int nCount = 0;
    	for(int i=0; i<1000000; i++ ){
    		try{
    			for(int x=0; x<TABLESIZE; x++ ){
    				if( m_Table[x] != 0 ){
    					OnExcept();	// 最適化されないように念のため
    					throw false;
    				}
    			}
    			nCount++;
    		}catch(...){
    		}
    	}
    	return nCount;
    }

CTest::CaseGoto と CTest::CaseTry の処理時間をそれぞれ計測してみると、
CTest::CaseGoto が 10ms、
CTest::CaseTry が 7551ms、
と、サンプルコードが特殊ではありますが、かなりの差があります。
(Pentium3 1.2G のマシンでVC++6を使用)

try~catch は、関数をまたいで goto 出来るうえ、ローカルクラスのデストラクタ処理なども考慮されており、使い勝手はC言語の頃からある setjmp~longjmp 以上の物なのですが、便利な分、遅いです。

例外処理本来の意味で、深いネスト(関数)からエラー処理の為に脱出するような使い方であれば良いと思うのですが、サンプルコードのように、普通の条件分岐で記述できる箇所では使わないようにしたほうが、パフォーマンスの面では有利だと思います。

さらに、ちょっと愚痴っぽくなりますが、
本来 goto文 を使うべき箇所(*1)で、goto文 を避ける為に try~catch を使っているような場合も、パフォーマンスの面だけでなく考慮してほしいな思うことがあります。
VisualC++ を使ってデバッガ上で実行すると、throw が発生する度に、出力ウインドウに"例外処理・・・"というメッセージが表示されます。
開発業務等で外部提供のモジュールを使用するケースはよくあると思いますが、
その外部モジュールが、通常フローにも関わらず throw 連発しているような物だったら、しかも常に処理が走り throw 連発するような物だったら、
出力ウインドウが"例外処理・・・"で埋め尽くされてしまいます。
自分で入れたデバッグメッセージは一瞬で流されて見れたもんじゃありません。
どうにかならないもんでしょうか・・・って誰にも文句を言えないとこにプログラマの辛い面を感じました。

なにはともあれ、try~catch を何も考えずに使うのは止めたほうが良いと思います。

/*
setjmp はメモリ上にスタック環境を保持しておき、longjmp でその状態を戻すというアクロバティックな動作をします。普通の条件分岐とは訳が違います。
その為か setjmp~longjmp は特殊で難しく、コードの見通しも悪くなるのでなるべく使わないようにしましょうとまで言われていた関数だったのですが、
C++では try~catch として言語仕様としてサポートされた訳です。
そう考えると、C言語の時代でも setjmp~longjmp をもっと便利に使うべきだったのかなと思います。
*/

/*
本件は C++ における try~catchのパフォーマンスについて述べています。
JAVA や C# は try~catch を使うことが大前提のような仕様になってますので遠慮なく使っていいと思います。(パフォーマンスはわかりません)
*/


*1 goto文は使ってはいけないとか、場合によっては使うべしという意見がありますが、自分は後者です。


Last-modified: 2005-12-25 (日) 08:43:00 (JST) (2941d) by takatsuka