大致这么一段逻辑
```
void test(item){
if(array_list == null){
array_list = new ArrayList<>();
}
if(!array_list.contains(item)) {
array_list.add(item);
}
}
```
这段代码在两台机器和不同用户之间会有些比较明显的性能差异.
具体是有两台机器A,B.
A为一台KVM,B是物理机器.
A宿主机器跟B是同型号CPU.
现象是一个benchmark在A上大概是5分钟不到.
而B是20-30分钟左右.
另外一个就是在B上以root和非root用户偶尔也会有些可见差别.
这个case的实际逻辑是是加载并解析一个配置,并且是单线程的.
所以整体过程应该是deterministic的.
理论上不应该有这么明显的差异的.
perf了一下.
B上面有一段是change_protection_range会相对显著地跟A有所区别.
大概翻了下对应内核版本的实现,看上去有一些huge page/large page相关的东西.
但是看系统参数和JVM配置,largepage相关的选项也并没有打开.
而且对比了下两者的环境变量和生效配置以及相应的.so也是一致的.
所以从系统层面是上来说,应该都是没差别的.
于是看了下VMThread的safepoint信息.
从日志里看是有一些bias lock的revoke情况.
但是从代码逻辑上来说并没有显著的synchronized应用.
这点就有些奇怪了.
把bias lock disable之后greys attach上去做profile.
然后发觉貌似性能是上去了.
两边都差不多是每秒20-30w左右的调用次数.
但是感觉不是很有说服力.
一个想法是bias lock带来的一点object的开销在这种频繁可能会有新对象创建的场景下被放大.
这样的话可能会跟perf的change_protection_range有联动.
因为可能会对cacheline有影响,毕竟一个是KVM一个是物理机器.
为了确定关联性,把profile去掉之后发觉性能差异有出现了.
那么为什么profile会影响到性能呢?
而且是positive的影响.
想起agent在retransform的时候,redefine class会触发deoptimize.
那么,如果是JIT的问题的话,agent deoptimize能提升性能,也就意味着触发了某个优化规则导致的.
尝试不同的compilation policy.
发现在non-tiered compilation(simple/stack walk)的情况下是ok的.
但是开启tiered compilation(simple threshold/advance threshold)之后则有大幅下降.
用perf-map-agent重新perf看了下.
发现non-tiered的情况下,hot code是array list的contains/indexOf.
而tiered的则是test和equals.
到这里其实就已经很明显了.
non-tiered只inline array list的相关调用,包括indexOf里的equals.
所以perf只看到了contains或者indexOf.
而tiered JIT了test和equals.
实际上看生产的assembly,除了做inline之外,同时还对indexOf做了loop unroll.
但是看perf的结果是test和equals同时被JIT了.
那么也就意味着test没有把equals inline进去.
看JIT生产的assembly证实了,对于整个调用链,也就是indexOf里的equals还是callq调用的.
所以应该是某个机制限制了把equals inline进去.
从而loop unroll之后反而引入了函数调用开销.
把inline日志打开可以看到test的inline有两类信息.
一个是callee is too large.
另外一个是already compiled into a big method.
这就可以解释了.
因为test本身不复杂,所以没有触发自身被caller inline进去.
于是hot code也只会inline这个调用链以下的.
而由于inline和loop unroll使得本身的code size变得比较大,在尝试进一步inline equals的时候被拒绝了.
调整这两个相关参数之后再看确实没有了equals的调用,同时性能保持了一致性.
而再回头看为什么KVM和物理机会有性能差异的时候,大概就是因为compiler thread的数量和并发程度不同.
因此在尝试inline equals的时候会出现时机和结果的不一致性.
所以想想的话,从代码风格上来说,确实compact和reusable的会相对来说比较友好一点.
像Stream这种batch process的风格来说的话,确实有可能for之类的手工展开的好.
因为一个loop unroll,一个是函数主题一般都比较小.
再就是手工展开的话,很难说不会触发像上面这种inline不完整反而受累的情况.
2019-11-16
订阅:
博文评论 (Atom)
牛来餐馆
看了欢迎来龙餐馆. 总的来说,作为一个院线电影,成品多少是有点不太匹配的. 看起来更多像是一部网络大电影. 尤其涉及到一些大场面特效的时候,可以看得出服化道的降本增效. 剧本层面有一些比较有意思或者说闪光点. 就是试图用一个餐馆或者厨师来串起一个比较宏大的叙事题材. 这个可以说是...
-
最近写一个AgentCLI原型玩具的时候,发现一些比较有意思的点. 因为主要是照着DeepSeek API来写的. 所以基本上也可以说是OpenAI Chat Completion API的一些点. 不过,可能有些是DeepSeek特有的. 一个是请求的stop list. 大致...
-
从未对雪有过什么幻想. 于是,当这雪终究下下来了,也没什么特别的兴奋. 只是,由于是南方人,也就姑且好奇了一番. 望了望着属于别处的风景. 也许是因为昨晚下过雨,也或许是本来就会如此. 冰混着点积水,倒是怀念起阳光灿烂的日子了. 面对着这未知的...
-
去看了长安的荔枝. 前半段还可以,尤其像荔枝林里不知道是笑还是哭的几个镜头表演算是相当出色了. 结合人物背景的那种对目标的绝望与对当下人际环境的希望的交叉矛盾心理. 后半段就有些过滤潦草了. 如果说整片是对于一骑红尘妃子笑,无人知是荔枝来的解构的话. 带入民生潦倒涂炭这点是没问题...
没有评论:
发表评论