エラトステネスの篩を使って素数を求める以下のコードを利用する。
public class SampleAlgorithm {
private static final int LIMIT = 1000000;
public static void main(String... args) {
int primeCount = 2;
boolean isPrime = false;
for (int i = 5; i <= LIMIT; i++) {
for (int j = 2; j * j <= i; j++) {
if (i % j == 0) {
isPrime = false;
break;
}
isPrime = true;
}
if (isPrime) {
primeCount += 1;
}
}
System.out.println(primeCount);
}
}
このコードを -Xint オプションをつけた場合、つけなかった場合で処理時間を比較する。
❯ time java -Xint SampleAlgorithm.java
78498
java -Xint SampleAlgorithm.java 1.80s user 0.05s system 98% cpu 1.876 total
❯ time java SampleAlgorithm.java
78498
java SampleAlgorithm.java 1.14s user 0.07s system 160% cpu 0.753 total
1000000 までの素数であれば微々たる差だが、桁を増やすと差が明確になっていく。
1億以下の素数を計算すると以下のようになる。
❯ time java -Xint SampleAlgorithm.java
664579
java -Xint SampleAlgorithm.java 21.35s user 0.13s system 99% cpu 21.590 total
❯ time java SampleAlgorithm.java
664579
java SampleAlgorithm.java 6.18s user 0.08s system 108% cpu 5.769 total
実行時のオプションに -XX:+PrintCompilation をつけることで JIT コンパイルが何を行っているかわかる。
このフォーマットには列ごとに意味があるが、それは次の JIT コンパイラのチューニング part1 でまとめる。
❯ java -XX:+PrintCompilation SampleAlgorithm.java
.....
530 958 3 com.sun.tools.javac.tree.JCTree::hasTag (14 bytes)
530 957 1 com.sun.tools.javac.code.Type$JCPrimitiveType::isPrimitive (2 bytes)
530 959 3 com.sun.tools.javac.code.Type$ClassType::accept (9 bytes)
532 960 3 java.lang.invoke.DirectMethodHandle::make (263 bytes)
533 961 3 java.lang.invoke.MemberName::isField (7 bytes)
533 962 3 java.util.ArrayList::isEmpty (13 bytes)
534 963 3 com.sun.tools.javac.jvm.Code::typecode (141 bytes)
534 964 3 com.sun.tools.javac.jvm.Code::width (42 bytes)
534 965 3 com.sun.tools.javac.jvm.Code::width (16 bytes)
535 966 3 com.sun.tools.javac.code.Type::typeNoMetadata (19 bytes)
536 734 4 java.lang.StringLatin1::indexOf (121 bytes) made not entrant
537 309 3 jdk.internal.jimage.ImageStringsReader::stringFromByteBuffer (28 bytes) made not entrant
537 828 4 java.lang.ref.Reference::reachabilityFence (1 bytes)
537 169 3 java.lang.ref.Reference::reachabilityFence (1 bytes) made not entrant
537 814 4 com.sun.tools.javac.util.ListBuffer::toList (10 bytes)
537 635 3 com.sun.tools.javac.util.ListBuffer::toList (10 bytes) made not entrant
537 755 4 java.lang.String::toString (2 bytes)
537 480 3 java.lang.String::toString (2 bytes) made not entrant
537 967 % 3 SampleAlgorithm::main @ 15 (65 bytes)
538 968 3 SampleAlgorithm::main (65 bytes)
538 969 % 4 SampleAlgorithm::main @ 15 (65 bytes)
539 967 % 3 SampleAlgorithm::main @ 15 (65 bytes) made not entrant
745 969 % 4 SampleAlgorithm::main @ 15 (65 bytes) made not entrant
- XX:+LogCompilation をつけるとより詳細な以下のようなログを確認することができる。
<<<一部抜粋>>>
<nmethod compile_id='1' compiler='c1' level='3' entry='0x000000011d1911c0' size='1056' address='0x000000011d191010' relocation_offset='376' insts_offset='432' stub_offset='816' scopes_data_offset='872' scopes_pcs_offset='936' dependencies_offset='1032' nul_chk_table_offset='1040' metadata_offset='864' method='java.lang.StringLatin1 hashCode ([B)I' bytes='42' count='101' backedge_count='2096' iicount='101' stamp='0.054'/>
<writer thread='9731'/>
<task_queued compile_id='2' method='java.util.concurrent.ConcurrentHashMap tabAt ([Ljava/util/concurrent/ConcurrentHashMap$Node;I)Ljava/util/concurrent/ConcurrentHashMap$Node;' bytes='22' count='256' iicount='256' level='3' stamp='0.054' comment='tiered' hot_count='256'/>
<task_queued compile_id='3' method='jdk.internal.misc.Unsafe getObjectAcquire (Ljava/lang/Object;J)Ljava/lang/Object;' bytes='7' count='256' iicount='256' level='3' stamp='0.054' comment='tiered' hot_count='256'/>
<task_queued compile_id='4' method='java.lang.String coder ()B' bytes='15' count='256' iicount='256' level='3' stamp='0.054' comment='tiered' hot_count='256'/>
<writer thread='42755'/>
<nmethod compile_id='2' compiler='c1' level='3' entry='0x000000011d191660' size='1376' address='0x000000011d191490' relocation_offset='376' insts_offset='464' stub_offset='1168' scopes_data_offset='1240' scopes_pcs_offset='1288' dependencies_offset='1368' oops_offset='1216' metadata_offset='1224' method='java.util.concurrent.ConcurrentHashMap tabAt ([Ljava/util/concurrent/ConcurrentHashMap$Node;I)Ljava/util/concurrent/ConcurrentHashMap$Node;' bytes='22' count='260' iicount='260' stamp='0.055'/>
<writer thread='9731'/>
<task_queued compile_id='5' method='java.lang.String isLatin1 ()Z' bytes='19' count='256' iicount='256' level='3' stamp='0.055' comment='tiered' hot_count='256'/>
<writer thread='42755'/>
<nmethod compile_id='3' compiler='c1' level='3' entry='0x000000011d191bc0' size='856' address='0x000000011d191a10' relocation_offset='376' insts_offset='432' stub_offset='720' scopes_data_offset='776' scopes_pcs_offset='800' dependencies_offset='848' metadata_offset='768' method='jdk.internal.misc.Unsafe getObjectAcquire (Ljava/lang/Object;J)Ljava/lang/Object;' bytes='7' count='263' iicount='263' stamp='0.055'/>
<nmethod compile_id='5' compiler='c1' level='3' entry='0x000000011d191f40' size='880' address='0x000000011d191d90' relocation_offset='376' insts_offset='432' stub_offset='752' scopes_data_offset='808' scopes_pcs_offset='824' dependencies_offset='872' metadata_offset='800' method='java.lang.String isLatin1 ()Z' bytes='19' count='259' iicount='259' stamp='0.055'/>
<nmethod compile_id='4' compiler='c1' level='3' entry='0x000000011d1922c0' size='816' address='0x000000011d192110' relocation_offset='376' insts_offset='432' stub_offset='688' scopes_data_offset='744' scopes_pcs_offset='760' dependencies_offset='808' metadata_offset='736' method='java.lang.String coder ()B' bytes='15' count='262' iicount='262' stamp='0.055'/>
<writer thread='9731'/>
<task_queued compile_id='6' method='java.lang.Object <init> ()V' bytes='1' count='512' iicount='512' level='3' stamp='0.057' comment='tiered' hot_count='512'/>
<writer thread='42755'/>
この時に重要になるのが level の値。これは階層型コンパイルが行われ、その際にどのコンパイルが適用されたかを示している。
レベルは以下の通り。