JIT Compiler Testarossaとjavacore
Testarossaについてはあまり資料が見当たらないのだが、
少しだけ気付いてることはあるので書いてみる。
一年か二年前にも実験してたが、今回気が向いたので、理解を深めてみた。
WAS8.0からJ9のバージョンはR26になり、
どうもJITコンパイラであるTestarossaのオプションを-Xjit:verboseで全表示しなくなったっぽい。
そのせいでオプションが効いてるのかも怪しい。
WAS7.0までは-Xjit:verboseで見ることが出来た。
それとも何か違うオプションで見れるのだろうか。
本当にoptions in effectにろくなのが出ない、count=0すら出してくれんのか。
念のため書いておくとR24とR26とWASバージョンの関係はこういうの。
J9R24 - Java6(WAS7)
J9R26 - Java601(WAS8.0 or 8.5),Java7(WAS8.5)
たしかJ9R23がJava5(WAS6.1)だったはず。
こんな感じのオプションがR24にはある。(-Xjit:verboseの結果より)
面倒臭いからAOTのは省略。
JIT type: Testarossa (Full) JIT options specified: verbose options in effect: aotMethodCompilesThreshold=200 aotMethodThreshold=200 bcount=250 catchSamplingSizeThreshold=1100 classLoadPhaseInterval=500 classLoadPhaseThreshold=155 code=8192 (KB) codepad=0 (KB) codetotal=131072 (KB) coldUpgradeSampleThreshold=3 count=1000 counts=- - - - - - 1000 250 1 1000 500 500 - - - 10000 10000 10000 data=8192 (KB) datatotal=0 (KB) disableProfiledNodeVersioning experimentalClassLoadPhaseInterval=40 fieldAccessTracingSocket=27015 greedyInliner=0 hcount=20 hotFieldThreshold=10 inlineCntrAllBucketSize=2147483647 inlineCntrCalleeTooBigBucketSize=2147483647 inlineCntrCalleeTooDeepBucketSize=2147483647 inlineCntrColdAndNotTinyBucketSize=2147483647 inlineCntrDepthExceededBucketSize=2147483647 inlineCntrRanOutOfBudgetBucketSize=2147483647 inlineCntrWarmCalleeHasTooManyNodesBucketSize=2147483647 inlineCntrWarmCalleeTooBigBucketSize=2147483647 inlineCntrWarmCallerHasTooManyNodesBucketSize=2147483647 insertInliningCounters=0 interpreterSamplingDivisor=16 interpreterSamplingThreshold=300 iprofilerJITSamplesBeforeTurningOff=70 iprofilerMemoryConsumptionLimit=18874368 iprofilerSamplesBeforeTurningOff=35000000 milcount=1 minSamplingPeriod=10 mtcount=-1 numInterfaceCallCacheSlots=4 oprofilingDuration=1 oprofilingPeriod=100 oprofilingThreadBufferSize=32768 resetCountThreshold=4000 sampleInterval=30 samplesToAvoidCompilation=0 samplesToAvoidProfiling=0 sampleThreshold=3000 sampleThresholdVariationAllowance=30 samplingFrequency=2 samplingFrequencyInIdleMode=1000 samplingHeartbeatInterval=F10 samplingThreadExpirationTime=-1 scorchingSampleThreshold=240 scount=1 scratchSpaceLimit=131072 (KB) softFailOnAssume stack=1024 target=s390-zos64 tocSize=64 (KB) userClassLoadPhaseThreshold=5 verbose=1 veryHotSampleThreshold=480
information centerから拾ってこれるオプションは極少数。
-Xjit:count=
これはIBM_MIXED_MODE_THRESHOLDと同義らしい
-Xjit:limitFile=(
注)z/OS USS環境だと"("が邪魔をするので、バックスラッシュでエスケープが必要なようだ。
ここで言うfilenameは、verbose,vlog=
-Xjit:optlevel=[ noOpt | cold | warm | hot | veryHot | scorching ]
-Xjit:verbose
-Xcodecache
注)単位はKBです。
これは-Xjit:code=
-Xquickstart クイックスタートについては後述する。
ここまでは分かりやすいところに書いてある。
-Xjit:verbose,vlog=
"-Xjit:verbose={compileStart|compileEnd}"
(Note that this option must be surrounded with quotation marks so that the vertical bar is not interpreted by the shell.)
-Xjit:exclude={
-Xjit:exclude={java/lang/Math.max(II)I}
-Xjit:{java/lang/Math.max(II)I}(optLevel=warm,count=0)
この指定方法はz/OSで出来るか不明。{}もエスケープがいるのかも。
ここに出てないオプションもたくさんあるっぽい。試してないけど。
http://www.apogee.com/resources/jit より引用。
disableArrayCopyOpts
disableDirectMemoryOps
disableDirectToJNI
disableFPCodeGen
disableFPEmulation
disableGRA
disableInlining
disableLocalCSE
disableLocalVP
disableScheduling
disableTreeSimplification
enableCompilationThread
bcLimit
APARから拾ってこれるのは・・・(バージョン精査してないため、古いor新しいJITオプションもあるかも)
-Xjit:compilationThreads=
-Xjit:disableAllocationSinking
-Xjit:disableAndSimplification
-Xjit:disableAsyncCheckRemoval
-Xjit:disableBlockSplitter
-Xjit:disableBlockVersioner
-Xjit:disableCHOpts
-Xjit:disableDynamicLoopTransfer
-Xjit:disableDirectToJNI
-Xjit:disableEscapeAnalysis
-Xjit:disableExplicitNewInitialization
-Xjit:disableGlobalVP
-Xjit:disableGLU
-Xjit:disableGuardedCountingRecompilations
-Xjit:disableHighWordRA
-Xjit:disableHighWordGRA
-Xjit:disableIdiomRecognition
-Xjit:disableInlining
-Xjit:disableInliningOfNatives
-Xjit:disableInternalPointers
-Xjit:disableInnerPreexistence
-Xjit:disableInterpreterProfiling
-Xjit:disableInterpreterProfiling,unloadedClassListMaxLength=3600
-Xjit:disableInvariantArgumentPreexistence
-Xjit:disableLocalDSE
-Xjit:disableLocalReordering
-Xjit:disableLocalVP
-Xjit:disableLookahead
-Xjit:disableLoopReduction
-Xjit:disableLoopStrider
-Xjit:disableLoopUnroller
-Xjit:disableMccFreeBlockRecycling
-Xjit:disableMonitorCoarsening
-Xjit:disableNewInstanceImplOpt
-Xjit:disableNewStringPeepholes
-Xjit:disableProfiling
-Xjit:disableRedundantAsyncCheckRemoval
-Xjit:disableRedundantMonitorElimination
-Xjit:disableRematerialization
-Xjit:disableScheduling
-Xjit:scratchSpaceLimit=
-Xjit:disableSequenceSimplification
-Xjit:disableSequentialStoreSimplification
-Xjit:disableStoreSinking
-Xjit:disableTreeSimplification
-Xjit:disableUnsafe
-Xjit:disableValueProfiling
-Xjit:disableVirtualGuardNOPing
-Xjit:disableVMThreadGRA
-Xjit:disableZArraySetUnroll
-Xjit:disableZGryphon
-Xjit:dontInline={java/lang/Object.
-Xjit:iprofilerSamplesBeforeTurningOff=35000000
-Xjit:noResumableTrapHandler
-Xjit:samplingThreadExpirationTime=100
-Xjit:scratchSpaceLimit=262144
多すぎる、もう無理。
よく使う(?)オプションだと、-Xjit:count=0というのがある。
これをやると初回のメソッド呼び出しでJITコードになる。
暖気運転してから性能測定するのが嫌な人が使う。
しかし、厳密にはそれはよくないこともある。
JITされたコードの保存領域には限りがあるので、順番的にどのメソッドがJIT化されるかって点がある。
メモリが潤沢で、かつちゃんとJITのチューニングをするなら、これでもいいだろう。
まあ、初めの方に呼ばれるのってフレームワーク的な何かだから、
不都合というか実害はあまり無いだろう。
いちおうこれで一気にwarmレベルまで行く。
optLevelも付ければ、一気により最適化することも出来るが、warmくらいでいいのよ。
optLevelとcountを同時に指定すると、どうもそのレベル以外のoptLevel用の閾値が無くなるようだから、
その辺りは間違えないようにしないといけない。
たぶんメソッド指定の場合、optLevelとcountを混ぜてもOKなんじゃないだろうか。
-Xjit:count=0をすると、どこが変わるのかを列挙しようか。
bcount=0 count=0 counts=- - - - - - 0 0 0 1000 500 500 - - - 10000 10000 10000 milcount=0 scount=0
これら5箇所が変わる。
countsについて見やすいように並べて比較してみよう
counts=- - - - - - 1000 250 1 1000 500 500 - - - 10000 10000 10000 counts=- - - - - - 0 0 0 1000 500 500 - - - 10000 10000 10000
これの読み方について色々実験をした結果、以下のことのようだ。
左から順に3つ刻みで読む
noOpt のcount,bcount,milcount (- - -) cold のcount,bcount,milcount (- - -) warm のcount,bcount,milcount (1000 250 1) hot のcount,bcount,milcount (1000 500 500) veryHot のcount,bcount,milcount (- - -) scorchingのcount,bcount,milcount (10000 10000 10000)
countとbcountの違いはloopがあるか否かのようだ。
loopのあるメソッドはbcountに引っかかるっぽい。(まだ実験してないけど)
milcountの意味はまだ不明。
scountは、shared data cacheから取ってくるとかの閾値っぽい。
これはいくつかの実験の結果です。
counts= - - - - - - 1000 250 1 1000 500 500 - - - 10000 10000 10000 (default) counts= - - - - - - 20 20 1 1000 500 500 - - - 10000 10000 10000 (count=20) counts= - - - - - - 0 0 0 1000 500 500 - - - 10000 10000 10000 (count=0) counts= - - - - - - 1000 2000 0 1000 500 500 - - - 10000 10000 10000 (bcount=2000) counts= - - - 2000 2000 2000 - - - 1000 500 500 - - - 10000 10000 10000 (-Xquickstart) counts=1000 250 1 (optLevel=noOpt) counts= - - - 1000 250 1 (optLevel=cold) counts= - - - - - - - - - - - - - - - 1000 250 1 (optLevel=scorching) warm〜veryHotは省略。
とりあえず、性能測定で暖気運転するならhot目指して2千回はぶん回せってこと。
まあscorching目指して12000回でもいいけど、JIT Code Cacheの事も考えないといけない。
他にも四半期に1回しか動かないプログラムが、単性能では良かったのに、
実際に動いてみるとインタープリタでしか動けないだとかは考えられる。
しかし、そんなプログラムでも大半は共通部品であろうから、そこまで問題のある性能になるというのは考えにくい。
心配ならダンプ取ってjdmpviewでinfo jitmして突き合わせてみるとか、
-Xshareclasses:printAllStatsとかで見るなども考えられる。
モジュールの結合度をゆるめるために重複コードを増やすとJIT Code Cacheに影響を少し与えるというのも考えられるが、
1つが2つになった程度の話なら全然問題ないから心配ない。
あと実験していて気付いたことがある。
必ずしも閾値を越える回数呼ばれていてもJITコードにならないメソッドもある。
インライン化されて呼ばれなくなるような感じだろう。
ここからは少し-Xquickstartについて書いてみる。
JIT type: Testarossa (Full) JIT options specified: verbose <= -Xquickstartは-Xjitで渡さないのでverboseではオプションとして確認できない options in effect: aotMethodCompilesThreshold=200 aotMethodThreshold=1000000000 bcount=2000 catchSamplingSizeThreshold=1100 classLoadPhaseInterval=500 classLoadPhaseThreshold=155 code=8192 (KB) codepad=0 (KB) codetotal=131072 (KB) coldUpgradeSampleThreshold=3 count=2000 counts=- - - 2000 2000 2000 - - - 1000 500 500 - - - 10000 10000 10000 data=8192 (KB) datatotal=0 (KB) disableProfiledNodeVersioning experimentalClassLoadPhaseInterval=40 fieldAccessTracingSocket=27015 forceAOT greedyInliner=0 hcount=20 hotFieldThreshold=10 inlineCntrAllBucketSize=2147483647 inlineCntrCalleeTooBigBucketSize=2147483647 inlineCntrCalleeTooDeepBucketSize=2147483647 inlineCntrColdAndNotTinyBucketSize=2147483647 inlineCntrDepthExceededBucketSize=2147483647 inlineCntrRanOutOfBudgetBucketSize=2147483647 inlineCntrWarmCalleeHasTooManyNodesBucketSize=2147483647 inlineCntrWarmCalleeTooBigBucketSize=2147483647 inlineCntrWarmCallerHasTooManyNodesBucketSize=2147483647 insertInliningCounters=0 interpreterSamplingDivisor=16 interpreterSamplingThreshold=2000 iprofilerJITSamplesBeforeTurningOff=70 iprofilerMemoryConsumptionLimit=18874368 iprofilerSamplesBeforeTurningOff=35000000 milcount=2000 minSamplingPeriod=10 mtcount=-1 numInterfaceCallCacheSlots=4 oprofilingDuration=1 oprofilingPeriod=100 oprofilingThreadBufferSize=32768 resetCountThreshold=4000 sampleInterval=30 samplesToAvoidCompilation=0 samplesToAvoidProfiling=0 sampleThreshold=3000 sampleThresholdVariationAllowance=30 samplingFrequency=2 samplingFrequencyInIdleMode=1000 samplingHeartbeatInterval=F10 samplingThreadExpirationTime=-1 scorchingSampleThreshold=240 scount=1 scratchSpaceLimit=131072 (KB) softFailOnAssume stack=1024 target=s390-zos64 tocSize=64 (KB) userClassLoadPhaseThreshold=5 verbose=1 veryHotSampleThreshold=480
通常と違う箇所はこれら。
aotMethodThreshold=1000000000 bcount=2000 count=2000 counts=- - - 2000 2000 2000 - - - 1000 500 500 - - - 10000 10000 10000 forceAOT interpreterSamplingThreshold=2000 milcount=2000
通常と違い、warmではなくcoldに重点が置かれて、閾値も増えている。
JITコストが高いから遅らせてるのかな。
それからcoldまで最適化してからのAOT?だと思われる。
たぶんsharedclassesにAOTコードがストアされてる状況であればもっと速いんだろう。
最近知ったJIT関連のオプション-Xcodecache
これは上述していたとおり、-Xjit:code=
javacoreのMEMINFOセクションに出てくるJIT Code Cacheの1セグメントのサイズに対応している模様。
(R24での例)
1STSEGTYPE JIT Code Cache NULL segment start alloc end type bytes 1STSEGMENT 00000000012DA668 000007FFFF7A0000 000007FFFF81C4E0 000007FFFFFA0000 00000068 800000
こんな感じ、この場合8MBが1セグメントある。
codetotalは、全セグメントを合わせた上限値であるはずです。
この-Xcodecache
z/OSでのR24だと素直に受け取ってくれてるんだけど、WIN7でのR26だと意図した値になってない。
1セグメントが必ずこの指定のサイズということもなく、稀にこれより大きいサイズが取られる。
R24の場合、NATIVEMEMINFOセクションは無い。
R26から出来た。これは非常にありがたい改善だったと思う。
これってz/OSでも出るのかな・・・、今度試しておこう。
(追記:zWAS V8.0でNATIVEMEMINFOは出てました)
0SECTION NATIVEMEMINFO subcomponent dump routine NULL ================================= 0MEMUSER 1MEMUSER JRE: 2,064,546,896 bytes / 9112 allocations 1MEMUSER | 2MEMUSER +--VM: 1,956,945,296 bytes / 7183 allocations 2MEMUSER | | 3MEMUSER | +--Classes: 118,768,624 bytes / 3783 allocations 3MEMUSER | | | 4MEMUSER | | +--Shared Class Cache: 62,914,560 bytes / 1 allocation 3MEMUSER | | | 4MEMUSER | | +--Other: 55,854,064 bytes / 3782 allocations 2MEMUSER | | 3MEMUSER | +--Memory Manager (GC): 1,648,648,800 bytes / 1027 allocations 3MEMUSER | | | 4MEMUSER | | +--Java Heap: 1,610,612,736 bytes / 1 allocation 3MEMUSER | | | 4MEMUSER | | +--Other: 38,036,064 bytes / 1026 allocations 2MEMUSER | | 3MEMUSER | +--Threads: 16,657,984 bytes / 334 allocations 3MEMUSER | | | 4MEMUSER | | +--Java Stack: 960,272 bytes / 55 allocations 3MEMUSER | | | 4MEMUSER | | +--Native Stack: 14,843,904 bytes / 55 allocations 3MEMUSER | | | 4MEMUSER | | +--Other: 853,808 bytes / 224 allocations 2MEMUSER | | 3MEMUSER | +--Trace: 595,616 bytes / 377 allocations 2MEMUSER | | 3MEMUSER | +--JVMTI: 17,784 bytes / 13 allocations 2MEMUSER | | 3MEMUSER | +--JNI: 405,424 bytes / 1130 allocations 2MEMUSER | | 3MEMUSER | +--Port Library: 170,475,368 bytes / 74 allocations 3MEMUSER | | | 4MEMUSER | | +--Unused <32bit allocation regions: 170,463,664 bytes / 1 allocation 3MEMUSER | | | 4MEMUSER | | +--Other: 11,704 bytes / 73 allocations 2MEMUSER | | 3MEMUSER | +--Other: 1,375,696 bytes / 445 allocations 1MEMUSER | 2MEMUSER +--JIT: 107,456,808 bytes / 1822 allocations 2MEMUSER | | 3MEMUSER | +--JIT Code Cache: 67,108,864 bytes / 4 allocations 2MEMUSER | | 3MEMUSER | +--JIT Data Cache: 6,291,648 bytes / 3 allocations 2MEMUSER | | 3MEMUSER | +--Other: 34,056,296 bytes / 1815 allocations 1MEMUSER | 2MEMUSER +--Class Libraries: 144,792 bytes / 107 allocations 2MEMUSER | | 3MEMUSER | +--Harmony Class Libraries: 2,000 bytes / 1 allocation 2MEMUSER | | 3MEMUSER | +--VM Class Libraries: 142,792 bytes / 106 allocations 3MEMUSER | | | 4MEMUSER | | +--sun.misc.Unsafe: 8,488 bytes / 2 allocations 3MEMUSER | | | 4MEMUSER | | +--Other: 134,304 bytes / 104 allocations
MEMINFOのセクションも多少フォーマットがR26で変わっている。
genconの時の領域のセグメント分けは前からされていたけど、
どれがTenuredかすぐ分かるようになってたり、
各セグメントの合計サイズが出てくれたりしている。
MEMINFOに出てくるセグメントタイプは5つある。
1STSEGTYPE Internal Memory 1STSEGTYPE Object Memory 1STSEGTYPE Class Memory 1STSEGTYPE JIT Code Cache 1STSEGTYPE JIT Data Cache
Object MemoryがJavaヒープの部分。
Object MemoryはNurseryやTenuredの領域なんで。(genconにしてます)
上述のWIN7でのNATIVEMEMINFOでは1.5GBのヒープを取ってるんだが、
実際にはヒープ領域はまだ拡張してなくて、
MEMINFOのObject Memoryでは少ない値になってたりする。
だから、NATIVEMEMINFOとMEMINFOを計算・比較しても辻褄は合わないので注意されたし。
(R26からの抜粋)
1STHEAPTYPE Object Memory NULL id start end size space/region 1STHEAPSPACE 0x00000000002B70C0 -- -- -- Generational 1STHEAPREGION 0x00000000001D3BE0 0x0000000002890000 0x000000001A890000 0x0000000018000000 Generational/Tenured Region 1STHEAPREGION 0x00000000001D3A00 0x0000000056690000 0x000000005ED00000 0x0000000008670000 Generational/Nursery Region 1STHEAPREGION 0x00000000001D3AF0 0x000000005ED00000 0x0000000062890000 0x0000000003B90000 Generational/Nursery Region NULL 1STHEAPTOTAL Total memory: 606076928 (0x0000000024200000) 1STHEAPINUSE Total memory in use: 217344496 (0x000000000CF469F0) 1STHEAPFREE Total memory free: 388732432 (0x00000000172B9610)
ちょっと面白いと思ったのはNATIVEMEMINFOでJavaヒープの部分は1アロケーションになってる点。
やはり-Xmxの値で1アロケーションってことだろう。すっかり忘れてた。
Nurseryは普通に繋がってるね、まあ前からだけど。
Class Memoryは名前の通りだろう。
Internal Memoeryはその他もろもろ。
JIT Data Cacheは良く分からないが、
最適化するためのプロファイルの作業領域なんじゃないだろうか。
R26でもcodeTotalの指定は効いているように思うが、ぴったりになるとは限らない。
最後のセグメントで多少上ブレする感じだろう。
JIT Code Cacheについては、NATIVEMEMINFOとMEMINFOの値は一致していた。
JITのオプションはほとんど説明が見つからないのでこれ以上の調査はきつい。もう疲れた。