先猜:一个数加 1,还会是它自己吗?
数越大,缝隙越宽。
固定只加 1。向右拖动,观察橙色目标与绿色落点什么时候分开。
真实 JavaScript Number 运算 · binary64
为什么落点会改变?
在 2 的 e 次方右侧,binary64 相邻数的间隔是 2 的 (e−52) 次方。53 时,加 1 正好落在两个数的中点,按“最近值,中点取偶数”舍入回起点。图只画起点右侧;左侧跨过幂次边界时间隔不同。精确整数目标用 BigInt 计算。
这只说明当前表示法容纳不下那个结果。大整数、小数与其他精度格式有各自的空间和计算成本。
换一个顺序,答案也会变吗?
这四个整数的精确和是 2;并排比较左到右、相邻配对、补偿求和。配对不保证每个输入都更准。
看起来绕路的公式
两个接近的平方根相减,会让此前的舍入误差暴露出来。把表达式改写为倒数,可以避开这次相消。展开讲义再核对它的适用范围。
先猜:少搬几次,会不会比少乘几次更重要?
一份数据,走了多少回头路?
每个结果格子都来自 A 的一行与 B 的一列。切换计算顺序,沿着每次读写走一遍;绿色表示缓存中已有,橙色表示需要装入。
等待第一步
| 顺序 | 乘法次数 | 数组访问 | 装入缓存行 | 结果核对 |
|---|
模型省略了什么?
三个数组各自按缓存行对齐;只统计装入,不统计脏行写回、预取、多个缓存层、真实地址冲突和编译器寄存器优化。每个数字占 8 字节。缓存容量是可调的假设,不能用这张表推断本机缓存大小或实际毫秒数。逐格实现的部分和留在局部变量,其余两种实现按源码更新 C。
先猜:16 个人会把工作缩短为十六分之一吗?
多出来的手,也要等同一个开头。
可调成本模型 · 单人任务为 100 时间单位
这条时间线基于哪些假设?
准备完成后,剩余工作可均匀拆给所有人,最后统一汇合;协调成本假设为“每增加一人的成本 × (人数−1)”。它是一种可检验的估计,不是调度器记录。将串行占比或协调成本设为零,分别检查理想上限;真实 Worker 计时在下方。
先猜:换一种表示,什么时候才划算?
提前做的工作,会在以后省回来吗?
查一次,还是查很多次?
每次求 16 项之和。逐项读取与前缀和真实执行并核对结果;条形只比较数组读取次数,读取成本并未设为实际毫秒。前缀和准备另有写入、分配和算术开销。
留下有用的值,也要记住它在哪里。
真实生成两个 256 项向量,并实际计算稠密与稀疏点积。空间为 typed array 数据区字节数,不含对象头与临时数组。转换要先扫描两份输入;非零比例高时,索引本身可能使空间更大。
先猜:再多算几步,总能再准一点吗?
答案慢慢靠近,什么时候可以停?
真实二分迭代 · 蓝线为区间半宽
为什么不直接宣称“误差小于这个数”?
精确实数运算中,根始终在二分区间内,半宽可界定中点的误差。本实现的 mid×mid 使用浮点数,接近精度极限时比较也会舍入;严格误差认证需要带向外舍入的区间方法。这里同时报告实际停止原因,并与 Math.sqrt 的结果对照,Math.sqrt 也不是精确实数答案。
回到真实机器 / 保留与直觉不同的结果
看过过程,再测一次。
同一组小整数输入,先用 BigInt 独立核对每个输出。三种顺序轮换测量各 5 次,保留全部样本;另一组比较使用 1、2、4 个新 Worker,包含启动、输入复制、计算、返回与拼接。
等待测量。较小输入可能接近计时器分辨率。
计时范围与环境
顺序实验测量函数调用,包含输入核验、输出分配与乘法;预热各一次,不包含生成输入和 BigInt 参考答案。Worker 实验每轮新建 Worker、复制完整 A/B,包含脚本加载和合并,参考核对在计时之外。没有测 GPU,也未读取 CPU 性能计数器。浏览器优化、后台负载、电源状态和计时器精度都可能影响结果。