開発を担当している井出真広(@imasahiro)です。Processingを使った、最適化されたString.formatメソッドを自動生成した事例を紹介します。String.formatApplicationを記述する上で、logging、APIのresponseを構築するためなどのために、様々なデータの変換が行われています。paddingするなど、入力された書式に基づいて文字列に変換することは日常的に行われています。そのような目的を実現するため、Java言語ではJava5から書式付き文字列を生成するためメソッドとしてString.formatが導入されました。%03d)を含んだ文字列)を元に整形して新たに文字列を生成するJava言語のメソッドです。このようなメソッドは多くのプログラミング言語に存在し、例えばC/C++言語ではsnprintfがあります。実際、String.formatは数値や文字列を整形するのに便利なので、よく使われるメソッドです。その実行は低速であり、場合によってはアプリケーション全体の実行に影響を与えます。optimizeされた書式付き文字列を構築するコードを書くことも少なくありません。String.formatが登場したばかりのJava5の時代も今は昔、Just-In-Timeコンパイラも大きく進化したはずの現代においてそんなことは考える必要が無いのではないと信じていましたが、世の中はそう甘くはありません。optimizeをしなければいけない事実に不満を感じていました。現実問題として、整形された文字の構築を行う場面はログ出力を含めて日常的に遭遇します。そのためString.formatを最適化すれば、様々なApplicationがより高速に動作できるのでは?と考えるようになりました。は遅いのか?本題に入る前に、なぜString.formatが遅いのかについて考えてみましょう。String.formatは実際にはFormat.formatメソッドで実装されています。このformatメソッドは大きく分けて2つのphaseで構成されています。1つ目のphaseでは書式文字列をパースします。OpenJDKのコードを確認してみると、正規表現を使いながらparseしていることが確認できます。2つ目のフェーズでは、1つ目のフェーズで見つけた書式指示子に従って、実際の文字列に変換していきます。[code]String.formatがどのような挙動をするか概要が掴めたところで、ほとんど自明ではありますが、実際に各フェーズがどの程度の時間を占めているかを計測してみましょう。以下はString.formatを使って数値を文字列に変換する例です。}}そして、表1がasync-profilerを用いて計測した結果になります。ここで、サンプル数、percentが多いほどそのメソッドがより多くの実行時間を占めており、objectを文字列に変換をするFormatSpecifier.printの実行の他に、書式文字列を正規表現を用いてパースしている時間がかなり含まれていることが確認できます。java.util.ArrayList.addこのように、String.formatでは、単に文字列への変換だけでなく、書式文字列のパースを合わせて行なっています。多くの場合、String.formatで指定される書式文字列は変化しないと仮定すると、このパースの時間を削減できれば、トータルの実行時間を削減できます。Processingによる書式付き文字列の変換ここまでで、パース処理にかかる時間を削減できれば、formatの実行時間を削減できることがわかりました。一つの方法は、正規表現のマッチで実現しているパース処理を高速化する方法です。1回にかかる時間を削減できますが、パース処理を完全には削減できるわけではありません。そのため、繰り返しString.formatを呼び出された場合を考えると、削減された実行時間に比べて最適化の労力に見合わないかもしれません。一つの方法はパース処理自体を事前に終わらせる方法です。こちらの方法ではパース処理は実行前に完了しておき、String.formatの呼び出しの際にはパース結果を使って文字列変換するだけです。そのため、String.formatが繰り返し呼び出された場合でも実行速度の点で満足できる結果を得られそうです。Processingと呼ばれる仕組みを使ってコードのコンパイル時に新たにコードを生成する仕組みを提供しています。APIとして導入された仕組みで、コンパイル時にAnnotationをscanして処理し、Javaコードを入力としてファイル(通常は.javaファイル)を出力します。Processingの面白いところは、Javaコードに記述された内容をJava言語を使ってコンパイル時に新たにJavaコードを生成できる点です。libraryで広く利用されています。typeを生成するために利用しています。Processingではすでに存在するソースコードに対して変更を加えることはできず、常に新しいファイルを生成するための仕組みという点に注意が必要です。auto-string-formatterを作成しました。Processorの1つとして動作します。auto-string-formatterは@AutoStringFormatterのannotationに付随する書式文字列を解析し、StringBuilderを使用したJavaコードに変換します。表2、表3のJavaコードはauto-string-formatterの使用例と変換後のJavaコードを示しています。Processing後ではJavaコードに変換されていることが確認できます。}}そして、表4がasync-profileによるプロファイル結果となります。プロファイル結果からも、正規表現のマッチ処理が消えていることが確認できます。optimizeしたコードの実行時間を比較しました。おわりにこの記事では、String.formatの挙動となぜ遅いかについて説明しました。Processor「auto-string-formatter」を紹介しました。auto-string-formatterはbintrayにpublishしており、Gradleであれば以下のように指定すれば利用可能です。Developers。