#Java9 #String 文字列結合のパフォーマンス向上について
2017年9月にリリースされたJava絡みの公式チュートリアルまとめ
これは何の記事?
いつかやってみるかもしれないチュートリアルをまとめておく。
一覧
JUnit 5 User Guide
- 2017/9/10リリース。
- PDFへのリンクあり
Java Shell User’s Guide
- 2017/9/21リリース
Java Platform, Standard Edition Java Shell User’s Guide - Contents
Project Jigsaw: Quick Start Guide
- 2017/9/21リリース
Project Jigsaw: Quick Start Guide
Java EE Tutorial
- 2017/9/21リリース。
半角英字の結合をしたらJava8より遅くなった件の調査【原因不明】 #Java9 #String
これの続編です。 結論から言うと謎が深まっただけです。 tadashi.hatenablog.com
LATIN1/UTF16 の判定が追加になっている、と書いてあるので、判定用の値を取得する coder()メソッドの結果を見てみることにしました。 d.hatena.ne.jp
ソースはこちらです。
※リフレクションを使っています。
https://github.com/suzukitadashi/jmhTest/blob/master/jmhTest/src/main/java/jmhTest/StringTest.java
package jmhTest; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class StringTest { private static String[] list = {"a","b","c","d","e","f","g","h","i", "j"}; public static void main(String... args) { // String[] val = {"", "a", "1", "あ"}; // for(String str : val) { // checkCoder(str); // } test1StringLine(); } private static void checkCoder(String str) { System.out.println("------"); Class<String> c = String.class; try { Method m = c.getDeclaredMethod("coder"); m.setAccessible(true); byte b = (byte)m.invoke(str); System.out.println(str + " coder(): "+b); Field f = c.getDeclaredField("coder"); f.setAccessible(true); byte b1 = f.getByte(str); System.out.println(str + " coder: "+b1); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } } public static void test1StringLine() { int i = 0; String str0 = list[i]; checkCoder(str0); i++; String str1 = list[i]; checkCoder(str1); i++; String str2 = list[i]; checkCoder(str2); i++; String str3 = list[i]; checkCoder(str3); i++; String str4 = list[i]; checkCoder(str4); i++; String str5 = list[i]; checkCoder(str5); i++; String str6 = list[i]; checkCoder(str6); i++; String str7 = list[i]; checkCoder(str7); i++; String str8 = list[i]; checkCoder(str8); i++; String str9 = list[i]; checkCoder(str9); String str = str0 + str1 + str2 + str3 + str4 + str5 + str6 + str7 + str8 + str9; checkCoder(str); } }
遅くなる理由は、LATIN1で判定して違う、ということでUTF-16の処理になるために処理時間が遅延しているのでは、と推測しました。
さて、結果はというと…
------ a coder(): 0 a coder: 0 ------ b coder(): 0 b coder: 0 ------ c coder(): 0 c coder: 0 ------ d coder(): 0 d coder: 0 ------ e coder(): 0 e coder: 0 ------ f coder(): 0 f coder: 0 ------ g coder(): 0 g coder: 0 ------ h coder(): 0 h coder: 0 ------ i coder(): 0 i coder: 0 ------ j coder(): 0 j coder: 0 ------ abcdefghij coder(): 0 abcdefghij coder: 0
coderの値が0ならLATIN1、1ならUTF-16と判定されるロジックなのですが、LATIN1と判定されているようです。
うーん、なんで半角数字同様のパフォーマンスが出てくれないのだろう…?
正式版Java9で文字列の結合の速度を確認する #Java9 #String
これは何?
2017/9/21にJava9が正式にリリースされました。 Java9ではStringのあれこれが変わったと聞いていますが、そんなことはどうでもいいのです。
維持屋にとっては、バージョンアップ後のパフォーマンスがどうなるのか、が重要なのです!!(極論)
前提
これと同じプログラムを使ってテストしました。 qiita.com
@YujiSoftware さんのブログで、VMオプションについて記載されていたのでそれについても試しています。
githubのソースの場所はこちら。 github.com
jmhの起動方法などはこちらにまとめています。 https://github.com/suzukitadashi/jmhTest/blob/master/jmhTest/command.txt
テストした端末のスペックなどは以下の通りです。
- Windows10 64bit
- Intel® Core™ i7-2820QM CPU @ 2.30GHz
- メモリは8GM
結果
注釈
- スマホからだと見づらいので、パソコンにするか、横にして画面を広げるか、などをお勧めします。
- Scoreの数字が大きいほど、パフォーマンスがいいと言えます。
半角英字
VM version: JDK 1.8.0_144, VM 25.144-b01
Benchmark Mode Cnt Score Error Units StringLineHankaku.call1StringLine thrpt 10 141.099 ± 1.144 ops/s StringLineHankaku.call2StringConcat thrpt 10 35.888 ± 0.221 ops/s StringLineHankaku.call3StringBuffer thrpt 10 97.689 ± 0.812 ops/s StringLineHankaku.call4StringBuilder thrpt 10 98.916 ± 3.052 ops/s
VM version: JDK 9-ea, VM 9-ea+134(Early Access版)
Benchmark Mode Cnt Score Error Units StringLineHankaku.call1StringLine thrpt 10 90.187 ± 2.961 ops/s StringLineHankaku.call2StringConcat thrpt 10 46.822 ± 3.277 ops/s StringLineHankaku.call3StringBuffer thrpt 10 68.937 ± 2.796 ops/s StringLineHankaku.call4StringBuilder thrpt 10 78.742 ± 2.665 ops/s
VM version: JDK 9, VM 9+181
VM options: なし
Benchmark Mode Cnt Score Error Units StringLineHankaku.call1StringLine thrpt 10 92.795 ± 2.711 ops/s StringLineHankaku.call2StringConcat thrpt 10 27.106 ± 17.205 ops/s StringLineHankaku.call3StringBuffer thrpt 10 53.108 ± 21.248 ops/s StringLineHankaku.call4StringBuilder thrpt 10 62.540 ± 21.275 ops/s
VM options: -XX:-CompactStrings -DCompactStringEnabled=false
Benchmark Mode Cnt Score Error Units StringLineHankaku.call1StringLine thrpt 10 108.819 ± 4.212 ops/s StringLineHankaku.call2StringConcat thrpt 10 50.795 ± 0.952 ops/s StringLineHankaku.call3StringBuffer thrpt 10 56.127 ± 0.568 ops/s StringLineHankaku.call4StringBuilder thrpt 10 104.129 ± 1.707 ops/s
半角数字
VM version: JDK 1.8.0_144, VM 25.144-b01
Benchmark Mode Cnt Score Error Units StringLineNumber.call1StringLine thrpt 10 112.127 ± 34.207 ops/s StringLineNumber.call2StringConcat thrpt 10 71.693 ± 0.270 ops/s StringLineNumber.call3StringBuffer thrpt 10 103.061 ± 1.590 ops/s StringLineNumber.call4StringBuilder thrpt 10 88.149 ± 35.149 ops/s
結果全文
VM version: JDK 9-ea, VM 9-ea+134(Early Access版)
Benchmark Mode Cnt Score Error Units StringLineNumber.call1StringLine thrpt 10 245.689 ± 13.801 ops/s StringLineNumber.call2StringConcat thrpt 10 132.742 ± 9.051 ops/s StringLineNumber.call3StringBuffer thrpt 10 49.300 ± 1.349 ops/s StringLineNumber.call4StringBuilder thrpt 10 52.594 ± 2.545 ops/s
結果全文
VM version: JDK 9, VM 9+181
VM options: なし
Benchmark Mode Cnt Score Error Units StringLineNumber.call1StringLine thrpt 10 187.399 ± 111.698 ops/s StringLineNumber.call2StringConcat thrpt 10 103.961 ± 50.983 ops/s StringLineNumber.call3StringBuffer thrpt 10 33.543 ± 21.675 ops/s StringLineNumber.call4StringBuilder thrpt 10 51.688 ± 0.346 ops/s
結果全文
VM options: -XX:-CompactStrings -DCompactStringEnabled=false
Benchmark Mode Cnt Score Error Units StringLineNumber.call1StringLine thrpt 10 238.952 ± 12.086 ops/s StringLineNumber.call2StringConcat thrpt 10 107.838 ± 3.610 ops/s StringLineNumber.call3StringBuffer thrpt 10 101.667 ± 97.174 ops/s StringLineNumber.call4StringBuilder thrpt 10 177.984 ± 3.698 ops/s
結果全文
全角文字
VM version: JDK 1.8.0_144, VM 25.144-b01
Benchmark Mode Cnt Score Error Units StringLineZenkaku.call1StringLine thrpt 10 141.133 ± 1.504 ops/s StringLineZenkaku.call2StringConcat thrpt 10 36.212 ± 0.202 ops/s StringLineZenkaku.call3StringBuffer thrpt 10 97.025 ± 2.155 ops/s StringLineZenkaku.call4StringBuilder thrpt 10 99.454 ± 1.509 ops/s
VM version: JDK 9-ea, VM 9-ea+134(Early Access版)
Benchmark Mode Cnt Score Error Units StringLineZenkaku.call1StringLine thrpt 10 92.686 ± 0.299 ops/s StringLineZenkaku.call2StringConcat thrpt 10 49.554 ± 0.684 ops/s StringLineZenkaku.call3StringBuffer thrpt 10 56.950 ± 6.474 ops/s StringLineZenkaku.call4StringBuilder thrpt 10 69.917 ± 0.210 ops/s
VM version: JDK 9, VM 9+181
VM options: なし
Benchmark Mode Cnt Score Error Units StringLineZenkaku.call1StringLine thrpt 10 93.176 ± 1.204 ops/s StringLineZenkaku.call2StringConcat thrpt 10 48.488 ± 0.728 ops/s StringLineZenkaku.call3StringBuffer thrpt 10 55.984 ± 6.048 ops/s StringLineZenkaku.call4StringBuilder thrpt 10 63.517 ± 0.586 ops/s
結果全文
VM options: -XX:-CompactStrings -DCompactStringEnabled=false
Benchmark Mode Cnt Score Error Units StringLineZenkaku.call1StringLine thrpt 10 109.511 ± 1.972 ops/s StringLineZenkaku.call2StringConcat thrpt 10 50.061 ± 2.419 ops/s StringLineZenkaku.call3StringBuffer thrpt 10 55.549 ± 0.624 ops/s StringLineZenkaku.call4StringBuilder thrpt 10 104.124 ± 1.103 ops/s
結果全文
まとめ
数値でない場合の結合速度は爆速になるどころか、遅くなっている。
- @YujiSoftware さんのブログを読む限りでは、半角英字は速くなってもいいはずなのだが…テストプログラムの書き方の問題であろうか?
VMオプションを付けると、StringBuilderもStringと同等のパフォーマンスになることがある模様。
- Java9に挙げた結果、パフォーマンスが遅くなって困るときはVMオプションの付与を検討してもいいかも。[要検証]
お願い
- 違う結果が出たよ、というのがありましたらテスト方法含めてどこかにまとめたうえで教えてください。勉強しに行きます。
JasperReportsを使うときのライブラリについて
クラスメソッド様のブログにはいつもお世話になっております。
2013年に出たブログですが、今でもたまにたどり着くこのページ。 依存ライブラリの部分がmavenだと若干古くなっているようなのでメモとして残しておく。
JasperReports LibraryはPDFを出力する場合にitextを利用しますが、この時に依存しているバージョンはLGPLである2.1系の最新バージョンである2.1.7にJasperReportsで独自のパッチを当てた、2.1.7.js2です。このバージョンはMavenのcentral repositoryに存在しないため、Jaspersoftのリポジトリを追加しなければなりません。
最新版(6.4.1)だと itext-2.1.7.js6 がMavenのcentral repositoryに存在するようで、リポジトリの追加は不要。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>xxxx.yyy</groupId> <artifactId>test1</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>test1</name> <url>http://maven.apache.org</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <javac.target>1.7</javac.target> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>3.8.1</version> <scope>test</scope> </dependency> <!-- https://mvnrepository.com/artifact/net.sf.jasperreports/jasperreports --> <dependency> <groupId>net.sf.jasperreports</groupId> <artifactId>jasperreports</artifactId> <version>6.4.1</version> </dependency> </dependencies> </project>
このページを参考に依存関係を確認する。
確認結果は以下の通り。
[INFO] xxxx.xxxx:test1:jar:0.0.1-SNAPSHOT [INFO] +- junit:junit:jar:3.8.1:test [INFO] \- net.sf.jasperreports:jasperreports:jar:6.4.1:compile [INFO] +- commons-beanutils:commons-beanutils:jar:1.9.3:compile [INFO] +- commons-collections:commons-collections:jar:3.2.2:compile [INFO] +- commons-digester:commons-digester:jar:2.1:compile [INFO] +- commons-logging:commons-logging:jar:1.1.1:compile [INFO] +- com.lowagie:itext:jar:2.1.7.js6:compile [INFO] | \- org.bouncycastle:bcprov-jdk15on:jar:1.52:compile [INFO] +- org.jfree:jcommon:jar:1.0.23:compile [INFO] +- org.jfree:jfreechart:jar:1.0.19:compile [INFO] +- org.eclipse.jdt.core.compiler:ecj:jar:4.3.1:compile [INFO] +- org.codehaus.castor:castor-xml:jar:1.3.3:compile [INFO] | +- org.codehaus.castor:castor-core:jar:1.3.3:compile [INFO] | +- commons-lang:commons-lang:jar:2.6:compile [INFO] | +- javax.inject:javax.inject:jar:1:compile [INFO] | +- stax:stax:jar:1.2.0:compile [INFO] | | \- stax:stax-api:jar:1.0.1:compile [INFO] | \- javax.xml.stream:stax-api:jar:1.0-2:compile [INFO] +- com.fasterxml.jackson.core:jackson-core:jar:2.1.4:compile [INFO] +- com.fasterxml.jackson.core:jackson-databind:jar:2.1.4:compile [INFO] +- com.fasterxml.jackson.core:jackson-annotations:jar:2.1.4:compile [INFO] +- org.apache.lucene:lucene-core:jar:4.5.1:compile [INFO] +- org.apache.lucene:lucene-analyzers-common:jar:4.5.1:compile [INFO] +- org.apache.lucene:lucene-queryparser:jar:4.5.1:compile [INFO] | +- org.apache.lucene:lucene-queries:jar:4.5.1:compile [INFO] | \- org.apache.lucene:lucene-sandbox:jar:4.5.1:compile [INFO] | \- jakarta-regexp:jakarta-regexp:jar:1.4:compile [INFO] +- org.olap4j:olap4j:jar:0.9.7.309-JS-3:compile [INFO] +- com.google.zxing:core:jar:3.2.1:compile [INFO] \- com.ibm.icu:icu4j:jar:57.1:compile [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 53.129 s [INFO] Finished at: 2017-08-18T10:41:44+09:00 [INFO] Final Memory: 14M/64M [INFO] ------------------------------------------------------------------------
以上。
SXSSFWorkbookの不具合 #apachePoi
知らなかったので再現プログラムを書いてみた。
import java.io.FileOutputStream; import java.io.IOException; import org.apache.poi.ss.usermodel.Cell; import org.apache.poi.ss.usermodel.CellStyle; import org.apache.poi.ss.usermodel.Font; import org.apache.poi.ss.usermodel.Row; import org.apache.poi.ss.usermodel.Sheet; import org.apache.poi.ss.usermodel.Workbook; import org.apache.poi.xssf.streaming.SXSSFWorkbook; public class WriteTest { public static void main(String[] args) { String outputFilePath = "out.xlsx"; Workbook book = null; FileOutputStream fout = null; try { book = new SXSSFWorkbook(); Font font = book.createFont(); font.setFontName("MS ゴシック"); font.setFontHeightInPoints((short) 9); CellStyle style = book.createCellStyle(); style.setWrapText(true); Sheet sheet = book.createSheet(); Row row = sheet.createRow(0); Cell cell0 = row.createCell(0); cell0.setCellValue("aaa\r\nbbb"); cell0.setCellStyle(style); Cell cell1 = row.createCell(1); cell1.setCellValue("aaa\rbbb"); cell1.setCellStyle(style); Cell cell2 = row.createCell(2); cell2.setCellValue("aaa\nbbb"); cell2.setCellStyle(style); Cell cell3 = row.createCell(3); cell3.setCellValue("aaa\\nbbb"); cell3.setCellStyle(style); fout = new FileOutputStream(outputFilePath); book.write(fout); } catch (IOException e) { e.printStackTrace(); } finally { try { book.close(); } catch (IOException e) { e.printStackTrace(); } } } }
処理結果はこうなり、A1セルの改行が2回行われていることが確認出来る。
原因となるコードはpoi-ooxml-3.xx.jarの org.apache.poi.xssf.streaming.SheetDataWriter.javaの371行目から379行目の部分。 ※3.17-beta1でも直っていないことを確認した。
// Special characters case '\n': case '\r': if (counter > last) { _out.write(chars, last, counter - last); } _out.write("
"); last = counter + 1; break;
#xa;はxml文書で改行コードを表すらしい。
当面の対応は、\r\nを\r、あるいは\nに置換するしかなさそうだけど、本対応はapacheにバグレポートを書くことだろうなぁ。 githubだったらissueに書けばいいんだろうけど、apache poiはsubversionで活動されているので報告先がわからない…。
というか、SXSSFWorkbookってあまり使われていないのかな。