[解決済み] Try-catchは私のコードをスピードアップさせるか?
質問
try-catchの影響をテストするためにいくつかのコードを書きましたが、驚くべき結果が出ています。
static void Main(string[] args)
{
Thread.CurrentThread.Priority = ThreadPriority.Highest;
Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.RealTime;
long start = 0, stop = 0, elapsed = 0;
double avg = 0.0;
long temp = Fibo(1);
for (int i = 1; i < 100000000; i++)
{
start = Stopwatch.GetTimestamp();
temp = Fibo(100);
stop = Stopwatch.GetTimestamp();
elapsed = stop - start;
avg = avg + ((double)elapsed - avg) / i;
}
Console.WriteLine("Elapsed: " + avg);
Console.ReadKey();
}
static long Fibo(int n)
{
long n1 = 0, n2 = 1, fibo = 0;
n++;
for (int i = 1; i < n; i++)
{
n1 = n2;
n2 = fibo;
fibo = n1 + n2;
}
return fibo;
}
私のパソコンでは、これは一貫して0.96前後の値をプリントアウトします。
Fibo()の中のforループをtry-catchブロックで囲むと、こんな感じになります。
static long Fibo(int n)
{
long n1 = 0, n2 = 1, fibo = 0;
n++;
try
{
for (int i = 1; i < n; i++)
{
n1 = n2;
n2 = fibo;
fibo = n1 + n2;
}
}
catch {}
return fibo;
}
これで、一貫して0.69と出力されるようになりました。しかし、なぜでしょうか?
注:Releaseの設定でコンパイルし、EXEファイルを直接実行しました(Visual Studio外)。
EDIT
Jon Skeetの
エクセレント
分析
は、try-catch が何らかの原因で、この特定のケースで x86 CLR がより有利な方法で CPU レジスタを使用するようにしていることを示しています(その理由はまだ分かっていないようです)。Jonの発見を確認したところ、x64 CLRにはこの差はなく、x86 CLRよりも高速に動作することがわかりました。また、私は
int
の代わりに、Fiboメソッド内の
long
という型に変換したところ、x86のCLRもx64のCLRと同じように高速になりました。
UPDATEです。 この問題はRoslynによって修正されたようです。同じマシン、同じCLRのバージョン -- VS 2013でコンパイルすると上記のような問題が残りますが、VS 2015でコンパイルすると問題が解消されます。
解決方法は?
そのうちの1つは ロスリン スタック使用量の最適化を理解することを専門とするエンジニアがこれを見て、C#コンパイラがローカル変数のストアを生成する方法と、C#コンパイラがローカル変数のストアを生成する方法の間の相互作用に問題があるようだと報告してくれました。 JIT コンパイラは、対応する x86 コードでレジスタのスケジューリングを行います。その結果、ローカルのロードとストアにおいて最適でないコードが生成されます。
JITterがブロックがtry-protected領域にあることを知っている場合、私たち全員に不明な何らかの理由で、問題のあるコード生成経路は回避されます。
これはかなり変ですね。JITterチームにフォローアップして、彼らがこれを修正できるよう、バグを入力してもらえるかどうか確認します。
また、RoslynではC#とVBコンパイラのアルゴリズムを改良して、いつローカルをquot;ephemeralにするか、つまり、起動の間スタック上の特定の場所を確保するのではなく、ただプッシュとポップを繰り返すようにするかということを決定しています。JITterは、ローカルをいつquot;dead"にするかについてより良いヒントを与えれば、レジスタの割り当てやその他をよりうまくできるようになると信じています。
ご指摘ありがとうございます。また、奇妙な動作についてお詫び申し上げます。
関連
-
[解決済み] 複数の例外を一度にキャッチする?
-
[解決済み] コードが含まれるアセンブリのパスを取得するにはどうすればよいですか?
-
[解決済み] 複数のJava例外を同じcatch節でキャッチすることはできますか?
-
[解決済み] なぜすべてのブロックを "try"-"catch "で包んではいけないのですか?
-
[解決済み] 警告をtry/catchすることはできますか?
-
[解決済み】プログラムを停止/終了させることなく、完全な例外トレースバックをキャッチして表示する方法は?
-
[解決済み】C#で例外をキャッチして再スローする理由とは?
-
[解決済み】例外をスローしない場合、try/catchブロックはパフォーマンスを低下させるか?
-
[解決済み】try/catch + using, 正しい構文
-
[解決済み】try {...} finally {...} は良くて、try {...} catch{} はダメなのはなぜ?
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み】「未割り当てのローカル変数を使用」とはどういう意味ですか?
-
[解決済み】スクリプトクラスが見つからないので、スクリプトコンポーネントを追加できない?
-
[解決済み】「入力文字列が正しい形式ではありませんでした」エラーの解決方法は?[重複しています]。
-
[解決済み] 'IEnumerable<SelectListItem>' 型の ViewData アイテムで、キーが国であるものは存在しない。
-
[解決済み】Socket.Selectがエラー "An operation was attempted on something that is not a socket" を返す。
-
[解決済み】Entity FrameworkからのSqlException - セッション内で他のスレッドが動作しているため、新しいトランザクションは許可されません。
-
[解決済み] [Solved] .NETでスレッドの終了を待つには?
-
[解決済み】パラメータ付きRedirectToAction
-
[解決済み】画像のペイントにTextureBrushを使用する方法
-
[解決済み】三項演算子はif-elseブロックの2倍遅い?