パスワードを忘れた? アカウント作成
12019141 story
プログラミング

前置インクリメント(++x) よりも後置インクリメント(x++) のほうが高速? 129

ストーリー by headless
演算 部門より
insiderman 曰く、

C/C++には「++x」(前置インクリメント) や「x++」(後置インクリメント) のようなインクリメント演算子がある。前者はxをインクリメントしてからその値を返すのに対し、後者はxの値をコピーしてからxをインクリメントし、コピーした値を返す。後置インクリメントは値をコピーする分遅いと言われていたそうなのだが、これとは逆の主張が話題になっているそうだ(闇夜のC++)。

これによると、前置インクリメントはインクリメント処理が終わるまで値を返せないのに対し、後置インクリメントはインクリメント処理が終わる前に値を返すことができる。そのため並列処理が可能になるなどのメリットがあるという。ただし、オブジェクトをコピーするコストについては議論されていないとのことなので、コピーに必要なコストが大きい場合は逆に処理が遅くなる可能性もある。

そもそもタレコミ子は前置インクリメントと後置インクリメントの処理の違いについては把握していたが、処理コストの違いについては知らなかったので非常に勉強になった(言われれば確かにそうだと理解できるのだが)。

記事で話題となっている書籍の英語版「Game Engine Architecture, Second Edition」は、該当部分をGoogleブックスで閲覧できるので、参照してほしい。

この議論は賞味期限が切れたので、アーカイブ化されています。 新たにコメントを付けることはできません。
  • by Anonymous Coward on 2015年04月19日 18時31分 (#2800153)

    都市伝説が形を変えて流風しているじゃないかな。(ただし、PDP-11のことは、かつては「常識」だった)。

    このあたりのそこそこ説得力のある話は、
    http://ja.wikipedia.org/wiki/PDP-11#.E5.91.BD.E4.BB.A4.E3.82.BB.E3.83.... [wikipedia.org]
    に書いてある。

    • by Anonymous Coward on 2015年04月20日 8時50分 (#2800376)

      時代とともにハードウェアも変われば「常識」も変わるというだけの話だな。似たような例として、「floatのほうが速い」→「コプロセッサがあるからfloatもdoubleも変わらない、むしろいちいち型変換が入る分floatのほうが遅い」→「floatのほうがSIMD命令で並列処理できる分速い」という変遷があった。
      ところで2番めを有名にした記事はこれ [pro.or.jp]なんだが、

      なお、どっちが速いか、どのくらいの差が出るかは、マシンやコンパイラ、数値演算チッ プに依存するところが多いので、実際に使うマシンで確認してください。「floatでなければ遅い」 という迷信だけは捨ててください。

      としっかり書かれているのに、この一番肝心な部分は無視されて新しい迷信を作り出しちゃったというのがもうね。

      親コメント
  • by Montague62 (1238) on 2015年04月19日 20時36分 (#2800206)

    b = a[i++] みたいな場合、
    まず a[i] の中身を取りに行く
    もう i は参照しちゃったので、i は変更可
    a[i] の中身を取りに行きながら、並行して i もインクリメント => 速い

    だけど、b = a[++] だと
    まず i をインクリメント
    インクリメントが終わらなきゃ、どこにアクセスしていいか分からない
    並行不可 => 遅い

    for( ほげ; ふが; i++) とか for( ほげ; ふが; ++i) の場合
    コンパイラが最適化しちゃうので、どっちも同じ

    結論: 習慣化する (≒考えずにやる) なら、後置で。

  • 業務アプリケーション開発してる自分の周りでは・・・

    i++って書いたら、これ何?(トリッキーな書き方してんじゃねーよという雰囲気)

    「++」が使える言語で、VBの癖なのかiKingaku = iKingaku + 1とかstrSql += strSql のようなシステムハンガリアンを未だに普通に見かける

    とかそんなレベルよ。
    どっちが速いとかなんとか!?業務アプリのレベルでは全く関係ない。
    組み込み系か、ゲームのAPI開発くらいでないの?

  • by Anonymous Coward on 2015年04月19日 17時07分 (#2800107)

    動作の違うものを比較しても意味がない

    • by shima_tetsuo (46446) on 2015年04月20日 12時47分 (#2800497) 日記

      いやほんとにその通りだ。
      for (int i = 0; i num; ++i)
      for (int i = 0; i num; i++)
      でどちらが高速かという話ならまだしも、無意味な比較にしか思えない。

      親コメント
    • by Anonymous Coward on 2015年04月20日 13時12分 (#2800524)

      プログラムの意味について議論しているのであれば仰るとおりですが、
      議論されている内容はプログラムの意味ではありません。
      少なくともそれだけを議論しているわけではありません。
      なので比較する意味はあります。

      親コメント
    • by Anonymous Coward

      用途も違いますからな。ただどちらかが軽いなら軽い方をつかうべきではある。
      個人的にはよほどリソースが逼迫しているか顧客からの要求が厳しくない限りはパフォーマンス上有利な方よりも処理に適した方を使いたい。

  • この手の話はよく出るけど、最終的には計測結果だよね。
    タレこみにあるリンク先でもマシンコードで一致する例などそこそこのことはしているが、ちゃんと意味を持たせるなら現実的な処理での計測結果がないとね。

    GAMEを作るような場合はぎりぎりの性能を目指すからそうでもないのかな?

    • PS3,PS4,XBox 360,XBox Oneなどで計測できて,その結果を公表できる人を待ち望んでいます.
      ベンチマーク結果って秘密保持契約に引っかかるのだろうか…?

      親コメント
    • by Anonymous Coward

      並列化を考慮する必要があるんだし、ケースバイケースになるんで

      単に計測したからといって結論が出るもんじゃ

      ないんだよ

      • ケースバイケースだから実測が重要なのだが...

        親コメント
        • by Anonymous Coward

          分かってないな

          結果に影響を及ぼしうる因子なんて無数に考えられるわけで

          単に、やってみた、で結論が出るもんじゃないんだよ

          • by Anonymous Coward

            実測って一種類の結果しか出ないものと思ってない?

            • by Anonymous Coward

              無数の因子の組み合わせを全て実測することは不可能なわけで

            • by Anonymous Coward

              因子のパターンが多すぎて結果出すのもしんどすぎるって話じゃないの

          • by Anonymous Coward

            > 結果に影響を及ぼしうる因子なんて無数に考えられるわけで

            そういう場合は「ほとんど影響なし」という単純な結論になるな

    • by Anonymous Coward

      ゲーム機もそれなりに複雑なOSがのっていたいたりで今回の例のインクリメント記法の違いは無視できる誤差かと。
      これを気にするくらいならゲームロジック全体を見直して無駄を省く方が精神衛生上よろしいと思います。
      ネトゲにいたってはそんなチューニングも無意味なくらい全体的に非同期で動かすことが前提ですし…
      ただシェーダーまわりはまだギリギリを狙うメリットが大きいかな。

      • 今の開発環境なら++が前か後か無視できるだろという意見には同意。
        ただ、昔は最適化が下手なコンパイラやちょっとしたテクニックで性能改善があった経験があるので、無下に否定する気はない。
        ぎりぎり狙うならコンパイラーの癖を考慮したコーディング規約くらい作るかもしれないとは思っている。一般化できないことだし、根拠薄い思いだけど。

        現場の人の意見が聞きたいな。

        親コメント
        • by Anonymous Coward on 2015年04月19日 21時21分 (#2800224)

          プロファイルをとって結果が有意であれば規約化というのはありだと思いますが、今回の前置後置の話レベルはやはり誤差かなあと。
          整数の割り算だとキャストしてでも逆数をかけた方が速いとかはあるかな。

          どちらかというとコードキャッシュ、データキャッシュを生かす実行単位を維持することの方が効果高いと思っています。
          ループ処理とか余計なif文をなくす設計とか。
          今時の規模だとデータキャッシュはようわからんというところでしょうが、コードをコンパクトにすることはまだコントロールできると思います。
          ここまでくるとゲームとか関係なくて、なんとなくでコード書いちゃ駄目だよねーってだけの話ではないかと。

          親コメント
    • by Anonymous Coward

      ブログ記事の方の感想も、どちらかと言うと効果に疑問を投げかける姿勢ですね。

      私は、後置インクリメントを使っている事を指摘した際に「だってゲームエンジン・アーキテクチャに書いてあったからなんとなく」という答えをするプログラマが増えない事だけを願っています。

      というコメントが象徴的かなと思います。

  • C++としてできるのはいいけど、オブジェクトに++/--を適用したくないor適用するなら(そこそこ)アトミックである特性を維持してほしかった...感が。

    # +=1の処理になる、ではない、んだよね?

    --
    M-FalconSky (暑いか寒い)
  • by Anonymous Coward on 2015年04月19日 17時13分 (#2800108)

    Cは速度変わらないけど
    C++ はオーバーロードの関係で余計な処理が入ってた覚えがある

  • by Anonymous Coward on 2015年04月19日 17時15分 (#2800112)

    騒ぐほど拘るとこかね?

  • by Anonymous Coward on 2015年04月19日 17時15分 (#2800113)

    ストールが問題になるにはデータ依存が存在していることが必要で、データ依存の有無はアルゴリズムによって決まります。
    インクリメントが後置か前置かとは別の話でしょう。
    前置インクリメントを使ったためにデータ依存が新たに生じることはありません。
    (生じるとすれば、アルゴリズムが変わっています。)

    影響があるとすればコンパイラの最適化でしょうが、前置インクリメントの方が一般に処理が単純なので、
    これを使ったために、データ依存解析に失敗するケースというのはちょっと想像がつきませんね。

    逆に、後置インクリメントを使うことによって生じるコピーコンストラクタの削除にコンパイラが失敗するケースなら十分あり得ます。
    一般論として、コピーコンストラクタを削除するには手続き間解析(少なくともインライン化)が必要ですが、
    手続き間解析はコストが大きいため、現行のコンパイラは、これを完全には行えていないからです。
    (そもそも、コピーコンストラクタのソースコードが提供されていない場合、リンク時最適化を利用しない限り、最適化はできません。)

    • by Anonymous Coward on 2015年04月19日 18時20分 (#2800146)

      うん。リンク先も、単純な場合はコンパイラが勝手に上手いことやってくれる、までは良いんだけど、そこから迷走しだしてるね。

      前置/後置の差で不自然なストールを演出しようとしても、
      わざわざそこでソースを分割して最適化出来ないような工夫さえしてなければ、
      本質的にストールを考慮て最適化する必要があるかどうか、コンパイラには見抜かれる。

      コンパイラの最適化の限界を知っておくのはなかなかおもしろい。
      ソースコードを愚直に実行するとものすごく無駄な手順を踏むことになるけど、
      構造化されてて書きやすく、従ってバグを入れにくいような書き方を気楽に使える。

      例えば、2×2行列の掛け算みたいな、手で書くと最小限のコードに出来そうだけどどこか書き間違えてしまいそうな処理も、
      汎用的なn×n行列の掛け算インライン関数を呼んでおけば、
      何だかループがアンロールされて、必死こいて必要最小限だけ手で書いたやつと似たような結果になる。
      出来合いの関数が無い処理でも、こういうのは、添え字を丁寧に書き並べるより、ループを使ったほうが書き間違いをしづらいし。

      親コメント
    • by Anonymous Coward

      この問題はアルゴリズムやコンパイラレベルの話ではなく、CPU内部のパイプラインの話なのだが…。

      • ストールを気にするのなんてループの中くらいだし、それなら今時のCPUだと上手く隠してくれそうな気がするする。

        親コメント
      • by suezo (2881) on 2015年04月19日 21時30分 (#2800229) 日記

        インクリメント前の変数を持つ時点でreturnする前の処理は多いと思うのだが?

        親コメント
      • 同感。元ねたがコンパイラの話とCPUの話をごっちゃにしているのが間違い。常識が覆ることがあったのはCPUの進歩で動的に平行処理されるようになったから。

        親コメント
  • by Anonymous Coward on 2015年04月19日 17時36分 (#2800120)

    286時代は前置の方が早いと聞いていたので、前置が習慣になった。
    今時パソコン向けならどっちでもいいと思うけどね。

    • by taka2 (14791) on 2015年04月20日 14時14分 (#2800562) ホームページ 日記

      68000使いだと、
      デクリメントは前置、
      インクリメントは後置、
      ですね。

      スタックの処理は通常、
      push時: スタックポインタをデクリメントしてから、スタックポインタの指すメモリに書き込み
      pop時: スタックポインタの指すメモリを読み込んでから、スタックポインタをインクリメント
      という処理になります。
      で、スタック処理を高速化するために、68000ではこれらの処理は一命令で処理できるようになっていました。

      でもって、68000はスタックポインタも汎用レジスタの一つ、という直交性の高い設計なので、汎用レジスタに対しても前置デクリメントと後置インクリメントは一命令で処理できるからちょっと速い。
      #正確に言えば、アドレスレジスタについての話なので、「*--p」「*p++」が一命令処理。

      親コメント
  • by Anonymous Coward on 2015年04月19日 17時59分 (#2800126)

    宗教チックになって、コードを書く人間によって
    記載方法が変わったりしない?

    品質面を考えそっちのほうが不安なんだが、、、
    もうしゅこし、まともなタレコミがほしい。

  • by Anonymous Coward on 2015年04月19日 18時10分 (#2800132)
    前世紀の話ですが、-- は前置、++ は後置が速いという話もありました。
    対象 CPU が何だったのか記憶にありませんが、x86系でないのは確か。
    と、ここまで書いて、逆だったかと思い始めた。。。
    • by bero (5057) on 2015年04月20日 13時40分 (#2800541) 日記

      たぶんMC680x0系
      ただし単なる--,++ではなく、
      *--p 、*p++のようにポインタを増減して指す値を使う場合。
      前者がpush、後者がpopに相当するので、スタックポインタ用に高速な命令が存在する。
      そしてスタックポインタとして汎用レジスタの一つを使うCPUでは、他の汎用レジスタでも使える。

      H8やSHもこのアドレッシングモードを踏襲してたと思う。

      親コメント
    • by Anonymous Coward

      mips fujituu1600 IBM 分からないテープが動いていた
      日立わからない1++しかなかった方打ち間違い起きたとこ。
      COBOL85しか知らない1++を使っていた、パソコン使って書いてる、最新のoSX今はIMEつていうのキーボードばかり汎用機ばかりで慣れない
      済まない
      は僕の癖(クセ)

    • by Anonymous Coward

      正解だよ。PDP-11 のアセンブラのアドレッシングモード(わかる?)だと、--iとi++しかないからね。でも、これは都市伝説の類。

  • by Anonymous Coward on 2015年04月19日 18時16分 (#2800138)

    で、そこ並列化されてる言語って?

typodupeerror

192.168.0.1は、私が使っている IPアドレスですので勝手に使わないでください --- ある通りすがり

読み込み中...