凪瀬 Blog
Programming SHOT BAR

目次

Blog 利用状況
  • 投稿数 - 260
  • 記事 - 0
  • コメント - 47014
  • トラックバック - 192
ニュース
広告
  • Java開発者募集中
  • 経歴不問
  • 腕に自信のある方
  • 富山市内
  • (株)凪瀬アーキテクツ
アクセサリ
  • あわせて読みたい
凪瀬悠輝(なぎせ ゆうき)
  • Java技術者
  • お茶好き。カクテル好き。
  • 所属は(株)凪瀬アーキテクツ
  • Twitter:@nagise

書庫

日記カテゴリ

 

クイズの解答です。

このクイズの趣旨は文字列連結演算子の+がどのように働くのかという知識を問うものでした。

まずは実行してみましょう。結果は以下のとおり。

A : 80026931
B : 143935333
C : 388877
D : 2152509

単位はns(ナノ秒)ですから、見やすくすると

メソッドナノ秒
A80,026,931
B143,935,333
C388,877
D2,152,509

となりますので、C,D,A,Bの順となりますね。

なぜ速度に差が出るのか?

Java言語仕様 第3版には「15.18.1 文字列連結演算子 +」という章があります。 今回のクイズは、このあたりの仕様が基準となります。

StringはいわゆるImmutableパターンという奴で、インスタンスが作られた際に値が決められ、 以後内部の値が変わることがありません。ですから、Stringの結合などは、 その都度新しいオブジェクトが作られ、古いオブジェクトは破棄されるわけです。

文字列連結の+はコンパイルの時点でStringBuilderのappend()による連結に置き換えられます。 そして、String型の変数に入るたびにStringオブジェクトを作り、古いStringオブジェクトが破棄されます。 String型はいわばchar型の配列ですから、結構生成にコストがかかります。 ここがパフォーマンスに大きな影響を及ぼすところですね。

さて、Stringのリテラル(つまるところダブルクォートに囲まれてソース中に書かれた文字列)同士の +演算はコンパイル時点で最適化されて、あらかじめ結合済みの文字列となる場合があります。
問題編でのかつのりさんの回答の

A,CとB,Dの違いは、
"hogepiyo"か"hoge","piyo"の違いです。

というところが正にここにあたります。"hoge" + "piyo"がコンパイル時にあらかじめ"hogepiyo"になるのですね。

変数との+演算の場合はこの手抜きができませんから、

str += "hoge";
str += "piyo";

としてしまうと

str += "hoge" + "piyo";

よりも遅くなってしまう。

C,DではStringBuilderを用いてStringインスタンスの生成を行わずに文字列結合しているから早く、 Dが2回appendを実行するのに対し、Cは最適化によって"hogepiyo"を1回appendするだけで済むのでCが最も早いわけです。

最適化は実装依存という落とし穴

Java言語仕様の15.18.1.2に「文字列連結の最適化」という項があります。

 一時的なStringオブジェクトの生成と破棄を回避するため、変換と連結を単一のステップで実行すること が実装に対して許されている。また、連続して文字列連結を行う際におけるパフォーマンスの向上を目的とし て、クラスStringBufferや同種の手法を採用し、式の評価中に生成される一時的なStringオブジェクト数 を削減することがJavaコンパイラに対して許されている
 プリミティブ型の場合、プリミティブ型を文字列に直接変換することによって、ラッパ・オブジェクトの生 成を回避するような最適化も実装に対して許されている。

強調は私によるものです。前者の強調部分はVMがコードを実行する際の話であり、 後者はコンパイラがコードを書き出す場合の話です。

じつはこの部分は実装依存なので、使用するコンパイラと計測に用いるVMによって結果が変わることがありえるのです。

「リテラルの文字列の+演算はどうせ連結してコンパイルされるから関係ない」というのは あるJavaコンパイラでは真だとしても、ほかのコンパイラでは違うかもしれません。
実際、Eclipse3.3で以下のコードをコンパイルすると

int i = 1;
System.out.println(i + "hoge" "piyo");

 0  iconst_1
 1  istore_1 [i]
 2  getstatic java.lang.System.out : java.io.PrintStream [16]
 5  new java.lang.StringBuilder [22]
 8  dup
 9  iload_1 [i]
10  invokestatic java.lang.String.valueOf(int) : java.lang.String [24]
13  invokespecial java.lang.StringBuilder(java.lang.String) [30]
16  ldc <String "hoge"> [33]
18  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [35]
21  ldc <String "piyo"> [39]
23  invokevirtual java.lang.StringBuilder.append(java.lang.String) : java.lang.StringBuilder [35]
26  invokevirtual java.lang.StringBuilder.toString() : java.lang.String [41]
29  invokevirtual java.io.PrintStream.println(java.lang.String) : void [45]
32  return

ってな感じのバイトコードが出力されます。
"hogepiyo"にならずに2回に分けて連結されていますね。 しかし、すべてのJavaコンパイラがこのようにコードを吐くとは限りません。 ちゃんと"hogepiyo"と最適化するコンパイラも存在するかもしれません。

結論

というわけで、AとB、CとDの順番は入れ替わる可能性があります。 このあたりについて考察があれば満点を差し上げたところなのですが…。

takaさん、かつのりさん、8点ということでお開きにしたいと思います。

投稿日時 : 2007年11月27日 23:05
コメント
  • # re: Javaの文字列連結パフォーマンスクイズ回答編
    やまだ
    Posted @ 2007/11/28 0:18
    回答→解答ですね、とまず茶々。

    >じつはこの部分は実装依存なので、
    ありゃ、昔は String の + は、StringBuffer の append と toString に展開される、って定義されてた気がするなぁ。
    ちょっと今手元には VM 仕様の本しかない(^^;ので、(覚えてれば)今度確認しておきます。
  • # re: Javaの文字列連結パフォーマンスクイズ解答編
    凪瀬
    Posted @ 2007/11/28 0:26
    おおっと。誤字やっちゃってますねorz
    修正しておきました。指摘ありがとうございます。

    展開されるけども「最適化をすることができる」という扱いですね。
    本文中で引用していますが、「許されている」という表現になっているところがポイント。

    1.4以前ではStringBufferでしたし、1.5からはStringBuilderになっています。これが「同種の手法」の部分でしょうね。
    このあたりは実装によってブレがある部分ではないでしょうか。
  • # re: Javaの文字列連結パフォーマンスクイズ解答編
    やまだ
    Posted @ 2007/11/30 23:04
    >展開されるけども「最適化をすることができる」という扱いですね。

    そういう定義だから最適化なんかしちゃいかん、ということだと思ってました。
    本でざっと仕様を確認してみましたけど、たしかに最適化自体を否定した表記はなさそうでしたね。
    #ということで、私の記憶違いだったようです。お騒がせしましたー。
  • # re: Javaの文字列連結パフォーマンスクイズ解答編
    bleis-tift
    Posted @ 2009/08/24 21:17
    トラックバックの送信に失敗するので、手動で失礼します。
    http://d.hatena.ne.jp/bleis-tift/20090824/1251115969
  • # MloxoytLUOqUjPEdifZ
    http://www.discreetpharmacist.com/
    Posted @ 2011/12/22 20:46
    LjrOjY I was looking for the report in Yandex and suddenly came across this page. I found a little information on my topic of my report. I would like more, and thanks for that..!
タイトル
名前
Url
コメント