コンテンツへスキップ
ものがたり
戻る

2005-08-30

やや不調。C#パーサをいじってcastまわりのやっつけを綺麗にできないかと格闘して、結局諦めた。Martinがそれなりに苦心して設計したのには勝てん。

goto heaven / goto hell

人はいかにgoto文を[使わない|使う]か。見た感じ、あまり意外ではないし、実に妥当な結論(?)に見える。gotoについてどう思う?という質問はひとつの試金石だし。

Ximianで最適化を試行錯誤するまでの僕は、gotoはほとんど使わなかったのだけど、gotoを使う場面というものに何となく憧れていた節がある。そしてmscorlibやmcsのソースなどをいじるようになると、たちまちgotoの嵐。パッチを作ればgotoじゃなきゃ嫌だと言われるような世界だ。このケースみたいに、別の関数を作って呼び出すなんてパフォーマンス的に論外な状況は多々ある。

一般的には、gotoは低レベルプログラミングに親和的で、デザインパターンがどうのSOAがどうのっていう世界ではあまり使われない。こういう人たちは、パフォーマンスをプログラム設計のレベルで問う機会が少ないだろうし、むしろ設計の障害となる構造化指向の破壊1を気にするので、gotoは使わない、という傾向が強いだろう。

ただ、僕の印象としては、C#におけるgotoの禁忌度(変な表現だけど)は、CとかC++のそれよりも小さいのではないかと思う。ひとつにはフロー・コントロールが言語レベルで確立していること(C#言語仕様で最初から明記されている)。未初期化変数はコンパイラによってかなり厳密にチェックされる2。finallyの存在によって、複数ラベル式が必要になる場面はほとんど無い。フロー・コントロールが確立しているので、(JITを含む)コンパイラが最適化を放棄することも無い。

ちなみにMonoでは、フローを分断するcontinueはLinus Torvaldsトリックとしてむしろ推奨されている。ガード節(と言うの?僕は経験浅いんでよく知らないんだけど)もそうだ。契約上特定の入力にはnullを返さなければならない時、それ以外の条件をelseでくくってあとは延々インデントが1ネスト深いコードが続くのって、ストレス溜まるし。


コメント

ladybug — 08/30/2005 10:09:44

「別の関数を作って呼び出すなんてパフォーマンス的に論外な」ってのは、コンパイル時や実行時のインライン展開に代表されるようなコード上に見えない最適化もあるので、コーディング時の関数/メソッドがどうパフォーマンスに影響するかは測りにくいというものありますよね。
私の場合は初回思考が Pascal なことが多いので、ループ脱出の return は関数内関数でコーディングされていて、インライン展開か往復2回の JMP になってることが多かったり。

atsushieno — 08/30/2005 15:03:23

inlineに関しては、見えてくれていなくても、最適化さえしてくれれば良いんですけど(w、期待通りに最適化してくれなかったり、必要に応じてコードを追加しているうちにあれあれ?っていうことになってしまうので、やっぱりそもそも関数を抜き出すことに抵抗があります。

ladybug — 08/30/2005 15:47:12

「わからない」といえば、gotoも最適化されるかもしれないんでしょうけど、現実的に最適化されないんでしょうね。
仮想関数呼び出しも goto とおなじで最適化されない部分かもしれない。cscでは virtual methodをどんどん削除して switch-case などで書き換えると実行時パフォーマンスがかなり上がったりします。

atsushieno — 08/30/2005 16:21:40

いや、フロー分析が正しく出来ていれば、gotoがあっても、変数割り当ての処理過程で、他のステートメントと同じように割り当てられた値の範囲なども正しく計算できるはずです。そのフロー分析の難易度は、continueやbreakを含むループと、そんなに違いは無いと思います(特にループ外で定義されるローカル変数の場合)。それでも(JITパフォーマンス上のロスを考慮して)最適化していないということはあり得ますけどね。
コーディングのステップでは、gotoはインラインと違って、後付けでコードを変更しても、ローカル変数の宣言位置やブロックのくくり方によって、最適化がかかるように制御しやすいです。コーダーがちゃんとフロー分析できないとダメですけど(笑)
仮想関数呼び出しをswitch-caseで最適化っていうのは思いつきませんでした。switch-caseということはTypeのHashCodeか何かで定数にしているのかと推測しますが、確かにそっちの方が速いかもしれませんね。邪道かも知れませんが(w 昔どこかのMLで「DOMのnodeTypeはswitch-caseで最適化できるから有用なんだ」みたいに書いたら嗤われた記憶があります。すげー正気で書いていたんですけどねー。

Footnotes

  1. もちろん、関数単位では出入り口一つの原則が維持されていることは理解している。だけどそれがリファクタリングにとって無用な制約になることも確かだ。

  2. その意味ではこの辺にバグをいくつか抱えていたmcsでgotoを使いまくっていたXimianのハッカーは比較的クレイジーだ


この記事を共有:

前の記事
goto heaven / goto hell
次の記事