« ハードディスク運が悪い その3 | トップページ | ScalaスケーラブルプログラミングをScala2.8RC1で読む その1 »

2010/05/10

ループ内で変数を宣言しない方が良いのか

要するに、Javaで下記コードmyMethod1の変数itwiceみたいに、ループの中で変数を宣言するとmyMethod2のようにループの外で宣言するよりコストがかかるんじゃなかろうか、という疑問です。

public void myMethod() {
    for (int i = 0; i < 10; i++) {
        int twice;
        twice = i * 2;
        System.out.println(twice);
    }
}
 
public void myMethod2() {
    int i;
    int twice;
    for (i = 0; i < 10; i++) {
        twice = i * 2;
        System.out.println(twice);
    }
}

私のイメージは、

  • (言語は違いますが)こちらに書かれているように、変数宣言するのに何らかの命令が発生するので、わずかだがコストは増える。
  • ただし、そのコストは微小で、それよりも変数のスコープを限定しメンテナンス性/可読性等を保つためにループ内で宣言した方が良い。

というものでした。

で、今回2つのメソッドをjavap -cコマンドでバイトコードに変換して、実際にどういう差異があるのか見てみました。

結果、双方同一のバイトコードが生成されました。

Code:
 0:   iconst_0
 1:   istore_1
 2:   iload_1
 3:   bipush  10
 5:   if_icmpge       25
 8:   iload_1
 9:   iconst_2
 10:  imul
 11:  istore_2
 12:  getstatic       #2; //Field java/lang/System.out:Ljava/io/PrintStream;
 15:  iload_2
 16:  invokevirtual   #3; //Method java/io/PrintStream.println:(I)V
 19:  iinc    1, 1
 22:  goto    2
 25:  return

命令の意味はJava Virtual Machine Specificationにあるそうなのですが…取り敢えず概要はひしだまさんのサイト”ひしだま's 技術メモページ”やWikipediaで理解しました。

変数の宣言に対応する命令は記述されておらず、初めて変数へ値が代入される際にその変数に関連する命令が登場しています。

以上より、(処理コスト低減を目的として)変数宣言を本来のスコープより外で行うことは、全く意味が無い、コストは完全に同一である、ということのようです。

EclipseやNetBeansで、変数宣言の行にブレークポイントが設定できないのはそういう理由なんですね、と言われて、なるほど、と思いました。

 

[追記 2011/3/11]

最近このページを訪れる方が多くいらっしゃるようなので、前述のサイトのC言語版についてもアセンブリを見てみました。

   1: 良い例:
   2: void test(){
   3:   int a, i, j;
   4:   for(i = 0;i < 100;i++){
   5:     for(j = 0;j < 100;j++){
   6:       a = i*j;
   7:     }
   8:   }
   9: }
  10:  
  11: 悪い例:
  12: void test(){
  13:   for(int i = 0;i < 100;i++){
  14:     for(int j = 0;j < 100;j++){
  15:       int a = i*j;
  16:     }
  17:   }
  18: }

Ubuntu10.04のgcc4.4.3で、-O0オプションをつけてみても同一のコードとなりました。0xffff * 0xffff回のループでも実行速度に差異はありません。また、上記Javaで試してみたのと同様デバッガ(GDB)でステップ実行したところ、変数宣言だけの行ではストップしませんでした。

つまり、Cにおいても変数宣言をループの外で行うことはコスト的に全く意味が無いということになります(リンク先にあるとおり、Microsoft製コンパイラのデバッグオプションを付けた場合、という限定された状況では意味があるのでしょうが。そういえばMicrosoftコンパイラはデバッグ用に変数宣言時特定の初期値を代入していたような…その分のコストかもしれません)。

結論として、現在ではループ外で変数宣言を行えばパフォーマンス改善につながる、というのは都市伝説である、と言い切ってしまってもよいのではないでしょうか。

« ハードディスク運が悪い その3 | トップページ | ScalaスケーラブルプログラミングをScala2.8RC1で読む その1 »

コメント

ソース保守の観点から見ても、あまりいい習慣ではないと思いました。例えば、以下のようなコードがあった場合、プログラマは最後の行で20が出力されるコードを書いたつもりでも、ミスタイプで10と出力されてしまいます。
int n = 0;
for (n = 0; n < 10; n++)
{
// do something
}
printf("n = %d\n", n);

for (int n = 11; n < 20; n++) // この変数nはfor文のスコープ内のみ。
{
// do something
}
printf("n = %d\n", n); // 本当は20と出力してほしい。が、10と出力される。

私の職場ではひとつのスコープ内に上記のようなループが何箇所のあり、これが原因でバグが出そうになったことがあります。このブログポストは”コスト的な観点な話”でしたが、保守性に関しても出羽さんのご意見を聞いてみたくて、コメントさせて頂きました。説明がわかりにくかったらすみません・・。

yukiさんこんばんは。

保守性の観点からは議論の余地無くスコープは可能な限り小さくとるべきだと私も考えています。

一応この意見は既に大勢を占めていると考えましたので、これを前提として置き、
にも拘らずスコープを小さくすべきでないとして挙げられる理由「パフォーマンスが低下する」が本当に妥当性のある意見なのかどうかを検証する、というのが本エントリの目的でした。

そして、検証の結果、パフォーマンス低下は全く無いことが分かったので大手を振ってスコープを縮められますね、というのが結論になります。

東京証券取引所システムarrowheadに対するエントリ
http://feather.cocolog-nifty.com/weblog/2010/09/arrowhead-7ec0.html
で別の実例も記載しているのですが、根拠のない(そして効果も全くない)パフォーマンス向上似非テクニックには、私もずいぶん手こずらされた経験があります…

これらは『時期尚早な最適化は諸悪の根源だ』
http://ja.wikipedia.org/wiki/%E6%9C%80%E9%81%A9%E5%8C%96_(%E6%83%85%E5%A0%B1%E5%B7%A5%E5%AD%A6)#.E6.9C.80.E9.81.A9.E5.8C.96.E3.81.99.E3.82.8B.E6.99.82.E6.9C.9F
というのが正に当てはまるケースだと思います(そして最適化に何の寄与もしない、つまりこのような提案を行う人は実際に検証した上で主張しているわけではない、のでより質が悪いと考えます)。

うう・・・・これやってた。気をつけよう。

コメントを書く

(ウェブ上には掲載しません)

トラックバック

この記事のトラックバックURL:
http://app.cocolog-nifty.com/t/trackback/18902/48327161

この記事へのトラックバック一覧です: ループ内で変数を宣言しない方が良いのか:

« ハードディスク運が悪い その3 | トップページ | ScalaスケーラブルプログラミングをScala2.8RC1で読む その1 »

other sites

  • follow us in feedly
  • github
  • stackoverflow

ソフトウェアエンジニアとして影響を受けた書籍

  • Christain Bauer: HIBERNATE イン アクション

    Christain Bauer: HIBERNATE イン アクション
    理論と実践が双方とも素晴らしい製品であるHibernate。本書はそのプロダクトを書名に冠していますが、Hibernateを使うつもりがなく、ORマッピングの解説書として読むにしても十分な良書です。Second EditionとしてJava Persistence With Hibernateという書籍も出版されていますが、残念ながら現在のところ 和訳はされていません。-インアクションは2.xの、Java Persistence-は3.1の頃のものなので、最新版とはちょっと違うところもあることに注意。 (★★★★★)

  • アンドリュー・S・タネンバウム: 分散システム 原理とパラダイム 第2版

    アンドリュー・S・タネンバウム: 分散システム 原理とパラダイム 第2版
    クライアント/サーバシステムを構築する上で必要となる知識が総論されてます。Web技術者も、フレームワーク部分を開発するのであれば必読。 (★★★★★)

  • Joel Spolsky∥著: ジョエル・オン・ソフトウェア

    Joel Spolsky∥著: ジョエル・オン・ソフトウェア
    前述の書籍「ソフトウエア開発プロフェッショナル」をより砕いたもの、という感じでしょうか。 前書きではプログラマでなくSE向けの本のように書かれているが、プログラマが読んでも面白い本であると思われます。 SEになった新人(あるいはそういう会社に入る/入りたての人)にとっては、これからどういったことが仕事を遂行していく上で起こりえるのか、どのように考えて行なっていけばいいのか決定する助けになると思います。 元は″Joel on Software″というブログの記事で、web上でも一部日本語で読めます。 http://japanese.joelonsoftware.com/ (★★★)

  • ドナルド・C・ゴース,ジェラルド・M・ワインバーグ: ライト、ついてますか

    ドナルド・C・ゴース,ジェラルド・M・ワインバーグ: ライト、ついてますか
    問題解決(一昔前のの流行語で言うところの『ソリューション』)能力は、システムエンジニアのスキルとして備えるべきもののうちのひとつです。しかし、これは難しい。学校で出されるテストと違い、唯一の、(問題提出者が想定している)解を求めるだけが「問題解決」では無いからです。そもそも、何が問題なのか、それは本当に問題なのか、それは本当に解決すべき問題なのか、その問題解決方法は正しいのか、などを解決しなければ、「その解は正しいのか」に辿りつくことができません。この本の最も良いところのひとつは、本があまり厚くないこと。すぐに読めるし、何回も読み返す気になるでしょう。 (★★★★★)

  • スティーブ・マコネル: ソフトウエア開発プロフェッショナル

    スティーブ・マコネル: ソフトウエア開発プロフェッショナル
    コードコンプリートで有名なスティーブマコネルの著書。新人SEに読んで欲しい。個人として業界の中でどうあるべきか、組織としてどうあるべきか、SEのプロ意識とは?SEの心構え概論、といったところでしょうか。また、業界における資格の重要性についても説かれています。この業界では資格が特に軽んじられる傾向がありますが、この傾向はどんな弊害をもたらすのか、将来的にこの業界は資格に対してどのような姿勢で臨んでいくべきなのか。日経BP社では(他の出版社もだが)最近、似たような類いのあまり面白くない書籍が乱出版されていますが、この本は別格だと思うので安心して購入して欲しいと思います。 (★★★★★)

無料ブログはココログ