<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>FP32 on 搬砖程序员带你飞</title>
        <link>https://lpflpf.cn/tags/fp32/</link>
        <description>Recent content in FP32 on 搬砖程序员带你飞</description>
        <generator>Hugo -- gohugo.io</generator>
        <language>zh-CN</language>
        <managingEditor>lipengfei634626165@163.com (lpflpf)</managingEditor>
        <webMaster>lipengfei634626165@163.com (lpflpf)</webMaster>
        <lastBuildDate>Mon, 26 Oct 2026 07:30:00 +0800</lastBuildDate><atom:link href="https://lpflpf.cn/tags/fp32/index.xml" rel="self" type="application/rss+xml" /><item>
            <title>同一个 if，有人写 2 个操作，有人写 15 个：差别不在算法，在你顺手写下的那行声明</title>
            <link>https://lpflpf.cn/posts/handwired-compare-cost-2026-10-26/</link>
            <pubDate>Mon, 26 Oct 2026 07:30:00 +0800</pubDate><author>lipengfei634626165@163.com (lpflpf)</author>
            <guid>https://lpflpf.cn/posts/handwired-compare-cost-2026-10-26/</guid>
            <description>&lt;img src=&#34;https://cdn.lpflpf.cn/covers/handwired-compare-cost-2026-10-26-v1-plain.png&#34; alt=&#34;Featured image of post 同一个 if，有人写 2 个操作，有人写 15 个：差别不在算法，在你顺手写下的那行声明&#34; /&gt;&lt;p&gt;你写完一段 JavaScript 判断，跑得挺好。有人跟你说，把它换成神经网络，结果逐位一样，还能求导。&lt;/p&gt;&#xA;&lt;p&gt;你点开仓库，标题写着 Turn your regular JavaScript code into a neural net。&lt;/p&gt;&#xA;&lt;p&gt;往下滚两屏，文档第一句是：No tool converts existing JavaScript source into a program。&lt;/p&gt;&#xA;&lt;p&gt;没有工具能把现有 JS 源码转成程序。它不做翻译，你得自己用它的原语，把那段判断手写一遍。&lt;/p&gt;&#xA;&lt;p&gt;真动手之后最疼的那件事在这儿：&lt;strong&gt;同一个 &lt;code&gt;if&lt;/code&gt;，为什么有时候只花 2 个操作，有时候要花 15 个，而且它不报错，只是变慢。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;h2 id=&#34;同一个比较成本差-7-倍而结果一位不差&#34;&gt;同一个比较，成本差 7 倍，而结果一位不差&#xA;&lt;/h2&gt;&lt;p&gt;先把最容易误解的地方钉住。它不编译你的代码，它给你四个原语：&lt;code&gt;affine&lt;/code&gt; 做加权和，&lt;code&gt;reglu&lt;/code&gt; 做 &lt;code&gt;left×max(0,gate)&lt;/code&gt;，&lt;code&gt;boolean&lt;/code&gt; 把分数舍成 0/1，&lt;code&gt;indexRead&lt;/code&gt; 读表。&lt;/p&gt;&#xA;&lt;p&gt;你用普通 JS 调这些原语，跑一次，生成一个 &lt;code&gt;Circuit&lt;/code&gt;。执行的时候，就是读一个 &lt;code&gt;Float32Array&lt;/code&gt;，算加乘和 ReLU。&lt;/p&gt;&#xA;&lt;p&gt;一个 &lt;code&gt;(a &amp;gt; 4) ? 10 : 12&lt;/code&gt; 进去，出来是 &lt;code&gt;max(0,a-4) - max(0,a-5)&lt;/code&gt;，两个分支都算，再拿一个 bit 选。&lt;/p&gt;&#xA;&lt;p&gt;选中那条分支本身也要钱。条件在 0..1 之间时，&lt;code&gt;emitSelect&lt;/code&gt; 每个数 4 个操作、3 层。&lt;/p&gt;&#xA;&lt;p&gt;真正的账在那个 bit 上。同一个 &lt;code&gt;a &amp;gt;= b&lt;/code&gt;，&lt;code&gt;emitCompare&lt;/code&gt; 会按你给的声明自动挑实现，挑到哪条，代价差一大截。&lt;/p&gt;&#xA;&lt;p&gt;表里最便宜的一行是整数 bit：两个操作数都声明在同一个 grid 上，每个部分和不超过 2^24 个 grid 步长，&lt;code&gt;&amp;gt;=&lt;/code&gt; 就是 2 个操作、2 层。&lt;code&gt;===&lt;/code&gt; 3 个操作。&lt;/p&gt;&#xA;&lt;p&gt;没有 grid，退到符号级联。它把差值拆符号位，逐级乘 2 的幂再 clamp，直到任何非零差值（低到 2^-149）都被抬到至少 1。0 到 16,384 这个范围里，&lt;code&gt;&amp;gt;=&lt;/code&gt; 是 15 个操作、14 层，&lt;code&gt;===&lt;/code&gt; 28 个。&lt;/p&gt;&#xA;&lt;p&gt;&lt;img alt=&#34;同一个 FP32 比较的三条实现路径与代价。整数 bit 是 2 个操作 2 层，符号级联要 15 个操作 14 层，boolean atom 只要 1 个操作但依赖保留 FP32 求值。数据来自 docs/expressions.md 与 docs/execution-model.md 的成本表。&#34; class=&#34;gallery-image&#34; data-flex-basis=&#34;381px&#34; data-flex-grow=&#34;159&#34; height=&#34;1342&#34; loading=&#34;lazy&#34; sizes=&#34;(max-width: 767px) calc(100vw - 30px), (max-width: 1023px) 700px, (max-width: 1279px) 950px, 1232px&#34; src=&#34;https://cdn.lpflpf.cn/images/posts/handwired-compare-cost-2026-10-26.png&#34; srcset=&#34;https://lpflpf.cn/handwired-compare-cost-2026-10-26_13250191900545001156_hu_e5f4a7d6900d9763.png 800w, https://lpflpf.cn/handwired-compare-cost-2026-10-26_13250191900545001156_hu_fb45ec29373bcebb.png 1600w, https://cdn.lpflpf.cn/images/posts/handwired-compare-cost-2026-10-26.png 2136w&#34; width=&#34;2136&#34;&gt;&#xA;&lt;/p&gt;&#xA;&lt;p&gt;还有一条更便宜的：boolean atom。&lt;code&gt;(x + 8,388,609) - 8,388,609&lt;/code&gt;，一个操作就把 0..1 的分数在 0.5 处舍成 0/1。&lt;/p&gt;&#xA;&lt;p&gt;但它只在执行器保留 FP32 存储求值时成立。哪天编译器把这个加常数再减常数化简回 &lt;code&gt;x&lt;/code&gt;，bit 就错了。&lt;/p&gt;&#xA;&lt;p&gt;两条路返回的位完全一样。所以这类问题不看结果，看清单——&lt;strong&gt;同一个 &lt;code&gt;if&lt;/code&gt; 花 2 个操作还是 15 个，取决于你在 &lt;code&gt;assumeGrid&lt;/code&gt; 里声明了什么。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;建完之后调 &lt;code&gt;circuit.roundingPoints()&lt;/code&gt;，按 kind 数一遍 &lt;code&gt;&#39;integer bit&#39;&lt;/code&gt; 和 &lt;code&gt;&#39;sign cascade&#39;&lt;/code&gt; 各有几条，才知道自己站在哪一边。&lt;/p&gt;&#xA;&lt;p&gt;作者自己在 HandwiredOS 的窗口命中测试里踩过这个。每个窗口四条边，声明都在 grid 上时每条边 2 个操作。&lt;/p&gt;&#xA;&lt;p&gt;坐标范围一放大到 ±65,536 px，标题栏右边缘的距离就超过 2^24 个 grid 步长。那一条自动变成符号级联，每个窗口多 13 个操作。geometry 完全不给 grid 时，每条边多 13 到 14 个。&lt;/p&gt;&#xA;&lt;p&gt;而 dock 项的宽度是除以 3 得来的，落在任何 grid 之外，只能改用 boolean atom。&lt;/p&gt;&#xA;&lt;p&gt;到这里就能看出这套东西卖的到底是什么了。它卖的是&lt;strong&gt;结果和你那份 JS 引用实现逐位相同&lt;/strong&gt;，不是省下来的开发时间。为了这个，它把每一处浮点判定都摊开来给你数。&lt;/p&gt;&#xA;&lt;h2 id=&#34;声明是你签的字它不会替你验&#34;&gt;声明是你签的字，它不会替你验&#xA;&lt;/h2&gt;&lt;p&gt;那次命中测试里最值得抄走的不是数字，是那句边界：&lt;code&gt;assumeGrid&lt;/code&gt;、&lt;code&gt;assumeRange&lt;/code&gt; 是调用方的承诺，不是证明。&lt;/p&gt;&#xA;&lt;p&gt;builder 信你。你写了个假 grid，整数 bit 就变回小数，而且程序一句警告都不给。&lt;/p&gt;&#xA;&lt;p&gt;它连梯度都按你说的话走。整数 bit 只在差值落在 -1 到 1 之间时给梯度，也就是 -1/grid 这么大。文档的原话是，这种情况只在你那条 grid 声明是假的时候才会发生。&lt;/p&gt;&#xA;&lt;p&gt;所以这条链上有三道各自的闸，缺一道都不行。&lt;/p&gt;&#xA;&lt;ul&gt;&#xA;&lt;li&gt;声明阶段：&lt;code&gt;assumeGrid&lt;/code&gt; / &lt;code&gt;assumeRange&lt;/code&gt; 写下去。&lt;/li&gt;&#xA;&lt;li&gt;构建阶段：&lt;code&gt;validateGrids&lt;/code&gt;、&lt;code&gt;validateIndexReads&lt;/code&gt; 走一遍。&lt;/li&gt;&#xA;&lt;li&gt;修完之后：&lt;code&gt;roundingPoints()&lt;/code&gt; 数 kind。&lt;/li&gt;&#xA;&lt;/ul&gt;&#xA;&lt;p&gt;第二道闸尤其别跳。README 里有个特别好用的反例。&lt;/p&gt;&#xA;&lt;p&gt;一段从 &lt;code&gt;circuit.constant(0)&lt;/code&gt; 开始累加的写法，因为常量 0 不强加 grid，把它拿去比较就会吃到 15 个操作的符号级联。把累加改从第一项起，同样的比较变回 2 个操作的整数 bit。&lt;/p&gt;&#xA;&lt;p&gt;代码看着几乎一样，成本差七倍。从常量 0 起头这个习惯，很多人生来就有。&lt;/p&gt;&#xA;&lt;p&gt;这套设计里最值钱的不是那四个原语，是这三步连在一起：声明，量代价，再用验证函数兜住声明。缺了最后一步，前面写的东西就是一份没人签字的合同。&lt;/p&gt;&#xA;&lt;h2 id=&#34;把循环改成树以后cpu-上反而更慢&#34;&gt;把循环改成树以后，CPU 上反而更慢&#xA;&lt;/h2&gt;&lt;p&gt;现在说那个最反直觉的地方。&lt;/p&gt;&#xA;&lt;p&gt;神经网络没法提前退出。一个 &lt;code&gt;findIndex&lt;/code&gt; 在普通 JS 里找到第一个命中就 &lt;code&gt;break&lt;/code&gt;，在这里必须一次算完全部候选。&lt;/p&gt;&#xA;&lt;p&gt;教科书给的解法是把循环改成树，log n 层，看起来漂亮。作者把这套改写试了一遍。结论是看情况，而且常常跟直觉相反。&lt;/p&gt;&#xA;&lt;p&gt;他给的例子是文本流里的行底判定，条件沿项目单调翻转。计数法 3n+3 个操作、3 层。64 行时是 195 个操作。用 &lt;code&gt;emitFirstActive&lt;/code&gt; 建树，447 个操作、34 层。&lt;/p&gt;&#xA;&lt;p&gt;单调的时候，计数版更快。&lt;/p&gt;&#xA;&lt;p&gt;换成任意分布，比如重叠窗口的命中，结论倒过来：树 316 个操作、31 层，链 127 个操作、127 层。作者在 patterns.md 的开头把这条讲得比数字更直接：CPU 上树经常比循环慢，所以下面好几个配方都写着「保留这个循环」。&lt;/p&gt;&#xA;&lt;p&gt;为什么？因为 CPU 执行器计费跟操作数走，不跟层数走。&lt;/p&gt;&#xA;&lt;p&gt;层本身也有钱，大约每 1,000 层 10 µs，花在拷贝每层读的值和写的结果上。&lt;/p&gt;&#xA;&lt;p&gt;同样 16,384 个二项和，64 层 55 µs，1,024 层 65 µs，16,384 层 210 µs。这是 Bun 1.4.3 在 M5 Max 上的数，跑的是 &lt;code&gt;bun lab/runtime-cost/layers.ts&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;「减了操作、加了层」这个交换，只有在执行器按层计价的地方才划算。&lt;/p&gt;&#xA;&lt;p&gt;CPU 上不是。所以 Knuth-Plass 那个最优断行程序，树形比链形慢 1.7 到 7.7 倍。作者自己在结论里也是这句：CPU 上树经常比循环慢，所以好几个配方都写着「保留这个循环」。&lt;strong&gt;你换掉的是操作数，加的是层费，而执行器只按操作数收钱。&lt;/strong&gt;&lt;/p&gt;&#xA;&lt;p&gt;顺带一个藏得更深的坑。&lt;code&gt;emitArgmax&lt;/code&gt; 在近乎平局时有个 8.9e-8 的宽度带，树可能选中的候选和循环不同。需要精确的时候换 &lt;code&gt;emitHighestScoreExact&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;h2 id=&#34;作者自己退回过文本布局搬出程序省了-45-倍&#34;&gt;作者自己退回过：文本布局搬出程序，省了 4.5 倍&#xA;&lt;/h2&gt;&lt;p&gt;一个项目最该看的地方，是作者自己往回走了哪几步。&lt;/p&gt;&#xA;&lt;p&gt;Notes 一开始把文本布局整个放进程序里算。&lt;/p&gt;&#xA;&lt;p&gt;那版是 20,381 个活操作，一次求值 133 µs。&lt;/p&gt;&#xA;&lt;p&gt;后来他换成浏览器原生的 &lt;code&gt;&amp;lt;textarea&amp;gt;&lt;/code&gt;，程序掉到 4,251 个活操作，一次求值 25.1 到 25.3 µs，快了大约 4.5 倍。页面加载那个长任务从 114 到 117 ms 降到 57 ms。&lt;/p&gt;&#xA;&lt;p&gt;还有一次 GPU 整体化的尝试。HandwiredOS 搬到 WebGPU，一次求值 2.2 ms，CPU 上是 81 µs，慢了 27 倍。因为在一张表里，shader 的语句是一条接一条顺序跑的。&lt;/p&gt;&#xA;&lt;p&gt;那个 6.4 MB 的 shader 在 Metal 里 28 分钟没编译完。&lt;/p&gt;&#xA;&lt;p&gt;容量也是写死的。Photos 为了省大约 300 个操作，用 2 个 spring 代替 12 个。代价是半秒内「关→开另一张→再关」时，第一张会跳回格子。&lt;/p&gt;&#xA;&lt;p&gt;Text rows 硬上限定在 16,384 行。超限时的行为要你自己显式给：snap、carry、报告、丢弃，四选一。不给兜底，超限就是算错，而且没有输出提示。&lt;/p&gt;&#xA;&lt;p&gt;我这些数字一个都没有复现。这台机器没有 M5 Max，也没有 Bun 1.4.3，全部引自仓库的 &lt;code&gt;lab/status.md&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;作者自己也写明，这些微秒值随 load average 变。同一份代码在 19 个标签页和 4 个标签页下会进不同的「快模式」，只能做相对比较。&lt;/p&gt;&#xA;&lt;p&gt;把这些放一起，判据其实很清楚。&lt;/p&gt;&#xA;&lt;p&gt;该搬进去的，是离散决策密集、每个数都能声明清楚范围的那类逻辑：命中测试、可见性标志、几个数之间的门槛判断、几百个候选里的选择。**判据只有一条：这个数你能不能对调用方说出它的 grid。**说不出来，搬进去就是给自己埋一条不报错的减速带。&lt;/p&gt;&#xA;&lt;p&gt;别搬的是另外三类。每个框架都要算出整块布局的活，比如文本排版。单张大表要上 GPU 的活，因为它的语句会一条接一条顺序跑。输入可能带 &lt;code&gt;Infinity&lt;/code&gt; 还能落进未选分支的活，因为 &lt;code&gt;0 × Infinity&lt;/code&gt; 是 &lt;code&gt;NaN&lt;/code&gt;。&lt;/p&gt;&#xA;&lt;p&gt;还有一条最容易被忽略的：它的精确是「对 FP32 操作数精确」，不是对 float64 的 JS 精确。&lt;/p&gt;&#xA;&lt;p&gt;宽度除以 244，再乘 1,732 减 24，FP32 的倒数先舍入了一步，得到 6.9999995。比 7 小一格。于是 &lt;code&gt;columns &amp;gt;= 7&lt;/code&gt; 在它这里算出 0，在你那段 float64 的 JS 里算出 1。&lt;/p&gt;&#xA;&lt;p&gt;这种差不是 bug。是两套数在打架。&lt;/p&gt;&#xA;&lt;h2 id=&#34;先花二十分钟数一遍你那段逻辑里的符号级联&#34;&gt;先花二十分钟，数一遍你那段逻辑里的符号级联&#xA;&lt;/h2&gt;&lt;p&gt;真要用，第一个动作不是搬业务逻辑，是在仓库里跑一遍成本报告，看你的程序站在哪一边。&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-bash&#34; data-lang=&#34;bash&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bun lab/runtime-cost/report.ts      &lt;span class=&#34;c1&#34;&gt;# 单次求值、帧、构建各花多少&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bun lab/runtime-cost/layers.ts      &lt;span class=&#34;c1&#34;&gt;# 层数本身的钱&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;bun &lt;span class=&#34;nb&#34;&gt;test&lt;/span&gt; tests/runtime              &lt;span class=&#34;c1&#34;&gt;# 先把原语行为对齐&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;然后挑你代码里最热的那条判断，按这个形状写一遍：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;4&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;5&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// JS: const wide = width &amp;gt; 4 ? 10 : 12&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;c1&#34;&gt;// 两个分支都算，靠 1 个 bit 选&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;c&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;circuit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;assumeGrid&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;width&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;/&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;64&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;            &lt;span class=&#34;c1&#34;&gt;// 声明：承诺 width 落在 1/64 的格子上&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bit&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;emitCompare&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;circuit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;width&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;s1&#34;&gt;&amp;#39;&amp;gt;&amp;#39;&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;circuit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;constant&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;4&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;wide&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;emitSelect&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;circuit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;bit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;,&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;circuit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;constant&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;12&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;),&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;circuit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;constant&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;mi&#34;&gt;10&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;));&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;写完数一眼：&lt;/p&gt;&#xA;&lt;div class=&#34;highlight&#34;&gt;&lt;div class=&#34;chroma&#34;&gt;&#xA;&lt;table class=&#34;lntable&#34;&gt;&lt;tr&gt;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code&gt;&lt;span class=&#34;lnt&#34;&gt;1&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;2&#xA;&lt;/span&gt;&lt;span class=&#34;lnt&#34;&gt;3&#xA;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#xA;&lt;td class=&#34;lntd&#34;&gt;&#xA;&lt;pre tabindex=&#34;0&#34; class=&#34;chroma&#34;&gt;&lt;code class=&#34;language-js&#34; data-lang=&#34;js&#34;&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;kinds&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;{};&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;k&#34;&gt;for&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;kr&#34;&gt;const&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt; &lt;span class=&#34;k&#34;&gt;of&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;circuit&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;roundingPoints&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;())&lt;/span&gt; &lt;span class=&#34;nx&#34;&gt;kinds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;kind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;=&lt;/span&gt; &lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;kinds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;[&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;p&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;kind&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;]&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;||&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;0&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;)&lt;/span&gt; &lt;span class=&#34;o&#34;&gt;+&lt;/span&gt; &lt;span class=&#34;mi&#34;&gt;1&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;;&lt;/span&gt;&#xA;&lt;/span&gt;&lt;/span&gt;&lt;span class=&#34;line&#34;&gt;&lt;span class=&#34;cl&#34;&gt;&lt;span class=&#34;nx&#34;&gt;console&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;.&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;log&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;(&lt;/span&gt;&lt;span class=&#34;nx&#34;&gt;kinds&lt;/span&gt;&lt;span class=&#34;p&#34;&gt;);&lt;/span&gt;   &lt;span class=&#34;c1&#34;&gt;// 期望看到 integer bit；满屏 sign cascade 说明有声明没落到 grid 上&#xA;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#xA;&lt;/div&gt;&#xA;&lt;/div&gt;&lt;p&gt;排查的顺序也顺手记一下。&lt;/p&gt;&#xA;&lt;p&gt;结果对但是慢了五六倍，先数 &lt;code&gt;sign cascade&lt;/code&gt;，八成是某处 &lt;code&gt;assumeGrid&lt;/code&gt; 没写或者写错了，尤其别从 &lt;code&gt;circuit.constant(0)&lt;/code&gt; 起头累加。&lt;/p&gt;&#xA;&lt;p&gt;平局附近和 JS 结果对不上，查 &lt;code&gt;emitArgmax&lt;/code&gt; 那段 8.9e-8 的宽度带。搬到 GPU 上结果变了，查 boolean atom 是不是被 shader 化简掉了。&lt;/p&gt;&#xA;&lt;p&gt;至于要不要现在接进生产，我的看法是别急。&lt;/p&gt;&#xA;&lt;p&gt;这是一个单作者、825 次提交、9 个 open issue、最后推送停在 10 月 17 日的项目。作者把它当研究在做，status.md 里记着大半年的回滚和推翻。它是这个方向目前最好的一份工程记录，但&lt;strong&gt;适合读，不适合押&lt;/strong&gt;。&lt;/p&gt;&#xA;&lt;p&gt;真正值得现在拿走的也不是代码。&lt;/p&gt;&#xA;&lt;p&gt;是那三个动作本身：把一个判定写成可以清点的形状，量出它到底花几个操作，再用一个验证函数把这件事从「我觉得我声明对了」变成「我验过」。&lt;/p&gt;&#xA;&lt;p&gt;这套动作不依赖 Handwired。你手上任何一个把浮点判断摊开写的地方，都能照这个顺序做一遍：先写下你对这个数范围的承诺，再数它实际花了几个操作，最后用测试把承诺焊死。&lt;/p&gt;&#xA;&lt;p&gt;三步里最容易被跳过的是第三步，而被跳过的也正是唯一能兜住前两步的那一步。&lt;/p&gt;&#xA;&lt;p&gt;你现在那段逻辑里，有几条热判断是别人给你的 grid 声明在做主，而从来没被数过？&lt;/p&gt;&#xA;</description>
        </item></channel>
</rss>
