<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Kotlin/JVM 性能基础 on Dorkytiger 的小屋</title>
    <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/</link>
    <description>Recent content in Kotlin/JVM 性能基础 on Dorkytiger 的小屋</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <atom:link href="https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>四种执行形态：为什么必须预热</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/01-execution-tiers/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/01-execution-tiers/</guid>
      <description>&lt;h1 id=&#34;21-四种执行形态为什么必须预热&#34;&gt;2.1 四种执行形态：为什么必须预热&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：无　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/02-jit-optimizations-and-deopt/&#34;&gt;2.2 JIT 的三大优化与去优化&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/02-jvm-performance-basics/01-execution-tiers/&#34;&gt;02-jvm-performance-basics/01-execution-tiers&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一句话结论&#34;&gt;一句话结论&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;JVM 上的同一段代码，随着被调用的次数增加，会经历「解释执行 → C1 编译 → C2 编译」三种形态，性能台阶式提升。&lt;/strong&gt; 所以你测前 100 次调用，测的其实是&lt;strong&gt;最慢的那种形态&lt;/strong&gt;，它和线上稳态毫无关系。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一用餐厅厨房理解分层编译&#34;&gt;一、用餐厅厨房理解分层编译&lt;/h2&gt;
&lt;p&gt;假设你开了一家餐厅，来了一个新厨师。看他怎么做菜：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;阶段&lt;/th&gt;
					&lt;th&gt;厨师状态&lt;/th&gt;
					&lt;th&gt;类比 JVM&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;第 1 天&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;照着菜谱一步一步做，看一行做一步，很慢但立刻能上手&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;解释执行&lt;/strong&gt;：逐条读取字节码并执行&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;做了几天后&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;熟练了，动作连贯，不用再看菜谱&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;C1 编译&lt;/strong&gt;：编译成机器码，快，但只做简单优化&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;做了几百次之后&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;老师傅开始&lt;strong&gt;重新设计整个流程&lt;/strong&gt;：提前备料、合并步骤、把常用的调料放在手边&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;C2 编译&lt;/strong&gt;：做激进优化（内联、逃逸分析、循环展开）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;正在做菜时被老师傅接手&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;这道菜做到一半，老师傅直接接手继续&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;OSR（栈上替换）&lt;/strong&gt;：循环体被热点编译&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;关键点&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;老师傅不会第一天就来。&lt;/strong&gt; 他需要先观察你做了几百次，确认「这道菜真的是最常点的」，才值得花时间重新设计流程。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;重新设计流程很费时间&lt;/strong&gt;（编译耗时），所以只有&lt;strong&gt;足够热&lt;/strong&gt;的代码才配得上 C2。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;所以：前几十次调用的性能，和你线上跑了一小时之后的性能，完全是两回事。&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二三种形态的实际差异&#34;&gt;二、三种形态的实际差异&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;形态&lt;/th&gt;
					&lt;th&gt;触发条件&lt;/th&gt;
					&lt;th&gt;相对速度&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;解释执行&lt;/td&gt;
					&lt;td&gt;一开始&lt;/td&gt;
					&lt;td&gt;最慢（可能慢 5–20 倍）&lt;/td&gt;
					&lt;td&gt;逐条字节码执行，还要做类型检查&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;C1（客户端编译器）&lt;/td&gt;
					&lt;td&gt;调用几百次后&lt;/td&gt;
					&lt;td&gt;中等&lt;/td&gt;
					&lt;td&gt;编译快、优化少，目标是「尽快摆脱慢速解释」&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;C2（服务端编译器）&lt;/td&gt;
					&lt;td&gt;调用数千到上万次后&lt;/td&gt;
					&lt;td&gt;最快&lt;/td&gt;
					&lt;td&gt;编译慢、优化激进，会做基于运行时 profile 的推测性优化&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;具体阈值随 JDK 版本和运行情况变化&lt;/strong&gt;（大致在数千到上万次调用 / 循环回边这个量级）。&lt;strong&gt;不要记具体数字，要记住「有台阶」这个事实。&lt;/strong&gt; 需要精确数字时用 &lt;code&gt;-XX:+PrintCompilation&lt;/code&gt; 观察自己的程序。&lt;/p&gt;</description>
    </item>
    <item>
      <title>JIT 的三大优化与去优化</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/02-jit-optimizations-and-deopt/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/02-jit-optimizations-and-deopt/</guid>
      <description>&lt;h1 id=&#34;22-jit-的三大优化与去优化&#34;&gt;2.2 JIT 的三大优化与去优化&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/01-execution-tiers/&#34;&gt;2.1 四种执行形态&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/03-microbenchmark-traps/&#34;&gt;2.3 微基准四大陷阱&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/02-jvm-performance-basics/02-jit-optimizations-and-deopt/&#34;&gt;02-jvm-performance-basics/02-jit-optimizations-and-deopt&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一句话结论&#34;&gt;一句话结论&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;C2 编译器会基于「运行时观察到的现象」做推测性优化——比如「这个调用点永远只有一种类型」。一旦推测被打破，它会撤销优化、退回慢速路径。&lt;/strong&gt; 这就是「去优化」，也是性能无端抖动的常见来源。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一先看三大优化用厨房类比&#34;&gt;一、先看三大优化（用厨房类比）&lt;/h2&gt;
&lt;p&gt;接着上一节的餐厅比喻。老师傅（C2）在观察了足够多次之后，会做三件事：&lt;/p&gt;
&lt;h3 id=&#34;优化一方法内联inlining-把常用步骤直接展开&#34;&gt;优化一：方法内联（Inlining）—— 把常用步骤直接展开&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;原来&lt;/strong&gt;：每次要做「去冰箱拿鸡蛋」这个动作，都要走过去、打开冰箱、拿蛋、关门。厨师会把它写成一个标准动作反复执行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;老师傅的做法&lt;/strong&gt;：既然每次都这样，那我干脆把这个动作&lt;strong&gt;直接展开到主流程里&lt;/strong&gt;，不用来回跑（省掉「方法调用的开销」），而且展开之后还能看到&lt;strong&gt;更大的优化空间&lt;/strong&gt;（比如发现「拿蛋之后马上要打蛋」，可以合并）。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;技术含义&lt;/strong&gt;：把被调用方法的字节码直接嵌入调用方，消除调用开销，并为后续优化（常量传播、死代码消除）创造条件。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这也是为什么火焰图会「丢帧」&lt;/strong&gt;：内联之后，中间那个方法在调用栈上就不存在了。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;优化二逃逸分析escape-analysis-只在内部用的东西不用真造一个&#34;&gt;优化二：逃逸分析（Escape Analysis）—— 只在内部用的东西，不用真造一个&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;原来&lt;/strong&gt;：厨房要盛一份配菜，得专门洗一个盘子、装盘、用完再洗。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;老师傅发现&lt;/strong&gt;：这个盘子&lt;strong&gt;只在厨房内部用&lt;/strong&gt;，端出去之前就被吃掉了……那我为什么还要真的拿一个盘子？直接在案板上处理完就行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;同理，还有两个衍生优化&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;锁消除&lt;/strong&gt;：如果一把锁只在内部用、不会被别的厨师碰到，那就没必要真锁。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;标量替换&lt;/strong&gt;：如果一个对象只在内部用，可以把它&lt;strong&gt;拆成几个局部变量&lt;/strong&gt;，连对象都不分配。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;技术含义&lt;/strong&gt;：如果 JIT 能证明一个对象不会「逃逸」出当前方法/线程，就可以把它分配在栈上（甚至拆成标量），从而&lt;strong&gt;减少堆分配与 GC 压力&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这解释了一个反直觉的现象&lt;/strong&gt;：有些看起来「创建了很多对象」的代码，实测非常快——因为那些对象根本没被真正创建。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;优化三基于-profile-的推测优化--按最常发生的情况设计流程&#34;&gt;优化三：基于 profile 的推测优化 —— 按「最常发生的情况」设计流程&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;老师傅观察到&lt;/strong&gt;：来这里的客人&lt;strong&gt;99% 都点同一道招牌菜&lt;/strong&gt;。于是他干脆把这道菜的料都提前备好放在手边。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;风险&lt;/strong&gt;：如果有一天来了个点别的菜的客人，他准备的料全用不上，还得临时找——&lt;strong&gt;比原来更慢&lt;/strong&gt;。&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;技术含义&lt;/strong&gt;：C2 会记录调用点的&lt;strong&gt;类型分布&lt;/strong&gt;。如果它发现「这个接口调用永远指向同一个实现类」，就会把虚调用（间接跳转）优化成直接调用（甚至内联掉）。这叫 &lt;strong&gt;monomorphic（单态）&lt;/strong&gt; 调用点。&lt;/p&gt;
&lt;p&gt;如果后来出现了第二种类型，就叫 &lt;strong&gt;bimorphic（双态）&lt;/strong&gt;；出现多种类型就是 &lt;strong&gt;megamorphic（巨态）&lt;/strong&gt;，此时 JIT 会放弃优化，退回慢速的虚调用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二去优化优化被撤销的瞬间&#34;&gt;二、去优化：优化被撤销的瞬间&lt;/h2&gt;
&lt;h3 id=&#34;什么是去优化&#34;&gt;什么是去优化&lt;/h3&gt;
&lt;p&gt;当 C2 的推测被打破时，它必须&lt;strong&gt;撤销已经生成的机器码&lt;/strong&gt;，退回到解释执行或较低优化的版本，然后带着新信息重新编译。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;① C2 假设「这个调用点只有 A 类型」→ 生成直接调用 A 的机器码（很快）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;② 某次请求带来了 B 类型
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;③ 假设被打破 → 当前机器码作废（made not entrant）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;④ 退回解释执行 / 重新走 C1、C2 编译
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;⑤ 期间性能明显下降（可能出现毫秒级甚至更长的抖动）
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;触发去优化的常见原因&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>微基准四大陷阱</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/03-microbenchmark-traps/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/03-microbenchmark-traps/</guid>
      <description>&lt;h1 id=&#34;23-微基准四大陷阱&#34;&gt;2.3 微基准四大陷阱&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/02-jit-optimizations-and-deopt/&#34;&gt;2.2 JIT 的三大优化与去优化&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/04-warmup-steady-coldstart/&#34;&gt;2.4 预热、稳态与冷启动&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/02-jvm-performance-basics/03-microbenchmark-traps/&#34;&gt;02-jvm-performance-basics/03-microbenchmark-traps&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一句话结论&#34;&gt;一句话结论&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;你写的基准测试，最大的对手不是机器噪声，而是优化器。&lt;/strong&gt; 它会把你的计算整个删掉、提前算好、或者把你的分支预测喂得太好。四个陷阱里，任何一个都会让你的数字变成废纸。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一陷阱一死代码消除dce-算了但没人用那就不算&#34;&gt;一、陷阱一：死代码消除（DCE）—— 算了但没人用，那就不算&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：测出 0.2 ns/op 这种物理上不可能的数字。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：计算的结果&lt;strong&gt;没有被使用&lt;/strong&gt;，JIT 判断「这个计算没有副作用」，于是把整个循环删掉了。你测的是一个空循环。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-kotlin&#34; data-lang=&#34;kotlin&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// ❌ 结果没人用 → 整个计算被删除
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;repeat(&lt;span style=&#34;color:#ae81ff&#34;&gt;100_000&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sumArray(&lt;span style=&#34;color:#66d9ef&#34;&gt;data&lt;/span&gt;)          &lt;span style=&#34;color:#75715e&#34;&gt;// 返回值丢掉了
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// ✅ 结果被消费 → 计算保留
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; sink = &lt;span style=&#34;color:#ae81ff&#34;&gt;0L&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;repeat(&lt;span style=&#34;color:#ae81ff&#34;&gt;100_000&lt;/span&gt;) {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    sink &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; sumArray(&lt;span style=&#34;color:#66d9ef&#34;&gt;data&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;println(sink)               &lt;span style=&#34;color:#75715e&#34;&gt;// 确保 sink 真的被使用，否则它也可能被优化掉
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;为什么难发现&lt;/strong&gt;：代码看起来「明明在算」，而且跑得飞快——你会以为是好事。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;判断方法&lt;/strong&gt;：把数据规模改成 10 倍，如果耗时&lt;strong&gt;完全不变&lt;/strong&gt;，说明计算被删了。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二陷阱二常量折叠--输入是已知的那就提前算好&#34;&gt;二、陷阱二：常量折叠 —— 输入是已知的，那就提前算好&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;现象&lt;/strong&gt;：数字看起来合理，但&lt;strong&gt;与输入规模无关&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;原因&lt;/strong&gt;：输入在编译期就能确定（比如字面量、&lt;code&gt;const&lt;/code&gt;），JIT 直接在编译时把结果算出来。&lt;/p&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-kotlin&#34; data-lang=&#34;kotlin&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// ❌ 输入是编译期常量 → 结果被提前算好
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;val&lt;/span&gt; data = IntArray(&lt;span style=&#34;color:#ae81ff&#34;&gt;4096&lt;/span&gt;) { &lt;span style=&#34;color:#66d9ef&#34;&gt;it&lt;/span&gt; }          &lt;span style=&#34;color:#75715e&#34;&gt;// 规律数据，JIT 能推导
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;repeat(&lt;span style=&#34;color:#ae81ff&#34;&gt;100_000&lt;/span&gt;) { sink &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; sumArray(&lt;span style=&#34;color:#66d9ef&#34;&gt;data&lt;/span&gt;) }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#75715e&#34;&gt;// ✅ 输入在运行时才确定
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;val&lt;/span&gt; rnd = Random(&lt;span style=&#34;color:#a6e22e&#34;&gt;System&lt;/span&gt;.nanoTime())        &lt;span style=&#34;color:#75715e&#34;&gt;// 真正的运行时随机
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;val&lt;/span&gt; data = IntArray(&lt;span style=&#34;color:#ae81ff&#34;&gt;4096&lt;/span&gt;) { rnd.nextInt() }
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;注意&lt;/strong&gt;：即使数据是「运行时生成的」，如果生成方式是 &lt;code&gt;{ it }&lt;/code&gt; 或 &lt;code&gt;{ it * 2 }&lt;/code&gt; 这类&lt;strong&gt;规律模式&lt;/strong&gt;，JIT 仍可能利用这个规律（比如识别出等差数列直接套公式）。&lt;/p&gt;</description>
    </item>
    <item>
      <title>预热、稳态与冷启动</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/04-warmup-steady-coldstart/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/04-warmup-steady-coldstart/</guid>
      <description>&lt;h1 id=&#34;24-预热稳态与冷启动&#34;&gt;2.4 预热、稳态与冷启动&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/03-microbenchmark-traps/&#34;&gt;2.3 微基准四大陷阱&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/05-memory-and-gc/&#34;&gt;2.5 内存与 GC&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/02-jvm-performance-basics/04-warmup-steady-coldstart/&#34;&gt;02-jvm-performance-basics/04-warmup-steady-coldstart&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一句话结论&#34;&gt;一句话结论&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;同一份代码，在「刚启动」「跑了 30 秒」「跑了一小时」三个时刻，是三个不同的系统。&lt;/strong&gt; 所以任何性能数字都必须标明它测的是哪一段——而判断「进入稳态」要靠&lt;strong&gt;数据&lt;/strong&gt;（方差收敛），不是靠固定的秒数。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一用运动员热身体理解三个阶段&#34;&gt;一、用运动员热身体理解三个阶段&lt;/h2&gt;
&lt;p&gt;你去跑 100 米：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;阶段&lt;/th&gt;
					&lt;th&gt;状态&lt;/th&gt;
					&lt;th&gt;成绩&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;刚睡醒，直接跑&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;肌肉僵硬、心率没上来、动作不协调&lt;/td&gt;
					&lt;td&gt;15 秒&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;热身 10 分钟后跑&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;身体进入状态&lt;/td&gt;
					&lt;td&gt;12 秒&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;热身充分、连续跑几次&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;完全进入状态，成绩稳定&lt;/td&gt;
					&lt;td&gt;11.5 秒（波动很小）&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;关键洞察&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;「热身多久」没有标准答案。&lt;/strong&gt; 有人 5 分钟就够，有人要 20 分钟。&lt;strong&gt;唯一的判断标准是「成绩是否稳定了」&lt;/strong&gt;，而不是「跑了多久」。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;你不会把「刚睡醒那次 15 秒」算进你的成绩统计。&lt;/strong&gt; 那是一次热身，不是测量。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;JVM 完全一样，而且有三个阶段。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二三个阶段分别是什么在变慢&#34;&gt;二、三个阶段分别是什么在变慢&lt;/h2&gt;
&lt;h3 id=&#34;阶段一冷启动进程刚起来&#34;&gt;阶段一：冷启动（进程刚起来）&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;正在发生&lt;/th&gt;
					&lt;th&gt;影响&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;JVM 启动、类加载&lt;/td&gt;
					&lt;td&gt;大量类需要从磁盘读取、验证、链接&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;所有代码在解释执行&lt;/td&gt;
					&lt;td&gt;最慢的执行形态&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;连接池是空的&lt;/td&gt;
					&lt;td&gt;需要建立数据库/Redis 连接&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;缓存是冷的&lt;/td&gt;
					&lt;td&gt;缓存穿透到数据库&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;线程池刚创建&lt;/td&gt;
					&lt;td&gt;线程创建开销&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;配置/密钥/证书加载&lt;/td&gt;
					&lt;td&gt;可能涉及网络&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;表现&lt;/strong&gt;：第一个请求可能需要&lt;strong&gt;几百毫秒到几秒&lt;/strong&gt;，而稳态是几十毫秒。&lt;/p&gt;
&lt;h3 id=&#34;阶段二预热期跑了十几秒到几分钟&#34;&gt;阶段二：预热期（跑了十几秒到几分钟）&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;正在发生&lt;/th&gt;
					&lt;th&gt;影响&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;热点方法升到 C1、C2&lt;/td&gt;
					&lt;td&gt;性能&lt;strong&gt;台阶式&lt;/strong&gt;改善（第 1 节）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;连接池填满&lt;/td&gt;
					&lt;td&gt;不用再等建连&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;JIT 的 profile 逐渐稳定&lt;/td&gt;
					&lt;td&gt;类型分布、分支走向被摸清&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;本地缓存逐渐升温&lt;/td&gt;
					&lt;td&gt;命中率上升&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;表现&lt;/strong&gt;：数字&lt;strong&gt;不稳定&lt;/strong&gt;，仍在下降。&lt;/p&gt;</description>
    </item>
    <item>
      <title>内存与 GC：分配速率才是关键</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/05-memory-and-gc/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/05-memory-and-gc/</guid>
      <description>&lt;h1 id=&#34;25-内存与-gc分配速率才是关键&#34;&gt;2.5 内存与 GC：分配速率才是关键&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/04-warmup-steady-coldstart/&#34;&gt;2.4 预热、稳态与冷启动&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/06-concurrency-primitives/&#34;&gt;2.6 并发原语成本与伪共享&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/02-jvm-performance-basics/05-memory-and-gc/&#34;&gt;02-jvm-performance-basics/05-memory-and-gc&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一句话结论&#34;&gt;一句话结论&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;决定 GC 压力的是「每秒产生多少垃圾」，不是「堆有多大」。&lt;/strong&gt; 所以 JVM 上的优化目标不是「零分配」，而是&lt;strong&gt;降低分配速率&lt;/strong&gt;和&lt;strong&gt;减少对象存活时间&lt;/strong&gt;。这两件事的效果，比调堆大小大得多。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二用办公室垃圾桶理解-gc&#34;&gt;二、用办公室垃圾桶理解 GC&lt;/h2&gt;
&lt;p&gt;假设一个办公室，每个人都在往垃圾桶里扔废纸：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;概念&lt;/th&gt;
					&lt;th&gt;类比&lt;/th&gt;
					&lt;th&gt;技术含义&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;扔废纸&lt;/td&gt;
					&lt;td&gt;分配对象&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;new&lt;/code&gt; / 创建对象&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;垃圾桶容量&lt;/td&gt;
					&lt;td&gt;堆大小&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;-Xmx&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;扔废纸的速度&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;分配速率&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;MB/s——最关键&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;保洁员来倒垃圾&lt;/td&gt;
					&lt;td&gt;GC&lt;/td&gt;
					&lt;td&gt;回收不可达对象&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;倒垃圾时全员停手&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;GC 停顿&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;Stop-The-World&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;有些文件不能扔，要归档&lt;/td&gt;
					&lt;td&gt;对象晋升&lt;/td&gt;
					&lt;td&gt;从年轻代到老年代&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;每人一个自己的小垃圾桶&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;TLAB&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;线程本地分配缓冲，避免排队领纸&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;一个巨大的纸箱&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;Humongous 对象&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;塞不进普通桶，触发特殊处理&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;从这个类比能推出三个关键结论&lt;/strong&gt;：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;垃圾桶越大，倒的次数越少，但每次倒的时间越长。&lt;/strong&gt; → 堆不是越大越好。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;如果大家扔得飞快，保洁员就要频繁来倒，全员频繁停手。&lt;/strong&gt; → &lt;strong&gt;分配速率是核心指标&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;每人有自己的小垃圾桶（TLAB），所以「扔纸」这个动作本身几乎不花时间。&lt;/strong&gt; → &lt;strong&gt;分配本身很便宜，代价在 GC&lt;/strong&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;第 3 条是最反直觉的&lt;/strong&gt;：从 C++ 过来的人会本能地认为「分配很贵，要尽量少分配」。在 JVM 上，分配一个普通对象就是「在一个线程私有区域里移动一下指针」（TLAB 里的指针碰撞），&lt;strong&gt;比一次内存访问还快&lt;/strong&gt;。真正贵的是&lt;strong&gt;这些对象累积起来导致的 GC&lt;/strong&gt;。&lt;/p&gt;</description>
    </item>
    <item>
      <title>并发原语成本与伪共享</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/06-concurrency-primitives/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/06-concurrency-primitives/</guid>
      <description>&lt;h1 id=&#34;26-并发原语成本与伪共享&#34;&gt;2.6 并发原语成本与伪共享&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/05-memory-and-gc/&#34;&gt;2.5 内存与 GC&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/07-coroutines-cost-model/&#34;&gt;2.7 协程的真实开销模型&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/02-jvm-performance-basics/06-concurrency-primitives/&#34;&gt;02-jvm-performance-basics/06-concurrency-primitives&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一句话结论&#34;&gt;一句话结论&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;无竞争的锁很便宜（几十纳秒），有竞争的锁非常贵（可能几百纳秒到微秒级）——差距是数量级的。&lt;/strong&gt; 而且加线程数超过某个点后，吞吐会&lt;strong&gt;下降&lt;/strong&gt;：因为上下文切换、缓存失效和锁竞争的成本超过了并行带来的收益。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一用共享打印机理解并发成本&#34;&gt;一、用共享打印机理解并发成本&lt;/h2&gt;
&lt;p&gt;办公室里有一台打印机，你和同事都要用：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;情况&lt;/th&gt;
					&lt;th&gt;你的体验&lt;/th&gt;
					&lt;th&gt;技术含义&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;没人在用，你直接印&lt;/td&gt;
					&lt;td&gt;走过去就能印，几秒钟&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;无竞争锁&lt;/strong&gt;：几十纳秒&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;同事正在印，你等他打完&lt;/td&gt;
					&lt;td&gt;排队等待&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;有竞争锁&lt;/strong&gt;：排队时间可能很长&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;你们都想改同一份文档&lt;/td&gt;
					&lt;td&gt;用「先检查版本，再提交」的方式，冲突就重试&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;CAS（比较并交换）&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;你刚坐下准备干活，又被叫去开会，回来还得重新进入状态&lt;/td&gt;
					&lt;td&gt;效率损失&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;上下文切换&lt;/strong&gt;：还要付出「缓存失效」的代价&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;你和同事的笔记本放在同一个抽屉里&lt;/strong&gt;，他每次开抽屉你都得停一下&lt;/td&gt;
					&lt;td&gt;明明没共享数据，却互相干扰&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;伪共享（false sharing）&lt;/strong&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;注意最后一条&lt;/strong&gt;：这就是伪共享——&lt;strong&gt;你们没有共享任何数据，但因为数据在内存里离得太近，硬件层面的缓存同步把你们绑在了一起。&lt;/strong&gt;&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二各种操作的成本量级&#34;&gt;二、各种操作的成本量级&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ 下面都是&lt;strong&gt;量级参考&lt;/strong&gt;，随 CPU、JDK 版本、竞争程度变化很大。&lt;strong&gt;请用配套代码在自己的机器上测一遍&lt;/strong&gt;——这个练习的价值远大于记住数字。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;操作&lt;/th&gt;
					&lt;th&gt;量级&lt;/th&gt;
					&lt;th&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;一次 L1 缓存访问&lt;/td&gt;
					&lt;td&gt;~1 ns&lt;/td&gt;
					&lt;td&gt;基准&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;无竞争的 &lt;code&gt;synchronized&lt;/code&gt;&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;十几到几十 ns&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;现代 JVM 的偏向锁/轻量级锁优化得很好&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;CAS 操作&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;十几 ns&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;AtomicInteger.incrementAndGet()&lt;/code&gt; 无竞争时&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;有竞争的锁&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;几百 ns ~ 微秒级&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;差距是数量级的，且有排队&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;volatile 写&lt;/td&gt;
					&lt;td&gt;几十 ns&lt;/td&gt;
					&lt;td&gt;需要内存屏障&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;线程创建&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;几十微秒&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;比一次普通操作贵 1000 倍&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;一次上下文切换&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;1–5 微秒&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;加上缓存失效的间接成本会更多&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;协程挂起/恢复&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;纳秒到数百纳秒&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;比线程切换便宜几个数量级&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;伪共享导致的额外延迟&lt;/td&gt;
					&lt;td&gt;可达百纳秒级&lt;/td&gt;
					&lt;td&gt;取决于访问模式与竞争强度&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;从这张表能读出三个结论&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>协程的真实开销模型</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/07-coroutines-cost-model/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/07-coroutines-cost-model/</guid>
      <description>&lt;h1 id=&#34;27-协程的真实开销模型&#34;&gt;2.7 协程的真实开销模型&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/06-concurrency-primitives/&#34;&gt;2.6 并发原语成本与伪共享&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/08-jmh-and-kotlinx-benchmark/&#34;&gt;2.8 JMH 与 kotlinx-benchmark&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/02-jvm-performance-basics/07-coroutines-cost-model/&#34;&gt;02-jvm-performance-basics/07-coroutines-cost-model&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一句话结论&#34;&gt;一句话结论&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;协程把「线程」和「并发任务」解耦了，但它不是免费的、也不是无限的。&lt;/strong&gt; &lt;code&gt;Dispatchers.Default&lt;/code&gt; 的并行度约等于 CPU 核数——&lt;strong&gt;一旦被阻塞调用占满，所有使用它的协程会一起卡住&lt;/strong&gt;。这是 Kotlin 后端最高频、也最难排查的性能故障。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一用餐厅服务理解协程&#34;&gt;一、用餐厅服务理解协程&lt;/h2&gt;
&lt;p&gt;一家餐厅有 &lt;strong&gt;8 个服务员&lt;/strong&gt;（= CPU 核数）：&lt;/p&gt;
&lt;h3 id=&#34;线程模型传统&#34;&gt;线程模型（传统）&lt;/h3&gt;
&lt;p&gt;一个服务员负责一桌客人：&lt;strong&gt;点单 → 等厨房做菜 → 上菜 → 结账&lt;/strong&gt;，全程守着这桌。等菜的时候他什么都不干，也不能服务别的桌。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;结果&lt;/strong&gt;：8 个服务员最多同时服务 8 桌。第 9 桌客人要等有人结账。&lt;/p&gt;
&lt;h3 id=&#34;协程模型&#34;&gt;协程模型&lt;/h3&gt;
&lt;p&gt;一个服务员可以这样干：&lt;strong&gt;给 1 号桌点单 → 把单子交给厨房 → 在等菜的时间里，去 2 号桌点单 → 再去 3 号桌 → 菜好了就上菜&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;结果&lt;/strong&gt;：8 个服务员可以同时服务几百桌，因为他们不「死等」。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这就是协程的核心价值&lt;/strong&gt;：在等待 IO 的时候，让出线程去处理别的任务。&lt;/p&gt;
&lt;h3 id=&#34;但协程有一个致命限制&#34;&gt;但协程有一个致命限制&lt;/h3&gt;
&lt;p&gt;假设某个服务员被派去做一件&lt;strong&gt;必须亲自守着&lt;/strong&gt;的事——比如「站在菜市场等摊主称菜」（这就是&lt;strong&gt;阻塞调用&lt;/strong&gt;）。&lt;/p&gt;
&lt;p&gt;他离开了餐厅。现在只剩 &lt;strong&gt;7 个服务员&lt;/strong&gt;在店里。如果 8 个人都被派去菜市场了呢？&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;餐厅里一个服务员都没有了。所有客人的请求全部卡住。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;这就是「阻塞调用污染调度器」&lt;/strong&gt;，也是本节最重要的一件事。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二协程本身的成本&#34;&gt;二、协程本身的成本&lt;/h2&gt;
&lt;h3 id=&#34;21-suspend-是怎么实现的&#34;&gt;2.1 &lt;code&gt;suspend&lt;/code&gt; 是怎么实现的&lt;/h3&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-kotlin&#34; data-lang=&#34;kotlin&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;suspend&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;fun&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;fetchUser&lt;/span&gt;(id: String): User {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;val&lt;/span&gt; cached = cache.&lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;(id)        &lt;span style=&#34;color:#75715e&#34;&gt;// 可能挂起
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; cached &lt;span style=&#34;color:#f92672&#34;&gt;?:&lt;/span&gt; api.&lt;span style=&#34;color:#66d9ef&#34;&gt;get&lt;/span&gt;(id)      &lt;span style=&#34;color:#75715e&#34;&gt;// 可能挂起
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;编译器会把上面这个函数改造成一个&lt;strong&gt;状态机&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>JMH 与 kotlinx-benchmark：标准化的基准工具</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/08-jmh-and-kotlinx-benchmark/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/08-jmh-and-kotlinx-benchmark/</guid>
      <description>&lt;h1 id=&#34;28-jmh-与-kotlinx-benchmark标准化的基准工具&#34;&gt;2.8 JMH 与 kotlinx-benchmark：标准化的基准工具&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/07-coroutines-cost-model/&#34;&gt;2.7 协程的真实开销模型&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/09-lab2/&#34;&gt;2.9 Lab 2&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/02-jvm-performance-basics/08-jmh-and-kotlinx-benchmark/&#34;&gt;02-jvm-performance-basics/08-jmh-and-kotlinx-benchmark&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一句话结论&#34;&gt;一句话结论&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;前面七节讲的每一个陷阱和坑，JMH 都替你处理好了。&lt;/strong&gt; 它不是一个「更好用的计时器」，而是一套&lt;strong&gt;防止你自己骗自己&lt;/strong&gt;的框架。代价是它的配置比你想象的啰嗦——但每一条配置都在防一个具体的错误。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一为什么必须用框架&#34;&gt;一、为什么必须用框架&lt;/h2&gt;
&lt;p&gt;回顾前七节，自己写基准要同时防住这些：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;风险&lt;/th&gt;
					&lt;th&gt;自己写要做什么&lt;/th&gt;
					&lt;th&gt;JMH 的做法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;死代码消除&lt;/td&gt;
					&lt;td&gt;手动消费结果&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;Blackhole.consume()&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;常量折叠&lt;/td&gt;
					&lt;td&gt;运行时生成输入&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@State&lt;/code&gt; + &lt;code&gt;@Setup&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;未消费结果&lt;/td&gt;
					&lt;td&gt;同上&lt;/td&gt;
					&lt;td&gt;同上&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;预热不足&lt;/td&gt;
					&lt;td&gt;手动预热并判断稳态&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@Warmup&lt;/code&gt; + &lt;code&gt;@Iterations&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;上一轮的 profile 污染下一轮&lt;/td&gt;
					&lt;td&gt;手动开新 JVM&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@Fork(N)&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;计时精度&lt;/td&gt;
					&lt;td&gt;手动 &lt;code&gt;nanoTime&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;@BenchmarkMode&lt;/code&gt; + 框架自己的计时循环&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;统计&lt;/td&gt;
					&lt;td&gt;手动算均值/分布&lt;/td&gt;
					&lt;td&gt;自动算 &lt;code&gt;Score ± Error&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;分配测量&lt;/td&gt;
					&lt;td&gt;手动监控&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;-prof gc&lt;/code&gt;，直接给 &lt;code&gt;gc.alloc.rate&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;一句话&lt;/strong&gt;：自己写，就是把这些坑重新踩一遍。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二jmh-注解速查&#34;&gt;二、JMH 注解速查&lt;/h2&gt;
&lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-kotlin&#34; data-lang=&#34;kotlin&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@State&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Scope&lt;/span&gt;.Benchmark)              &lt;span style=&#34;color:#75715e&#34;&gt;// 状态的作用域：Benchmark（共享）/ Thread（每线程）/ Group
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@BenchmarkMode&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Mode&lt;/span&gt;.Throughput)      &lt;span style=&#34;color:#75715e&#34;&gt;// 模式：Throughput / AverageTime / SampleTime / SingleShotTime
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@OutputTimeUnit&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;TimeUnit&lt;/span&gt;.MILLISECONDS)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Warmup&lt;/span&gt;(iterations = &lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, time = &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)    &lt;span style=&#34;color:#75715e&#34;&gt;// 预热 3 轮，每轮 1 秒
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Measurement&lt;/span&gt;(iterations = &lt;span style=&#34;color:#ae81ff&#34;&gt;5&lt;/span&gt;, time = &lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)  &lt;span style=&#34;color:#75715e&#34;&gt;// 测量 5 轮，每轮 1 秒
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Fork&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)                             &lt;span style=&#34;color:#75715e&#34;&gt;// 跑在 2 个独立 JVM 里
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#a6e22e&#34;&gt;@Threads&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;)                          &lt;span style=&#34;color:#75715e&#34;&gt;// 线程数
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;open&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;class&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;MyBenchmark&lt;/span&gt; {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;private&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;lateinit&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; data: IntArray
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Setup&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Level&lt;/span&gt;.Trial)              &lt;span style=&#34;color:#75715e&#34;&gt;// 每个 fork 前执行一次
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;fun&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;setup&lt;/span&gt;() {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;val&lt;/span&gt; rnd = Random(&lt;span style=&#34;color:#ae81ff&#34;&gt;42&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;data&lt;/span&gt; = IntArray(&lt;span style=&#34;color:#ae81ff&#34;&gt;4096&lt;/span&gt;) { rnd.nextInt() }    &lt;span style=&#34;color:#75715e&#34;&gt;// 运行时输入，防常量折叠
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@TearDown&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;Level&lt;/span&gt;.Trial)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;fun&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;tearDown&lt;/span&gt;() { &lt;span style=&#34;color:#75715e&#34;&gt;/* 清理 */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Benchmark&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;fun&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;myMethod&lt;/span&gt;(bh: Blackhole) {                  &lt;span style=&#34;color:#75715e&#34;&gt;// Blackhole 消费结果
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;var&lt;/span&gt; sum = &lt;span style=&#34;color:#ae81ff&#34;&gt;0L&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; (v &lt;span style=&#34;color:#66d9ef&#34;&gt;in&lt;/span&gt; &lt;span style=&#34;color:#66d9ef&#34;&gt;data&lt;/span&gt;) sum &lt;span style=&#34;color:#f92672&#34;&gt;+=&lt;/span&gt; v
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        bh.consume(sum)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Benchmark&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@OperationsPerInvocation&lt;/span&gt;(&lt;span style=&#34;color:#ae81ff&#34;&gt;1000&lt;/span&gt;)                 &lt;span style=&#34;color:#75715e&#34;&gt;// 一次调用做了 1000 次操作，自动摊薄
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;fun&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;bulkOperation&lt;/span&gt;(bh: Blackhole) { &lt;span style=&#34;color:#75715e&#34;&gt;/* ... */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@Benchmark&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#a6e22e&#34;&gt;@CompilerControl&lt;/span&gt;(&lt;span style=&#34;color:#a6e22e&#34;&gt;CompilerControl&lt;/span&gt;.&lt;span style=&#34;color:#a6e22e&#34;&gt;Mode&lt;/span&gt;.DONT_INLINE)   &lt;span style=&#34;color:#75715e&#34;&gt;// 诊断：禁止内联某方法
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;fun&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;diagnostic&lt;/span&gt;(bh: Blackhole) { &lt;span style=&#34;color:#75715e&#34;&gt;/* ... */&lt;/span&gt; }
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;四个最常被忽略的参数&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
    <item>
      <title>Lab 2：三份基准与一个坏基准</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/09-lab2/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/09-lab2/</guid>
      <description>&lt;h1 id=&#34;29-lab-2三份基准与一个坏基准&#34;&gt;2.9 Lab 2：三份基准与一个坏基准&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/02-jvm-performance-basics/08-jmh-and-kotlinx-benchmark/&#34;&gt;2.8 JMH 与 kotlinx-benchmark&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/03-test-pyramid/&#34;&gt;第 3 章 测试分层&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/02-jvm-performance-basics/09-lab2/&#34;&gt;02-jvm-performance-basics/09-lab2&lt;/a&gt;
预计时长：&lt;strong&gt;90 分钟&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一这个-lab-的目标&#34;&gt;一、这个 Lab 的目标&lt;/h2&gt;
&lt;p&gt;前八节讲了理论，这个 Lab 让你&lt;strong&gt;亲手把每一节的结论验证一遍&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;做完之后，你应该对「一个性能数字可不可信」有&lt;strong&gt;肌肉记忆&lt;/strong&gt;——而不是靠回忆规则清单。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二任务清单&#34;&gt;二、任务清单&lt;/h2&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th&gt;#&lt;/th&gt;
					&lt;th&gt;任务&lt;/th&gt;
					&lt;th&gt;产出&lt;/th&gt;
					&lt;th&gt;时长&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;1&lt;/td&gt;
					&lt;td&gt;同一个算法三种测法对比&lt;/td&gt;
					&lt;td&gt;三组数字 + 差异解释&lt;/td&gt;
					&lt;td&gt;30 min&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;2&lt;/td&gt;
					&lt;td&gt;故意写一个坏基准&lt;/td&gt;
					&lt;td&gt;荒谬数字 + 原因分析&lt;/td&gt;
					&lt;td&gt;15 min&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;3&lt;/td&gt;
					&lt;td&gt;测你自己的「并发成本表」&lt;/td&gt;
					&lt;td&gt;6 个操作的量级&lt;/td&gt;
					&lt;td&gt;25 min&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;4&lt;/td&gt;
					&lt;td&gt;调度器饥饿实验&lt;/td&gt;
					&lt;td&gt;观察到全局卡顿&lt;/td&gt;
					&lt;td&gt;20 min&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;5&lt;/td&gt;
					&lt;td&gt;建实验档案&lt;/td&gt;
					&lt;td&gt;&lt;code&gt;docs/experiments/E02-.../README.md&lt;/code&gt;&lt;/td&gt;
					&lt;td&gt;—&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三任务-1三份基准对比&#34;&gt;三、任务 1：三份基准对比&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;被测对象&lt;/strong&gt;：一个「有点计算量」的纯函数。建议用其中一个：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;对 &lt;code&gt;IntArray&lt;/code&gt; 求和 / 求最大值&lt;/li&gt;
&lt;li&gt;字符串拼接 1000 次&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Regex&lt;/code&gt; 匹配一段文本&lt;/li&gt;
&lt;li&gt;对一组数据做排序&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;三种测法&lt;/strong&gt;：&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
