<?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>导论 on Dorkytiger 的小屋</title>
    <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/</link>
    <description>Recent content in 导论 on Dorkytiger 的小屋</description>
    <generator>Hugo</generator>
    <language>en-us</language>
    <atom:link href="https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>三类问题必须分开处理</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/01-three-kinds-of-problems/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/01-three-kinds-of-problems/</guid>
      <description>&lt;h1 id=&#34;01-三类问题必须分开处理&#34;&gt;0.1 三类问题必须分开处理&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：无（这是全书的起点）　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/02-throughput-latency-cost/&#34;&gt;0.2 吞吐、延迟、成本的三方权衡&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/00-introduction/01-three-kinds-of-problems/&#34;&gt;00-introduction/01-three-kinds-of-problems&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;一从一个真实的对话开始&#34;&gt;一、从一个真实的对话开始&lt;/h2&gt;
&lt;blockquote&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;/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;/li&gt;
&lt;li&gt;你是想知道「它现在为什么慢、时间花在哪」吗？→ 那应该做&lt;strong&gt;性能剖析&lt;/strong&gt;。&lt;/li&gt;
&lt;/ul&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;微基准（Microbenchmark）&lt;/th&gt;
					&lt;th&gt;负载测试（Load Test）&lt;/th&gt;
					&lt;th&gt;性能剖析（Profiling）&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;这个系统能扛多少？&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;code&gt;copy()&lt;/code&gt; 和手动复制哪个快？」&lt;/td&gt;
					&lt;td&gt;「上线前它能撑住 1000 QPS 吗？」&lt;/td&gt;
					&lt;td&gt;「P99 从 80ms 涨到 400ms，卡在哪？」&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;/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;JMH、kotlinx-benchmark&lt;/td&gt;
					&lt;td&gt;k6、Gatling、wrk2&lt;/td&gt;
					&lt;td&gt;async-profiler、JFR、火焰图&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;code&gt;ns/op&lt;/code&gt; 或 &lt;code&gt;ops/s&lt;/code&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;/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;被 JIT 优化掉，测出假数据&lt;/td&gt;
					&lt;td&gt;压测机自己成为瓶颈&lt;/td&gt;
					&lt;td&gt;只看到 CPU 时间，漏掉等待&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&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;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;这个指标本身正常吗？&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;/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;拍 CT / 造影&lt;/strong&gt;&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;/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/00-introduction/02-throughput-latency-cost/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/02-throughput-latency-cost/</guid>
      <description>&lt;h1 id=&#34;02-吞吐延迟成本的三方权衡&#34;&gt;0.2 吞吐、延迟、成本的三方权衡&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/01-three-kinds-of-problems/&#34;&gt;0.1 三类问题必须分开处理&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/03-latency-is-a-distribution/&#34;&gt;0.3 延迟是分布，不是数字&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;一用一个快递公司的故事理解三者关系&#34;&gt;一、用一个快递公司的故事理解三者关系&lt;/h2&gt;
&lt;p&gt;假设你开了一家快递站，每天要送 1000 个包裹：&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;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;&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;strong&gt;攒够 100 件送一趟&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;&lt;strong&gt;高&lt;/strong&gt;（10 趟送完）&lt;/td&gt;
					&lt;td&gt;高（第一个包裹要等到第 100 个才出发）&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;p&gt;再看第三个维度：那辆车的载重、司机的数量、仓库的大小——这就是&lt;strong&gt;资源成本&lt;/strong&gt;。想同时提高吞吐、降低延迟，唯一办法是&lt;strong&gt;加钱&lt;/strong&gt;（更多车辆、更多司机）。&lt;/p&gt;
&lt;p&gt;性能优化就是这么回事：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;性能优化的本质，是在给定的成本预算下，重新分配「吞吐—延迟」这一对矛盾。&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;所以当有人说「我要性能更好」时，你必须追问：&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;「你能接受多大成本？你更在意「每秒处理更多」还是「每个请求更快」？」&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二三个指标的通俗定义与精确含义&#34;&gt;二、三个指标的通俗定义与精确含义&lt;/h2&gt;
&lt;h3 id=&#34;21-吞吐量throughput&#34;&gt;2.1 吞吐量（Throughput）&lt;/h3&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;：每秒完成的请求数（req/s、QPS、RPS）。注意分母是「&lt;strong&gt;成功完成&lt;/strong&gt;」的请求数——返回 500 的错误请求不该算进吞吐。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;22-延迟latency--response-time&#34;&gt;2.2 延迟（Latency / Response Time）&lt;/h3&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;——客户端观测、网关观测、服务端 handler 内部，三者数值不同（第 1 章会展开）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;23-资源成本cost--resource&#34;&gt;2.3 资源成本（Cost / Resource）&lt;/h3&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;：CPU 核数、内存、连接数、副本数、以及这些折算成的账单。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;记住&lt;/strong&gt;：延迟和吞吐是「结果」，成本是「代价」。只谈前两个而不谈代价的性能报告，是不完整的。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三一个具体的数字例子批量-vs-单条&#34;&gt;三、一个具体的数字例子：批量 vs 单条&lt;/h2&gt;
&lt;p&gt;假设你在写一个批量导入功能，要往数据库插 1000 行。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;方案 A：逐条插入&lt;/strong&gt;&lt;/p&gt;</description>
    </item>
    <item>
      <title>延迟是分布，不是数字</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/03-latency-is-a-distribution/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/03-latency-is-a-distribution/</guid>
      <description>&lt;h1 id=&#34;03-延迟是分布不是数字&#34;&gt;0.3 延迟是分布，不是数字&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/02-throughput-latency-cost/&#34;&gt;0.2 吞吐、延迟、成本的三方权衡&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/04-littles-law/&#34;&gt;0.4 用 Little&amp;rsquo;s Law 估算容量&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/00-introduction/03-latency-distribution/&#34;&gt;00-introduction/03-latency-distribution&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;「这个接口的平均延迟是 11 ms」这句话，可能同时意味着「99% 的人只等了 1 ms」和「1% 的人等了整整 1 秒」。&lt;/strong&gt; 后端必须看延迟的&lt;strong&gt;分布&lt;/strong&gt;，尤其是尾部。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一一个-100-个请求的数字游戏&#34;&gt;一、一个 100 个请求的数字游戏&lt;/h2&gt;
&lt;p&gt;假设你的接口收到了 100 个请求：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;99 个&lt;/strong&gt;请求很快，各花了 &lt;strong&gt;1 ms&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;1 个&lt;/strong&gt;请求很慢，花了 &lt;strong&gt;1000 ms&lt;/strong&gt;（比如遇到了 GC 停顿或数据库锁等待）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&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;总耗时 = 99 × 1 ms + 1 × 1000 ms = 1099 ms
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;平均延迟 = 1099 / 100 ≈ 11 ms
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;报出去的数是 11 ms。听起来很棒，对吧？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;但对那 1 个用户来说，他结结实实地等了 1 秒。如果这个接口一天被调用 100 万次，那就是&lt;strong&gt;一万个用户&lt;/strong&gt;每天遇到「卡了一秒」。&lt;/p&gt;</description>
    </item>
    <item>
      <title>用 Little&#39;s Law 估算容量</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/04-littles-law/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/04-littles-law/</guid>
      <description>&lt;h1 id=&#34;04-用-littles-law-估算容量&#34;&gt;0.4 用 Little&amp;rsquo;s Law 估算容量&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/03-latency-is-a-distribution/&#34;&gt;0.3 延迟是分布，不是数字&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/05-utilization-and-queueing/&#34;&gt;0.5 为什么 90% 利用率已经很危险&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/00-introduction/04-littles-law/&#34;&gt;00-introduction/04-littles-law&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;ul&gt;
&lt;li&gt;平均&lt;strong&gt;每分钟进来 5 个客人&lt;/strong&gt;（到达率 λ）；&lt;/li&gt;
&lt;li&gt;每个客人平均&lt;strong&gt;坐 20 分钟&lt;/strong&gt;（停留时间 W）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&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;5 人/分钟 × 20 分钟 = 100 人
&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;这个「显然」的结论，就是 Little&amp;rsquo;s Law（利特尔法则）。它之所以重要，是因为把「客人」换成「请求」、把「座位」换成「连接」，它就变成了容量估算公式。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二公式与每个符号的人话含义&#34;&gt;二、公式与每个符号的人话含义&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-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;L = λ × W
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&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;strong&gt;L&lt;/strong&gt;&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;&lt;strong&gt;λ&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;到达率&lt;/td&gt;
					&lt;td&gt;每秒有多少个请求进来&lt;/td&gt;
					&lt;td&gt;req/s&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;W&lt;/strong&gt;&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;/tbody&gt;
&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;单位换算的直觉&lt;/strong&gt;：&lt;code&gt;req/s × s = 个&lt;/code&gt;——秒被约掉了，剩下的就是「同时有几个」。&lt;/p&gt;</description>
    </item>
    <item>
      <title>为什么 90% 利用率已经很危险</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/05-utilization-and-queueing/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/05-utilization-and-queueing/</guid>
      <description>&lt;h1 id=&#34;05-为什么-90-利用率已经很危险&#34;&gt;0.5 为什么 90% 利用率已经很危险&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/04-littles-law/&#34;&gt;0.4 用 Little&amp;rsquo;s Law 估算容量&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/06-coordinated-omission/&#34;&gt;0.6 协调遗漏：压测报告的最大谎言&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/00-introduction/05-queueing/&#34;&gt;00-introduction/05-queueing&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; CPU 用到 50% 时，排队几乎不存在；用到 90% 时，平均排队时间已经是服务时间的 &lt;strong&gt;9 倍&lt;/strong&gt;。这就是容量规划必须留 30%–50% 余量的数学依据，而不是「保险起见」。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一先用超市收银台建立直觉&#34;&gt;一、先用超市收银台建立直觉&lt;/h2&gt;
&lt;p&gt;想象一家超市只有&lt;strong&gt;一个收银台&lt;/strong&gt;（一个 CPU 核 / 一个连接）：&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;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;50% 忙&lt;/strong&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;80% 忙&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;有时要等一下，因为前面可能正好有人&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;90% 忙&lt;/strong&gt;&lt;/td&gt;
					&lt;td&gt;大概率要排队，而且队伍不短&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;&lt;strong&gt;95% 忙&lt;/strong&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;99% 忙&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;：收银员只从 50% 忙到 90% 忙，「空闲时间」只从 50% 降到 10%（少了 5 倍），但你的等待时间却涨了&lt;strong&gt;远不止 5 倍&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;为什么？因为&lt;strong&gt;空闲时间是「吸收波动」的缓冲&lt;/strong&gt;。当收银员有 50% 空闲时，偶尔来一波客人马上就能消化掉；当只剩 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;在「单队列、单服务、到达时间随机、服务时间随机」这个简化模型下（学术上叫 M/M/1），平均排队等待时间的近似公式是：&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;平均等待时间 ≈ (ρ / (1 - ρ)) × 服务时间
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;其中 &lt;strong&gt;ρ（rho）就是利用率&lt;/strong&gt;，取值 0 到 1。&lt;/p&gt;</description>
    </item>
    <item>
      <title>协调遗漏：压测报告的最大谎言</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/06-coordinated-omission/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/06-coordinated-omission/</guid>
      <description>&lt;h1 id=&#34;06-协调遗漏压测报告的最大谎言&#34;&gt;0.6 协调遗漏：压测报告的最大谎言&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/05-utilization-and-queueing/&#34;&gt;0.5 为什么 90% 利用率已经很危险&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/07-common-fallacies/&#34;&gt;0.7 九条常见谬误自查清单&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/00-introduction/06-coordinated-omission/&#34;&gt;00-introduction/06-coordinated-omission&lt;/a&gt;
&lt;strong&gt;这是本章最重要的一节。&lt;/strong&gt; 它解释了一个现象：为什么你的压测 P99 很漂亮，上线后用户却在抱怨卡顿。&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;，于是这次卡顿只被记录成 1 个慢样本、而不是几百个。尾延迟被系统性地藏起来了。这个现象叫&lt;strong&gt;协调遗漏（Coordinated Omission）&lt;/strong&gt;。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一用一个故事理解它&#34;&gt;一、用一个故事理解它&lt;/h2&gt;
&lt;p&gt;假设你开了一个办事窗口，外面有 &lt;strong&gt;100 个客户&lt;/strong&gt;排队办事。&lt;/p&gt;
&lt;p&gt;平时每个人办 1 秒。突然，第 50 号客户遇到一个复杂情况，窗口&lt;strong&gt;卡了整整 1 秒&lt;/strong&gt;（比如系统查不动了）。&lt;/p&gt;
&lt;h3 id=&#34;真实世界会怎样客户自己来&#34;&gt;真实世界会怎样（客户自己来）&lt;/h3&gt;
&lt;p&gt;100 个客户是&lt;strong&gt;按自己的节奏来的&lt;/strong&gt;。窗口卡住的那 1 秒里，&lt;strong&gt;外面又来了 100 个新客户&lt;/strong&gt;（假设每秒来 100 个）。&lt;/p&gt;
&lt;p&gt;所以这次卡顿的后果是：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;100 个在排队的客户&lt;/strong&gt;，每人多等 1 秒；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;100 个新来的客户&lt;/strong&gt;，一进门就要等；&lt;/li&gt;
&lt;li&gt;总共 &lt;strong&gt;200 个客户&lt;/strong&gt;的体验被拖慢了 1 秒。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;用轮到我才去的方式会怎样典型压测工具&#34;&gt;用「轮到我才去」的方式会怎样（典型压测工具）&lt;/h3&gt;
&lt;p&gt;现在换一种组织方式：&lt;strong&gt;每次只派 1 个客户去窗口，他办完回来，再派下一个。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;窗口卡住的那 1 秒里，外面&lt;strong&gt;没有任何新客户进来&lt;/strong&gt;——因为那唯一一个客户还在窗口前等着呢。&lt;/p&gt;
&lt;p&gt;于是统计结果变成：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;这 1 秒的卡顿，只影响了 &lt;strong&gt;1 个客户&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;报告里写：「100 个请求中有 1 个慢了 1 秒」，P99 &lt;strong&gt;看起来还凑合&lt;/strong&gt;；&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;但那 200 个在真实世界里被拖慢的客户，根本没有出现在统计里。&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;这就是协调遗漏&lt;/strong&gt;：压测工具的「节奏」与服务端的「卡顿」被&lt;strong&gt;协调&lt;/strong&gt;在了一起，卡顿越大，工具发得越慢，慢样本越少，尾延迟被低估得越严重。&lt;/p&gt;</description>
    </item>
    <item>
      <title>九条常见谬误自查清单</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/07-common-fallacies/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/07-common-fallacies/</guid>
      <description>&lt;h1 id=&#34;07-九条常见谬误自查清单&#34;&gt;0.7 九条常见谬误自查清单&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/06-coordinated-omission/&#34;&gt;0.6 协调遗漏：压测报告的最大谎言&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/08-performance-loop/&#34;&gt;0.8 性能工程闭环&lt;/a&gt;
用法：拿你现在项目里的做法，逐条对照。&lt;strong&gt;踩了 3 条以上，说明你现在的性能数据基本不可用。&lt;/strong&gt;&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;ul&gt;
&lt;li&gt;&lt;strong&gt;表现&lt;/strong&gt;：汇报「平均延迟 12 ms，性能良好」。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为什么错&lt;/strong&gt;：均值会把 1% 用户的糟糕体验摊薄掉（见 &lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/03-latency-is-a-distribution/&#34;&gt;0.3&lt;/a&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改成&lt;/strong&gt;：至少同时报 P50 / P95 / P99 + 错误率 + 样本量。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-用闭环vu模型测尾延迟&#34;&gt;❷ 用闭环（VU）模型测尾延迟&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表现&lt;/strong&gt;：「用 100 个并发用户压了 5 分钟」。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为什么错&lt;/strong&gt;：协调遗漏会系统性低估尾延迟，服务越糟报告越好看（见 &lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/06-coordinated-omission/&#34;&gt;0.6&lt;/a&gt;）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改成&lt;/strong&gt;：用恒定到达率（开环）模型；或至少交叉验证实际请求数是否等于期望值。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-压测期间不观测服务端&#34;&gt;❸ 压测期间不观测服务端&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表现&lt;/strong&gt;：只盯着压测工具输出的 QPS 和延迟，服务端除了一个 &lt;code&gt;top&lt;/code&gt; 什么都没看。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为什么错&lt;/strong&gt;：你只知道「慢了」，不知道「为什么慢」，无法定位也无法优化。压测的价值有 80% 在服务端数据里。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改成&lt;/strong&gt;：压测同时采集四层指标（业务/延迟/资源/饱和度），并保存火焰图与 GC 日志。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-改完代码不回归压测&#34;&gt;❹ 改完代码不回归压测&lt;/h3&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;：没有 before/after 对比，优化收益无法证明，甚至可能引入了退化。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改成&lt;/strong&gt;：同一脚本、同一环境、≥3 轮对比，并用统计方法判断差异是否显著（第 5 章）。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-把一次运行的结果当结论&#34;&gt;❺ 把一次运行的结果当结论&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表现&lt;/strong&gt;：「跑了一次，P99 是 95 ms，达标。」&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为什么错&lt;/strong&gt;：你自己的环境噪声可能有 ±8%，单次运行的数字毫无意义。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改成&lt;/strong&gt;：先量化噪声底线（跑 5 次相同配置），再做结论；报告里给出每轮的值。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-忽略预热jvm-特有&#34;&gt;❻ 忽略预热（JVM 特有）&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表现&lt;/strong&gt;：服务启动后立刻压测，把前 30 秒的数据算进 P99。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为什么错&lt;/strong&gt;：那时代码还在解释执行 / C1 阶段、连接池还是空的、数据库缓存还没热，数据不能代表稳态。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改成&lt;/strong&gt;：预热阶段独立于测量阶段，且预热数据不计入统计。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-数据集与线上不一致&#34;&gt;❼ 数据集与线上不一致&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表现&lt;/strong&gt;：用 1 万行数据压测，线上是 1 亿行。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为什么错&lt;/strong&gt;：数据量决定执行计划——1 万行可能全表扫描很快，1 亿行则会走索引。结论完全不可迁移。同样，&lt;strong&gt;均匀分布&lt;/strong&gt;的数据会让缓存命中率虚高、锁竞争虚低。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改成&lt;/strong&gt;：数据量与线上同数量级，访问分布用**幂律（齐夫）**模拟真实热点。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-压测客户端与服务同机&#34;&gt;❽ 压测客户端与服务同机&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表现&lt;/strong&gt;：在同一台机器上跑服务和 k6。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为什么错&lt;/strong&gt;：压测工具自己也要吃 CPU、内存、网络，和服务互相抢资源，两边数据都失真。而且会掩盖网络延迟。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;改成&lt;/strong&gt;：压测客户端独立部署；至少做到绑核隔离。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;-只看性能不看错误率&#34;&gt;❾ 只看性能不看错误率&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;表现&lt;/strong&gt;：「QPS 从 3000 提升到 5000，优化成功！」——同时错误率从 0.1% 涨到 4%。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;为什么错&lt;/strong&gt;：这不是性能提升，是把「慢」换成了「错」。返回 500 的请求不计入成功延迟统计，所以延迟数字反而「变好」了。&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;hr&gt;
&lt;h2 id=&#34;二这些谬误会互相掩盖&#34;&gt;二、这些谬误会互相掩盖&lt;/h2&gt;
&lt;p&gt;单独看每一条都不致命，但它们组合起来会形成「看起来很专业、实际上毫无信息量」的报告：&lt;/p&gt;</description>
    </item>
    <item>
      <title>性能工程闭环</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/08-performance-loop/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/08-performance-loop/</guid>
      <description>&lt;h1 id=&#34;08-性能工程闭环&#34;&gt;0.8 性能工程闭环&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/07-common-fallacies/&#34;&gt;0.7 九条常见谬误自查清单&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/09-cpp-to-jvm-intuitions/&#34;&gt;0.9 从 C++ 到 JVM：哪些直觉必须修正&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;一八步闭环&#34;&gt;一、八步闭环&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-text&#34; data-lang=&#34;text&#34;&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;① 定义目标(SLO)                                           │
&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;② 建立模型（Little&amp;#39;s Law / 队列估算）                       │
&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&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;④ 剖析归因（火焰图 / JFR / 线程快照）                         │
&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&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&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;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;为什么是循环而不是流程&lt;/strong&gt;：第 ⑧ 步的监控数据会成为下一轮第 ① 步「目标是否需要调整」的输入；第 ⑦ 步的回归结果会成为下一轮第 ③ 步的基线。性能工作没有终点，只有「当前这一轮的稳定态」。&lt;/p&gt;</description>
    </item>
    <item>
      <title>从 C&#43;&#43; 到 JVM：哪些直觉必须修正</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/09-cpp-to-jvm-intuitions/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/09-cpp-to-jvm-intuitions/</guid>
      <description>&lt;h1 id=&#34;09-从-c-到-jvm哪些直觉必须修正&#34;&gt;0.9 从 C++ 到 JVM：哪些直觉必须修正&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/08-performance-loop/&#34;&gt;0.8 性能工程闭环&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/10-lab0/&#34;&gt;0.10 Lab 0：先被骗一次&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一句话结论&#34;&gt;一句话结论&lt;/h2&gt;
&lt;p&gt;你从 C++ 并发里练出来的直觉&lt;strong&gt;大部分仍然有效&lt;/strong&gt;，但有 &lt;strong&gt;6 条&lt;/strong&gt;在 JVM 上会给出错误结论。这一节逐条说清：哪些要保留、哪些要推翻、为什么。&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;在 JVM 上是否成立&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;CPU 缓存层级没变，缓存未命中依然昂贵&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td&gt;伪共享（false sharing）会拖慢性能&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;且 JVM 上更容易被 GC 与安全点放大&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;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/02-throughput-latency-cost/&#34;&gt;0.2&lt;/a&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;而且比 C++ 更重要（因为 JVM 更难预测）&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;所以不要把这一节理解成「C++ 经验没用」&lt;/strong&gt;——恰恰相反，你的优势在于「知道到底在发生什么」，这是很多只会调参的人不具备的。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;二必须修正的-6-条直觉&#34;&gt;二、必须修正的 6 条直觉&lt;/h2&gt;
&lt;h3 id=&#34;-代码写出来性能就定了&#34;&gt;❶ 「代码写出来，性能就定了」&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;C++ 的现实&lt;/strong&gt;：编译产物固定，同一段代码第一次调用和第一万次调用性能基本一致。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;JVM 的现实&lt;/strong&gt;：代码要经历 &lt;strong&gt;解释执行 → C1 编译 → C2 编译&lt;/strong&gt; 三个阶段，性能随调用次数变化。C2 还会根据运行时的&lt;strong&gt;类型分布&lt;/strong&gt;做激进优化（比如把虚调用变成直接调用），一旦类型变了就**去优化（deoptimization）**退回重来。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;strong&gt;后果&lt;/strong&gt;：不预热的测量毫无意义；一次压测的前 30 秒和后 5 分钟是两个不同的系统。&lt;/p&gt;</description>
    </item>
    <item>
      <title>Lab 0：先被骗一次</title>
      <link>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/10-lab0/</link>
      <pubDate>Mon, 01 Jan 0001 00:00:00 +0000</pubDate>
      <guid>https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/10-lab0/</guid>
      <description>&lt;h1 id=&#34;010-lab-0先被骗一次&#34;&gt;0.10 Lab 0：先被骗一次&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;上一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/00-introduction/09-cpp-to-jvm-intuitions/&#34;&gt;0.9 从 C++ 到 JVM：哪些直觉必须修正&lt;/a&gt;　|　下一节：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/01-metrics-and-slo/&#34;&gt;第 1 章 指标与目标&lt;/a&gt;
配套代码：&lt;a href=&#34;https://www.dorkytiger.top/kotlin/kotlin-benchmark/code/00-introduction/10-lab0/&#34;&gt;00-introduction/10-lab0&lt;/a&gt;
预计时长：&lt;strong&gt;60 分钟&lt;/strong&gt;（编码 20 分钟 + 观察与记录 40 分钟）&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;一这个-lab-想让你获得什么&#34;&gt;一、这个 Lab 想让你获得什么&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;不是&lt;/strong&gt;学会写基准测试——那是第 2 章的事。&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;阶段-1制造一个荒谬的数字不消费返回值&#34;&gt;阶段 1：制造一个荒谬的数字（不消费返回值）&lt;/h3&gt;
&lt;p&gt;写一个「看起来有计算量」的函数，例如：对一个 4096 个元素的 &lt;code&gt;IntArray&lt;/code&gt; 求和。&lt;/p&gt;
&lt;p&gt;然后用 &lt;code&gt;System.nanoTime()&lt;/code&gt; 包住一个循环调用它，打印每次迭代的平均耗时。&lt;strong&gt;故意不做任何防护&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不预热；&lt;/li&gt;
&lt;li&gt;不消费返回值；&lt;/li&gt;
&lt;li&gt;输入数组用规律数据（&lt;code&gt;IntArray(4096) { it }&lt;/code&gt;）。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;跑三次，观察数字。&lt;/p&gt;
&lt;h3 id=&#34;阶段-2加上防护看数字怎么变&#34;&gt;阶段 2：加上防护，看数字怎么变&lt;/h3&gt;
&lt;p&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;/li&gt;
&lt;/ol&gt;
&lt;p&gt;再跑 20 轮，观察数字如何从「几百纳秒」逐步下降到稳定值。&lt;/p&gt;
&lt;h3 id=&#34;阶段-3观察预热曲线&#34;&gt;阶段 3：观察预热曲线&lt;/h3&gt;
&lt;p&gt;每 2000 次迭代采样一次耗时，打印 60 个采样点，画出（或观察）曲线，找到进入平台的时刻。&lt;/p&gt;
&lt;hr&gt;
&lt;h2 id=&#34;三你应该观察到什么预期现象&#34;&gt;三、你应该观察到什么（预期现象）&lt;/h2&gt;
&lt;blockquote&gt;
&lt;p&gt;⚠️ 你的具体数字会和我这里不同（取决于 CPU、JDK 版本、是否向量化），但&lt;strong&gt;现象的形态&lt;/strong&gt;应该一致。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;阶段-1-的预期荒谬&#34;&gt;阶段 1 的预期：荒谬&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-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;round 0: 131.74 ns/op     ← 第一次很慢：类加载 + 解释执行
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;round 1:   0.21 ns/op     ← 荒谬！比一次内存访问还快
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;round 2:   0.19 ns/op     ← 依然荒谬
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;为什么荒谬&lt;/strong&gt;：在现代 CPU 上，一次内存访问大约 1 纳秒，一次加法大约 0.3 纳秒。&lt;strong&gt;4096 次加法不可能在 0.19 纳秒内完成&lt;/strong&gt;——这个数字在物理上不可能。&lt;/p&gt;</description>
    </item>
  </channel>
</rss>
