下面统一按“C2 性能 = 100%,数值越高越好”计算。之前比例由 dev_4 的耗时比取倒数,当前比例采用你在另一环境的实测结果。
总体对比
| Renaissance 子项 |
dev_4 之前性能 |
当前性能 |
相对旧 C3 提升 |
当前 C3/C2 耗时 |
当前状态 |
| neo4j-analytics |
16.8% |
32% |
1.90x |
3.13x |
仍严重劣化 |
| page-rank |
31.6% |
46% |
1.46x |
2.17x |
仍明显劣化 |
| par-mnemonics |
31.3% |
97% |
3.10x |
1.03x |
基本追平 |
| philosophers |
36.1% |
116% |
3.21x |
0.86x |
C3 反超约 16% |
| reactors |
60.2% |
90% |
1.50x |
1.11x |
接近追平 |
“当前 C3/C2 耗时”按 100 / 性能比例 换算;例如 32% 表示 C3 耗时约为 C2 的 3.13 倍。
功能变化与残余缺口
| 子项 |
当前分支新增、且与该项相关的能力 |
旧分析中对应的作用 |
当前仍待补齐 |
更新后的性能结论 |
| neo4j-analytics |
Unsafe _getByte、_getShort、_getLong、_getIntVolatile、_getLongVolatile、_putLongVolatile;Class.isInstance;System.arraycopy |
已覆盖旧版 8 个主要 Unsafe 缺口中的 6 个。按旧 profile 计算,大约消掉 21.78 个百分点的可辨认 Unsafe cycles;同时消除部分 JNI/native transition |
_getInt、_putByte、_allocateInstance;完整 EA/标量替换;一般代码质量 |
从 16.8% 提升到 32%,说明补齐的 Unsafe/type/arraycopy 确实有效。但 _getInt 在旧 profile 中单独占约 10.19% cycles,仍是最明确主因 |
| page-rank |
Unsafe _getLong、_getShort、_putLong、_getByte、_putInt、_getReference、_getReferenceVolatile;hashCode/identityHashCode;isInstance/isArray/isPrimitive/isAssignableFrom;getLength;arraycopy |
旧版 12 个普通 Unsafe 缺口中补了 7 个;hash/type/array 次级组除 allocation 外基本补齐。按旧 profile 约消掉 17.96 个百分点的已知 fallback cycles |
_getInt、_putShort、_putByte、_getDouble、_putDouble、_allocateInstance;EA;Spark/TimSort/Kryo 热点代码膨胀和内联边界 |
从 31.6% 提升到 46%,符合“修了一半显式 fallback、仍剩一半”的静态判断。当前主要问题仍是 Kryo/LZ4 普通 Unsafe 访问 |
| par-mnemonics |
arraycopy;Class.isPrimitive;vectorizedHashCode;StringBuilder 构造器 intrinsic;StringBuilder/StringBuffer concat fusion;部分字符串编码 intrinsic |
旧 C2 A/B 中,单独禁用 arraycopy 可解释总差距约 29.7%;vectorizedHashCode/isPrimitive 是次要项;concat fusion 有助于短字符串构造 |
_allocateInstance;完整 EA;部分 stream/lambda 对象消除;一般代码尺寸和内联反馈 |
从 31.3% 提升到 97%,基本证明 arraycopy 是旧回归的核心开关之一。allocateInstance 虽未实现,但已经不足以造成明显总分回归 |
| philosophers |
Class.isInstance;compareAndSetReference;getReferenceVolatile;其他 reference compare-and-exchange/weak-CAS;更多 VarHandle/Unsafe 内存序 lowering |
正好覆盖旧版两个最大因果组:isInstance 单项约对应旧总差距 30.6%;reference CAS + volatile load 单项约对应 22.6%。二者在 STM 中会被冲突/重试进一步放大 |
_allocateInstance;完整 EA;profile devirtualization 的收益模型;并发 STM 下的代码形态和多模态稳定性;primitive getAndAdd direct intrinsic |
从 36.1% 提升到 116%,旧版 intrinsic 根因已经基本消失。新结果还表明 intrinsic 补齐改善了事务时序,收益远大于 flat profile 占比 |
| reactors |
compareAndSetReference;compareAndExchangeLong;putInt/getIntVolatile/putReferenceVolatile;isInstance/isArray;arraycopy;weak reference CAS |
旧版这些 fallback 的可见上限只有约 8%–10% cycles,但它们位于 ForkJoin、队列和消息发送链,修复后可能通过内联及调度反馈产生放大收益 |
普通 _getInt;_allocateInstance;恢复 direct primitive getAndAdd{Byte,Short,Int,Long};跨 reactor 调度链代码膨胀;EA/对象消除 |
从 60.2% 提升到 90%,改善比旧 A/B 预期更大,说明并发调度反馈明显。剩余约 10% 更可能来自 getAndAdd 退化和整体代码形态 |
当前仍缺失的 intrinsic 汇总
| 待补功能 |
影响子项 |
旧证据/预期影响 |
优先级 |
_getInt |
neo4j、page-rank、reactors |
Neo4j 旧 profile 中 Java wrapper + VM helper 合计约 10.19%;PageRank 约 8.52% |
P0 |
_putByte |
neo4j、page-rank |
Neo4j 约 2.20%;PageRank 约 1.79% |
P0 |
_putShort |
page-rank |
PageRank 约 2.20% |
P0 |
_getDouble/_putDouble |
page-rank |
合计约 0.73%,自身不大,但会影响 Kryo 内联和 transition |
P1 |
_allocateInstance |
五项均可能命中,尤其 page-rank/par-mnemonics |
旧实验:PageRank 约解释总差距 10.2%;par-mnemonics 约 21.6%;其他项较低 |
P0/P1 |
primitive _getAndAdd{Byte,Short,Int,Long} direct intrinsic |
reactors 为主,其他并发 workload |
dev_4 有、当前分支反而没有;目前会执行 Java volatile-load + weak-CAS 循环 |
P1 |
| Java heap EA / scalar replacement |
page-rank、par-mnemonics、reactors |
当前 DoEscapeAnalysis 主要只是内联启发式,没有 C2 ConnectionGraph 对等实现 |
P1 |
| profile devirtualization 收益模型 |
philosophers、Scala/actor workload |
transformation 本身与旧分支相同,但 intrinsic 补齐后总体结果已改变;需要重新 A/B |
P2 |
| 通用代码尺寸、内联边界 |
page-rank、reactors |
旧版热点方法存在 1.4–3.3x 代码膨胀;reactors 的消息链仍是主要残余候选 |
P2 |
当前分支其他可能产生收益的公共变化
这些功能已经进入当前代码,但无法仅凭现有跨环境数据准确拆分贡献:
综合看,接下来最划算的实现顺序是:
_getInt、_putByte、_putShort。
_allocateInstance。
_getDouble/_putDouble。
- 恢复 primitive direct
getAndAdd,重点验证 reactors。
- 再处理 EA、代码尺寸和内联质量。
修完前两批后,Neo4j 和 PageRank 应当是最值得立即重测的两个验收项。
初测完成:这 5 项 C3 全部显著慢于 C2,几何平均耗时达到 C2 的 3.08 倍。
| Benchmark |
C2 末5轮均值 (ms) |
C3 末5轮均值 (ms) |
C3/C2 (倍) |
C3 回退幅度 |
| neo4j-analytics |
1,566.8 |
9,322.6 |
5.95 |
+495.0% |
| page-rank |
3,242.0 |
10,255.9 |
3.16 |
+216.3% |
| par-mnemonics |
3,961.1 |
12,673.0 |
3.20 |
+219.9% |
| philosophers |
6,244.4 |
17,282.2 |
2.77 |
+176.8% |
| reactors |
20,388.4 |
33,879.6 |
1.66 |
+66.2% |
测试口径:
- BiSheng JDK 21.0.9 release build
- C2:
-XX:-TieredCompilation -XX:-UseJeandleCompiler - C3:
-XX:-TieredCompilation -XX:+UseJeandleCompiler - NUMA node 2
- 每项 10 轮,统计末 5 轮
- 共 100 个 iteration,全部成功,无异常标记
- 末 5 轮 CV 为 0.07%–6.63%,差距远大于运行波动
初步建议:
- 先分析
par-mnemonics:3.2× 回退且方差最低,最适合做 C2/C3 perf、热点汇编和编译日志对照。 - 随后分析
neo4j-analytics:回退最严重,达到 5.95×。 - 再用
page-rank 判断问题是否也集中在 Spark closure、集合和装箱路径。 philosophers、reactors 涉及并发和调度,适合在较窄案例确认问题后分析。
详细评估见 [assessment.md](/home/luofeng/jdk21-c3-jit/results/renaissance-c2-c3-selected-20260731_1036/assessment.md),结构化数据见 [comparison-last-5.tsv](/home/luofeng/jdk21-c3-jit/results/renaissance-c2-c3-selected-20260731_1036/comparison-last-5.tsv)。原始日志:[C2](/home/luofeng/jdk21-c3-jit/results/renaissance-c2-c3-selected-20260731_1036/c2/process.log)、[C3](/home/luofeng/jdk21-c3-jit/results/renaissance-c2-c3-selected-20260731_1036/c3/process.log)。
结论:neo4j-analytics 的主要劣化原因是 C3 缺失 Neo4j 高频依赖的 HotSpot intrinsic,尤其是 Unsafe 普通及 volatile 内存访问。结论置信度较高,源码、CPU profile 和因果 A/B 实验三者相互吻合。
| 实验 |
稳态时间 |
| C2 |
1.618 s |
| C3 |
9.213 s |
| C2 禁用 Unsafe intrinsic |
5.996 s |
| C2 禁用全部已识别缺失 intrinsic |
6.976 s |
在 C2 中主动禁用 C3 缺失的 intrinsic 后,复现了约 70.5% 的 C3/C2 总差距。
C3 当前缺失的关键功能:
Unsafe 普通/volatile load/store:
_getInt、_getByte、_getLong、_getShort、_getIntVolatile、_getLongVolatile、_putByte、_putLongVolatile_isInstance_arraycopy_allocateInstance
C3 的 Unsafe 白名单目前基本只有 getAndAdd 和 compareAndSet 原子族,[C3 支持表 (line 163)](/home/luofeng/jdk21-c3-jit/src/hotspot/share/jeandle/jeandleIntrinsicLowering.cpp:163);而 C2 明确支持完整普通/volatile/acquire/opaque/unaligned 访问族,以及 [`arraycopy` (line 578)](/home/luofeng/jdk21-c3-jit/src/hotspot/share/opto/c2compiler.cpp:578)、[`allocateInstance/isInstance` (line 687)](/home/luofeng/jdk21-c3-jit/src/hotspot/share/opto/c2compiler.cpp:687)。
CPU profile 也直接反映了 fallback:
ThreadStateTransition::transition_from_native:16.44%Unsafe.getInt + Unsafe_GetInt:10.19%Unsafe.getByte + Unsafe_GetByte:8.03%Unsafe.getLongVolatile + Unsafe_GetLongVolatile:6.10%- 可识别的 Unsafe wrapper/runtime 合计约 34%
因此 C3 原本应该生成直接 load/store 的位置,实际频繁进入 Unsafe_Get*/Unsafe_Put* VM native 入口,并承担 Java/native 状态切换成本。[C3 原始热点 (line 1)](/home/luofeng/jdk21-c3-jit/results/neo4j-c2-c3-analysis-20260731/c3-profile-v2/perf-report-fields.txt:1)
硬件计数器进一步排除了“缓存劣化”为主因:
- 指令数:C3 是 C2 的 2.71 倍
- IPC:2.52 → 1.59,下降 36.9%
- cycles:4.30 倍
- cache-miss rate:0.944% → 0.802%,反而降低
所以本质是执行了更多 fallback 指令,并且 native 边界降低了流水线效率。
修复优先级建议:
- 先补齐 Unsafe 普通/volatile load/store lowering。
- 再补
_isInstance、_arraycopy、_allocateInstance。 - 修复后检查
Unsafe_Get*、Unsafe_Put* 和 native transition 是否从热点头部消失。 - 剩余约 29.5% 差距再继续做内联、去虚化和代码尺寸对比;目前证据不足以把这部分归因给某个具体优化。
完整分析、数据和实验说明在[分析报告 (line 8)](/home/luofeng/jdk21-c3-jit/results/neo4j-c2-c3-analysis-20260731/analysis-report.md:8)。初始结果为 C2 1.567 秒、C3 9.323 秒,即 C3 慢 5.95 倍,见[五项对比 (line 19)](/home/luofeng/jdk21-c3-jit/results/renaissance-c2-c3-selected-20260731_1036/assessment.md:19)。
par-mnemonics 的主因已定位:C3 缠失 _arraycopy 和 _allocateInstance intrinsic,可以因果解释大约一半劣化。
| 实验 |
稳态时间 |
| C2 |
3.315 s |
| C3 |
11.043 s |
| C2 禁用 _arraycopy |
5.607 s |
| C2 禁用 _allocateInstance |
4.984 s |
| C2 禁用全部已观察缺失 intrinsic |
7.278 s |
组合实验解释 C3/C2 总差距约 51.3%。
核心证据:
- C3 profile 中
System.arraycopy + JVM_ArrayCopy 占 14.39%,包括数组复制内部路径约 15.55%。 Unsafe.allocateInstance + Unsafe_AllocateInstance + ObjAllocator 占约 9.77%。- native 状态切换另外占 8.77%。
- C2 profile 中这些 VM fallback 均未进入热点头部。[C3 热点原始数据 (line 12)](/home/luofeng/jdk21-c3-jit/results/par-mnemonics-c2-c3-analysis-20260731/c3-profile/perf-report.txt:12)
源码也明确证实:
- [C3 intrinsic 白名单 (line 163)](/home/luofeng/jdk21-c3-jit/src/hotspot/share/jeandle/jeandleIntrinsicLowering.cpp:163)没有
_arraycopy、_allocateInstance、_vectorizedHashCode、_isPrimitive。 - C2 支持 [`_vectorizedHashCode` (line 216)](/home/luofeng/jdk21-c3-jit/src/hotspot/share/opto/c2compiler.cpp:216)、[`_arraycopy` (line 578)](/home/luofeng/jdk21-c3-jit/src/hotspot/share/opto/c2compiler.cpp:578)、[`_allocateInstance` (line 687)](/home/luofeng/jdk21-c3-jit/src/hotspot/share/opto/c2compiler.cpp:687)。
剩余约一半表现为代码质量和内联反馈问题:
- C3 指令数是 C2 的 2.40 倍。
- IPC 从 2.69 降到 2.02。
- cache-miss rate 从 0.481% 升到 0.862%。
HashMap.computeIfAbsent、groupingBy$53、wrapAndCopyInto 等热方法的 C3 机器码约为 C2 的 1.7 倍。- C2 会把
ArrayList.add → add → grow → grow 整条链内联,[C2 inline 证据 (line 1565)](/home/luofeng/jdk21-c3-jit/results/par-mnemonics-c2-c3-analysis-20260731/c2-inlining/process.log:1565);C3 因 already compiled into a big method 提前停止,[C3 inline 证据 (line 1520)](/home/luofeng/jdk21-c3-jit/results/par-mnemonics-c2-c3-analysis-20260731/c3-inlining/process.log:1520)。
其他缺口影响较小:
- 缺少 Java heap escape analysis:C2 关闭后劣化约 8.9%。
- 缺少 C2 dedicated StringConcat 优化:约 4.4%。
_vectorizedHashCode:单独约 2.4%。_isPrimitive:基本无影响。
建议修复顺序:
_arraycopy_allocateInstance- 修复后重新评估代码尺寸和内联反馈
_vectorizedHashCode- escape analysis 和一般代码生成质量
完整数据和实验记录在[分析报告 (line 8)](/home/luofeng/jdk21-c3-jit/results/par-mnemonics-c2-c3-analysis-20260731/analysis-report.md:8)。
page-rank 分析完成。核心结论:C3 是 C2 的 3.163 倍,第一主因是 C3 缺少普通 Unsafe.get/put intrinsic。
关键证据:
- C2 禁用 12 项普通 Unsafe intrinsic 后,可解释 32.7% 的总性能差距。
- 加上
allocateInstance、identity hash、类型检查和 arraycopy 等缺失项,可解释约 56.9%。
- C3 profile 中普通 Unsafe fallback 占 24.05%,native transition 占 14.45%;C2 基本没有这些开销。
- C3 动态指令数为 C2 的 2.846 倍,IPC 下降 15%;cache miss rate 反而更低,排除 cache miss 是主因。
- 剩余约 43% 与内联、代码膨胀和逃逸分析差异有关,目前尚未完全拆分。部分 C3 热点代码尺寸是 C2 的 1.4–3.3 倍。
建议修复顺序:
- 普通 Unsafe load/store
Unsafe.allocateInstance
- identity hash、
arraycopy、类型/数组 intrinsic
- Spark aggregation/TimSort 内联与代码尺寸
- 逃逸分析
完整结论、A/B 实验、profile 和源码证据见:analysis-report.md。
philosophers 分析完成。初始同批结果 C3/C2 为 2.768x(+176.8%),主要是 intrinsic 缺口被 64 线程 Scala STM 冲突重试放大。
核心结论:
-
最大单项:C3 缺少 _isInstance
- C3 profile 中
Class.isInstance + jni_IsInstanceOf 占 12.17% cycles。
- C2 禁用后从 5.829 s 升至 9.211 s,约可解释初始差距的 30.6%。
-
第二主因:缺少 _compareAndSetReference 和 _getReferenceVolatile
- C3 明确进入 Unsafe/VM fallback,C2 没有。
- C2 禁用后升至 8.322 s,约对应初始差距的 22.6%。
-
C3 profile devirtualization 是额外放大器
- 三组成对实验平均让 C3 再慢 1.748 s / 11.8%。
- 但关闭后 C3 仍约 14.9 s,说明它不是全部根因。
-
_allocateInstance 是次要缺口,C2 禁用后约慢 8.3%。
-
逃逸分析在本项影响只有约 3%,处于运行波动范围。
-
C3 使用的 CPU 核并不少,但受扰动的 PMU 实验中执行了 C2 的 7.43 倍指令、产生 17.73 倍 branch miss,属于额外动态工作和事务反馈,而不是简单并行度不足。
这些比例不能直接相加:组合禁用实验出现非线性结果,证明编译代码形态会改变 STM 冲突、rollback 和 retry 稳态。
建议修复顺序:
_isInstance
- reference CAS 和 volatile reference access 家族
_allocateInstance
- 补齐 intrinsic 后重新约束 profile-devirtualization 的 guard/CFG/code-size 收益模型
完整报告和全部源码、profile、A/B 证据见:analysis-report.md。
reactors 分析完成,完整报告见 analysis.md。
核心结论:
-
C3 为 33.880 秒,C2 为 20.388 秒,C3 慢 1.662x / 66.2%,绝对增加 13.491 秒。
-
主因是动态指令膨胀:C3 instructions 为 C2 的 2.71x,cycles 为 2.07x;不是单纯 CPU stall。
-
劣化集中在跨 reactor 调度、转发和创建:
ForkJoinThroughput:贡献 42.5%
Roundabout:16.9%
PingPong:12.4%
ThreadRing:11.4%
ForkJoinCreation:10.2%
五项合计贡献 93.4%;CountingActor 没有劣化。原始拆分数据见 C2 和 C3。
代码形态上,C3 的 $bang$extension 机器码是 C2 的 2.51x,并占 C3 cycles 的 5.35%;C3 有 37 次因已编译代码过大而拒绝内联,C2 只有 6 次。但调大内联阈值、强制内联 $bang$extension 均没有改善,所以这只是整体代码质量问题的表征,不能归因于单一内联点。
C3 确认缺少本 workload 命中的这些 C2 intrinsic:
- 引用 CAS、
compareAndExchangeLong
- Unsafe 普通/volatile 访问
Class.isInstance/isArray
arraycopy
allocateInstance
白名单证据见 jeandleIntrinsicLowering.cpp。它们造成 JNI/runtime fallback,但 profile 上限约 8%–10%,C2 禁用实验也未出现稳定退化,因此是次要项,解释不了 66%。
另外,C3 allocation elimination 目前只是保守子集,而且尚未接入当前编译流水线;对象初始化还使用 atomic store 和临时 release fence。不过 EA、ZeroTLAB A/B 都不支持它们是本次主因。
建议优先以 ForkJoinThroughput 为最小复现,检查:
$bang$extension → send → enqueueEvent → processEvents/executeBatch → ForkJoinPool
重点比较 C2/C3 LLVM IR 和 AArch64 汇编中的重复类型检查、oop/klass load、barrier、CAS/volatile lowering及未消除分支。PMU 原始证据在 C2 perf-stat 和 C3 perf-stat。
下面统一按“C2 性能 = 100%,数值越高越好”计算。之前比例由
dev_4的耗时比取倒数,当前比例采用你在另一环境的实测结果。总体对比
dev_4之前性能功能变化与残余缺口
_getByte、_getShort、_getLong、_getIntVolatile、_getLongVolatile、_putLongVolatile;Class.isInstance;System.arraycopy_getInt、_putByte、_allocateInstance;完整 EA/标量替换;一般代码质量_getInt在旧 profile 中单独占约 10.19% cycles,仍是最明确主因_getLong、_getShort、_putLong、_getByte、_putInt、_getReference、_getReferenceVolatile;hashCode/identityHashCode;isInstance/isArray/isPrimitive/isAssignableFrom;getLength;arraycopy_getInt、_putShort、_putByte、_getDouble、_putDouble、_allocateInstance;EA;Spark/TimSort/Kryo 热点代码膨胀和内联边界arraycopy;Class.isPrimitive;vectorizedHashCode;StringBuilder 构造器 intrinsic;StringBuilder/StringBuffer concat fusion;部分字符串编码 intrinsic_allocateInstance;完整 EA;部分 stream/lambda 对象消除;一般代码尺寸和内联反馈allocateInstance虽未实现,但已经不足以造成明显总分回归Class.isInstance;compareAndSetReference;getReferenceVolatile;其他 reference compare-and-exchange/weak-CAS;更多 VarHandle/Unsafe 内存序 loweringisInstance单项约对应旧总差距 30.6%;reference CAS + volatile load 单项约对应 22.6%。二者在 STM 中会被冲突/重试进一步放大_allocateInstance;完整 EA;profile devirtualization 的收益模型;并发 STM 下的代码形态和多模态稳定性;primitivegetAndAdddirect intrinsiccompareAndSetReference;compareAndExchangeLong;putInt/getIntVolatile/putReferenceVolatile;isInstance/isArray;arraycopy;weak reference CAS_getInt;_allocateInstance;恢复 direct primitivegetAndAdd{Byte,Short,Int,Long};跨 reactor 调度链代码膨胀;EA/对象消除当前仍缺失的 intrinsic 汇总
_getInt_putByte_putShort_getDouble/_putDouble_allocateInstance_getAndAdd{Byte,Short,Int,Long}direct intrinsicdev_4有、当前分支反而没有;目前会执行 Java volatile-load + weak-CAS 循环DoEscapeAnalysis主要只是内联启发式,没有 C2 ConnectionGraph 对等实现当前分支其他可能产生收益的公共变化
这些功能已经进入当前代码,但无法仅凭现有跨环境数据准确拆分贡献:
DoEscapeAnalysis使用点仍主要是内联启发式:jeandleCompilation.cpp。综合看,接下来最划算的实现顺序是:
_getInt、_putByte、_putShort。_allocateInstance。_getDouble/_putDouble。getAndAdd,重点验证 reactors。修完前两批后,Neo4j 和 PageRank 应当是最值得立即重测的两个验收项。
初测完成:这 5 项 C3 全部显著慢于 C2,几何平均耗时达到 C2 的 3.08 倍。
测试口径:
-XX:-TieredCompilation -XX:-UseJeandleCompiler-XX:-TieredCompilation -XX:+UseJeandleCompiler初步建议:
par-mnemonics:3.2× 回退且方差最低,最适合做 C2/C3 perf、热点汇编和编译日志对照。neo4j-analytics:回退最严重,达到 5.95×。page-rank判断问题是否也集中在 Spark closure、集合和装箱路径。philosophers、reactors涉及并发和调度,适合在较窄案例确认问题后分析。详细评估见 [assessment.md](/home/luofeng/jdk21-c3-jit/results/renaissance-c2-c3-selected-20260731_1036/assessment.md),结构化数据见 [comparison-last-5.tsv](/home/luofeng/jdk21-c3-jit/results/renaissance-c2-c3-selected-20260731_1036/comparison-last-5.tsv)。原始日志:[C2](/home/luofeng/jdk21-c3-jit/results/renaissance-c2-c3-selected-20260731_1036/c2/process.log)、[C3](/home/luofeng/jdk21-c3-jit/results/renaissance-c2-c3-selected-20260731_1036/c3/process.log)。
结论:
neo4j-analytics的主要劣化原因是 C3 缺失 Neo4j 高频依赖的 HotSpot intrinsic,尤其是Unsafe普通及 volatile 内存访问。结论置信度较高,源码、CPU profile 和因果 A/B 实验三者相互吻合。在 C2 中主动禁用 C3 缺失的 intrinsic 后,复现了约 70.5% 的 C3/C2 总差距。
C3 当前缺失的关键功能:
Unsafe普通/volatile load/store:_getInt、_getByte、_getLong、_getShort、_getIntVolatile、_getLongVolatile、_putByte、_putLongVolatile_isInstance_arraycopy_allocateInstanceC3 的 Unsafe 白名单目前基本只有
getAndAdd和compareAndSet原子族,[C3 支持表 (line 163)](/home/luofeng/jdk21-c3-jit/src/hotspot/share/jeandle/jeandleIntrinsicLowering.cpp:163);而 C2 明确支持完整普通/volatile/acquire/opaque/unaligned 访问族,以及 [`arraycopy` (line 578)](/home/luofeng/jdk21-c3-jit/src/hotspot/share/opto/c2compiler.cpp:578)、[`allocateInstance/isInstance` (line 687)](/home/luofeng/jdk21-c3-jit/src/hotspot/share/opto/c2compiler.cpp:687)。CPU profile 也直接反映了 fallback:
ThreadStateTransition::transition_from_native:16.44%Unsafe.getInt + Unsafe_GetInt:10.19%Unsafe.getByte + Unsafe_GetByte:8.03%Unsafe.getLongVolatile + Unsafe_GetLongVolatile:6.10%因此 C3 原本应该生成直接 load/store 的位置,实际频繁进入
Unsafe_Get*/Unsafe_Put*VM native 入口,并承担 Java/native 状态切换成本。[C3 原始热点 (line 1)](/home/luofeng/jdk21-c3-jit/results/neo4j-c2-c3-analysis-20260731/c3-profile-v2/perf-report-fields.txt:1)硬件计数器进一步排除了“缓存劣化”为主因:
所以本质是执行了更多 fallback 指令,并且 native 边界降低了流水线效率。
修复优先级建议:
_isInstance、_arraycopy、_allocateInstance。Unsafe_Get*、Unsafe_Put*和 native transition 是否从热点头部消失。完整分析、数据和实验说明在[分析报告 (line 8)](/home/luofeng/jdk21-c3-jit/results/neo4j-c2-c3-analysis-20260731/analysis-report.md:8)。初始结果为 C2 1.567 秒、C3 9.323 秒,即 C3 慢 5.95 倍,见[五项对比 (line 19)](/home/luofeng/jdk21-c3-jit/results/renaissance-c2-c3-selected-20260731_1036/assessment.md:19)。
par-mnemonics的主因已定位:C3 缠失_arraycopy和_allocateInstanceintrinsic,可以因果解释大约一半劣化。组合实验解释 C3/C2 总差距约 51.3%。
核心证据:
System.arraycopy + JVM_ArrayCopy占 14.39%,包括数组复制内部路径约 15.55%。Unsafe.allocateInstance + Unsafe_AllocateInstance + ObjAllocator占约 9.77%。源码也明确证实:
_arraycopy、_allocateInstance、_vectorizedHashCode、_isPrimitive。剩余约一半表现为代码质量和内联反馈问题:
HashMap.computeIfAbsent、groupingBy$53、wrapAndCopyInto等热方法的 C3 机器码约为 C2 的 1.7 倍。ArrayList.add → add → grow → grow整条链内联,[C2 inline 证据 (line 1565)](/home/luofeng/jdk21-c3-jit/results/par-mnemonics-c2-c3-analysis-20260731/c2-inlining/process.log:1565);C3 因already compiled into a big method提前停止,[C3 inline 证据 (line 1520)](/home/luofeng/jdk21-c3-jit/results/par-mnemonics-c2-c3-analysis-20260731/c3-inlining/process.log:1520)。其他缺口影响较小:
_vectorizedHashCode:单独约 2.4%。_isPrimitive:基本无影响。建议修复顺序:
_arraycopy_allocateInstance_vectorizedHashCode完整数据和实验记录在[分析报告 (line 8)](/home/luofeng/jdk21-c3-jit/results/par-mnemonics-c2-c3-analysis-20260731/analysis-report.md:8)。
page-rank分析完成。核心结论:C3 是 C2 的 3.163 倍,第一主因是 C3 缺少普通Unsafe.get/putintrinsic。关键证据:
allocateInstance、identity hash、类型检查和arraycopy等缺失项,可解释约 56.9%。建议修复顺序:
Unsafe.allocateInstancearraycopy、类型/数组 intrinsic完整结论、A/B 实验、profile 和源码证据见:analysis-report.md。
philosophers分析完成。初始同批结果 C3/C2 为 2.768x(+176.8%),主要是 intrinsic 缺口被 64 线程 Scala STM 冲突重试放大。核心结论:
最大单项:C3 缺少
_isInstanceClass.isInstance + jni_IsInstanceOf占 12.17% cycles。第二主因:缺少
_compareAndSetReference和_getReferenceVolatileC3 profile devirtualization 是额外放大器
_allocateInstance是次要缺口,C2 禁用后约慢 8.3%。逃逸分析在本项影响只有约 3%,处于运行波动范围。
C3 使用的 CPU 核并不少,但受扰动的 PMU 实验中执行了 C2 的 7.43 倍指令、产生 17.73 倍 branch miss,属于额外动态工作和事务反馈,而不是简单并行度不足。
这些比例不能直接相加:组合禁用实验出现非线性结果,证明编译代码形态会改变 STM 冲突、rollback 和 retry 稳态。
建议修复顺序:
_isInstance_allocateInstance完整报告和全部源码、profile、A/B 证据见:analysis-report.md。
reactors分析完成,完整报告见 analysis.md。核心结论:
C3 为 33.880 秒,C2 为 20.388 秒,C3 慢
1.662x / 66.2%,绝对增加 13.491 秒。主因是动态指令膨胀:C3 instructions 为 C2 的
2.71x,cycles 为2.07x;不是单纯 CPU stall。劣化集中在跨 reactor 调度、转发和创建:
ForkJoinThroughput:贡献 42.5%Roundabout:16.9%PingPong:12.4%ThreadRing:11.4%ForkJoinCreation:10.2%五项合计贡献 93.4%;
CountingActor没有劣化。原始拆分数据见 C2 和 C3。代码形态上,C3 的
$bang$extension机器码是 C2 的2.51x,并占 C3 cycles 的 5.35%;C3 有 37 次因已编译代码过大而拒绝内联,C2 只有 6 次。但调大内联阈值、强制内联$bang$extension均没有改善,所以这只是整体代码质量问题的表征,不能归因于单一内联点。C3 确认缺少本 workload 命中的这些 C2 intrinsic:
compareAndExchangeLongClass.isInstance/isArrayarraycopyallocateInstance白名单证据见 jeandleIntrinsicLowering.cpp。它们造成 JNI/runtime fallback,但 profile 上限约 8%–10%,C2 禁用实验也未出现稳定退化,因此是次要项,解释不了 66%。
另外,C3 allocation elimination 目前只是保守子集,而且尚未接入当前编译流水线;对象初始化还使用 atomic store 和临时 release fence。不过 EA、ZeroTLAB A/B 都不支持它们是本次主因。
建议优先以
ForkJoinThroughput为最小复现,检查:$bang$extension → send → enqueueEvent → processEvents/executeBatch → ForkJoinPool重点比较 C2/C3 LLVM IR 和 AArch64 汇编中的重复类型检查、oop/klass load、barrier、CAS/volatile lowering及未消除分支。PMU 原始证据在 C2 perf-stat 和 C3 perf-stat。