きどたかのブログ

いつか誰かがこのブログからトラブルを解決しますように。

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=と同じだ。(WAS7.0の場合、R24の場合)
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のオプションはほとんど説明が見つからないのでこれ以上の調査はきつい。もう疲れた。