点击之家节点.js RowBinary 代码生成器
如果JS有-O3编译器旗,这种技能就成了. (对于 RowBinary 读取和写入)
一个技能和一个库,让一个编码代理从一个 ClickHouse 响应的列类型定义的首个传出口号上生成有声的 RowBinary 编码器. 这个 精神 在接近的背后。 。 .
读与写. 覆盖两个方向:阅读器(解码字节 = 值)和写作器(编码值 = 字节),在下分出 src/readers/ 财务报告和审定财务报表 src/writers/. 阅读器路径是比较成熟的路径——编剧们反射它为类型所打入的,有几条只解码路径(Dynamic, (中文(简体) ), JSON,则运行时头/编译路径,并列键入-阵列路径)尚未镜像.
状态
- Sonnet 4.6:60% - > 94.0%通过率
- opus 4.8:71% - > 94.7% 通过率
- Haiku 4.5: 52% - > 86.0%通过率
- ✅ 作曲家 2.5 快: 3x 解析器性能
- qq 724/724测试(阅读者+作家)
- 已选中类型
- 基准数
示例
取一个小订单结果:
SELECT id, uid, price, status FROM orders
-- id UInt8
-- uid UUID
-- price Decimal64(2)
-- status Enum8('new' = 1, 'shipped' = 2, 'done' = 3)
仅限API 阅读器 ——你通过组织图书馆的梳理器来写作. 正确,清晰, 和一个精细的默认:
const readOrderRow: Reader<OrderRow> = (s) => ({
id: readUInt8(s),
uid: formatUUID(readUUID(s)),
price: readDecimal64(2)(s),
status: readInt8(s), // raw enum int; `readEnum8(map)` resolves it to the name
});
技能的优化阅读器生成 ——同行,单形为
直线代码. 整行为固定宽(1+16+8+1=26字节),所以
四个单独的边框检查组合成一个 advance(s, 26) 每片叶子
读取在常数偏移时发生; 每一字段的组合符已消失 :
const readOrderRowFast: Reader<OrderRow> = (s) => {
const { buf, view } = s;
const o = advance(s, 26); // one bounds check for the whole 26-byte row
const id = buf[o]!;
const uid = formatUUIDTable(buf.subarray(o + 1, o + 17));
const price: DecimalValue = [view.getBigInt64(o + 17, true), 2];
const status = view.getInt8(o + 25);
return { id, uid, price, status };
};
同样的数值,同样的流出安全—— ~3.4x 键 快点.
如何使用
作为图书馆(具有技能):
npm install @clickhouse/rowbinary
npx skills-npm setup
仅仅作为一种技能:
npx skills add ClickHouse/clickhouse-js/skills/clickhouse-js-node-rowbinary
> Hey, Claude, tell me what the rowbinary skill can do for me.
> A lot! It generates custom, high-performance RowBinary readers and writers…
> Super, generate a reader for the queries in app/src/model.ts.
< Reading skill clickhouse-js-node-rowbinary…
用它与 ClickHouse JS 客户端
只有这个图书馆 解码 字节——它不打开连接. 说对了
与官方客户端获取 RowBinary 响应并反馈字节块
输入 streamRowBatches(chunks, readRow)。 。 。 .
RowBinary 不是客户端自行解码的格式之一,所以不要使用
client.query({ format: ... }) 对于它。 改为使用 client.exec({ query }) 与
页:1 FORMAT RowBinary 在SQL中写入的条款 exec 手拿回来
原始, 未解码的字节流 ,这正是这个库
消费。 (使用简单 RowBinary没有 RowBinaryWithNamesAndTypes时,除非您
读取器还跳过主名/类型头头. )
下面的行读取器是 orders 实例 缩略语.md; 互换
在阅读器中,技能为自己的专栏生成.
import {
type Reader,
readUInt8,
readInt8,
readUUID,
formatUUID,
readDecimal64,
type DecimalValue,
streamRowBatches,
} from "@clickhouse/rowbinary";
import { createClient } from "@clickhouse/client";
type OrderRow = {
id: number;
uid: string;
price: DecimalValue;
status: number;
};
const readOrderRow: Reader<OrderRow> = (s) => ({
id: readUInt8(s),
uid: formatUUID(readUUID(s)),
price: readDecimal64(2)(s),
status: readInt8(s), // raw enum int; `readEnum8(map)` resolves it to the name
});
// `exec` resolves to a Node `Stream.Readable`. It is already an
// `AsyncIterable<Uint8Array>` (chunks are `Buffer`/`Uint8Array`, which
// `streamRowBatches` normalizes), so pass `stream` straight in:
const client = createClient();
const { stream } = await client.exec({
query: "SELECT id, uid, price, status FROM orders FORMAT RowBinary",
});
for await (const rows of streamRowBatches(stream, readOrderRow)) {
for (const row of rows) console.log(row); // { id, uid, price: [unscaled, scale], status }
}
await client.close();
为什么值得
四个支柱——速度、正确性、判断力和起重较小的模型:
- ~2–3x码比直通解码器快. 技能的释放
单态、平整、直线编码——内含读取、边界检查
连接到相邻的固定宽柱上, 正确的阵列布局—— 测量
在~1.3-3.4x时 使用平面梳理器 API 写入的相同逻辑
(单位:千美元)
npm run bench) (中文(简体) ). 这就是为什么- 内含 JIT 友好代码
- 基准热路
- 最低限度拨款
- v8 和 Node.js 具体优化
- 正确无误,否则会悄悄地崩溃. UUID 字节
秩序,
Variant逐类取名歧视者,DateTime64二级 精度,签名高字宽整数,忠实小数,Dynamic编号 :JSON自我描述、透明包装、不透明AggregateFunction——每个 以实时、服务器验证的测试编码( u)详细情况) (中文(简体) ). - 判决,不只是代码. 技能包含工作知识
正确的调用 在此之前 写一行,所以代理既不过,也不过
工程师:
- RowBinary对不对? 将弦重结果改为文本,a
JSON*格式 + V8 的原生JSON.parse(单位:千美元)gzip编号 :zstd可以击败JS RowBinary 解码器 — 当数据是数字 / 宽整数 / 时为 RowBinary 到达 二进制重磅. - 整个缓冲还是流? 放下枪
advance()检查完整 in-memory 缓冲器( 较快); 为必须被块化的 HTTP 响应保留它们 横行而行. - 放下可移植脚手架. RowBinary是小 endian 和该 目标为x86/ARM,因此,技能可以避开大-endian / 字节- swap "可移动性"代码 谨慎的一发卡 倾向于添加.
- RowBinary对不对? 将弦重结果改为文本,a
- 提高更小模型的性能. 因为技能掌握在 来之不易的答案在前面 它最能提升一个更弱的模型 在24世纪 技能与无技能基准,技能 提出 索内 4.6 从60.4%到 94.0% (+34pp)——以技能装备提升水平 缩写 4.8 (94.7%), 它本身 所得 +23pp(71.5%=94.7%)。 编曲 2.5 快点 已经 a 3x解析器性能提升, Haiku 4.5 提出 从52%到86%——技能结束 大部分的模型能力差距 任务.
它会做什么
考虑到查询结果的列——他们的名字和 ClickHouse 类型
定义(按 RowBinaryWithNamesAndTypes,或由
——技能生成精确针对这些类型的解析码. 相反
而不是运输一个通用的,运行时驱动的解码器, 它释放出直线代码
因此,解析器只包含逻辑
具体结果形状需要.
司马只知道运行时间? compileRowBinaryWithNamesAndTypes(cursor)
读取 RowBinaryWithNamesAndTypes 将每个列类型折叠成一
苍蝇上的读取器( 类型字符串解析由 @clickhouse/datatype-parser, (中文(简体) ),
返回一个 readRows 用于溪流其余部分的驱动程序——通用的无编码源
动态计划路径。 上面的专用编码源在
类型是固定的.
重磅类型的正确性
为了平地 UInt64, String, DateTime 结果一个强大的模型已经写得很快,
正确的代码自己。 技术能维持下去 rowBinary 的长尾
陷阱 ——从出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出自出
在这里通过一个实时的服务器验证测试被捕获:
- 用户名 两个小恩德
UInt64半个, 每个字节反转对文本 窗体(顺序不是16字节). Variant(...)1字节歧视指数 类型名称 (ClickHouse全球分类),不是声明令;0xFF这是NULL.DateTime64(P)返回为[Date, nanoseconds]因此,子第二部分 并不是输给一个Date毫秒分辨率;Time编号 :Time64是期限, 不是瞬间.- 宽整数 —— 说
Int128编号 :Int256由64位单词组成 高级 单词已签名; 保留64位值bigint从不迷路number。 。 。 . - 十进制 - 保持准确
[unscaled, scale]双胞胎,不是迷幻活体. Dynamic编号 :JSON——自我描述:每值二进制类型编码,然后 值; 已声明类型JSON路径没有标记( 需要 计划。 包装被去除(Nullable编号 :Variant• 混凝土类型).- 透明包装 —— 说
LowCardinality(T)编号 :SimpleAggregateFunction(f, T)解码为内部T(RowBinary没有字典层);Nested(...)实值Array(Tuple(...))没有自己的线. AggregateFunction(...)——不透明,未定框状态:不可解码甚至 可跳转; 替换为服务器端 .FixedString保留跟踪 NUL 粘贴;Enum解码为 内嵌(名称图为元数据);BFloat16是前16个比特 (单位:千美元)Float32。 。 。 .
这也是原始模型最有可能出错的地方. 在清洁室测试中
编号 Variant 编号 : UUID 编号 : DateTime64 编号 : LowCardinality 计划,没有技能
索纳特生产了一个 默默错误的 UUID (将字节当作平地,缺失)
双回转式布局, 和无技能的奥普斯得到它 仅仅在之后
三次网络搜索技巧将这些答案交给了前方——通过
建筑,不看。 见 baseline/README.md 为全能控制.
而失败并不是一次性的——它是硬币的活页. 运行相同的无技能
声入人心 orders 计划( s)UInt8, UUID, Decimal64(2), Enum8(中文(简体) ) 5次进
隔离,仅限 5项中的3项 运行解码正确; 两种失败相同
UUID 字节顺序拼接. 即使路由运行在生成码中也不同 ~1. 9x
吞吐量。 凭借技巧,每跑一跑都是正确的. 因此,一个A/B 下售
间隙:从头到尾,模型是正确的,大约是60%的时间和沉默错误
其它的,而技巧 使正确的决定.
实例
6个端到端实例 缩略语.md每艘船舶均配备一个API-组合器
读取器和一个优化的,单形的, 并有一个可循环的回程测试和
a 基准——因此以下速度是衡量的,而不是声称的(第24号/V8;
npm run bench 给自己的号码:
| 示例 | 列数 | 优化速度 |
|---|---|---|
| 命令 | UUID, (中文(简体) ), Decimal64, (中文(简体) ), Enum8 | ~3.4x 键 |
| 推车 | 筑巢 Array(Tuple(...)), (中文(简体) ), Array(Nullable(...)) | ~2.0x 宽度 |
| 遥测 | Map, (中文(简体) ), Array(Float64), (中文(简体) ), Nullable名称 Tuple | ~1.4x 宽度 |
| 观察性 | Variant, (中文(简体) ), DateTime64(3), (中文(简体) ), LowCardinality,筑起 | ~1.4x 宽度 |
| 简介 | Array(String), (中文(简体) ), Nullable(Int32) | ~1.3x 键 |
| 事件 | UInt64, (中文(简体) ), String, (中文(简体) ), DateTime 分级 | ~1.05x — 平时 |
二轴能出胜相. 复合结构 单态化在
每行剪接器关闭次数的比例(carts 编号 :
telemetry 编号 : observability) (中文(简体) ). 每行格式化 是另一个,独立
复合材料: orders 最高的胜利(~3.4x),几乎
完全通过将 BigInt UUID 交换到查找表
formatUUIDTable. 真正平坦的病例——无热事的平板排
(单位:千美元)events)——在平面上,所以比较简单的API读取器就是那里正确的调取.
量度,不要假设.
范围
- 在范围(阅读):
RowBinary, (中文(简体) ),RowBinaryWithNames,以及RowBinaryWithNamesAndTypes节点.js的解码——全缓冲和流线 (被挤出)advance()编号 :NeedMoreData, (中文(简体) ),readRows(),和自动streamRowBatches()(带有内置小圆形警告和可选)coalesceChunks()解开过滤器). - 范围(书面): 反向编码路径—— a
writeX反射每个readX,将字节附加到一个Sink附加时writeRows()从@clickhouse/rowbinary/writer。只有几条解码路径尚未实现 镜像 :Dynamic, (中文(简体) ),JSON,运行时页眉/编译路径和列 键入阵列路径. - 范围外(目前): 浏览器和 Edge 运行时间, 非 RowBinary 格式 (JSON / CSV / TSV / Parquet),和大endian主机.
灵魂
RowBinary 代码生成器是一个狭义的东西。 但它是作为实例建造的 更宽泛的赌注是,一旦有能力的LLM成为其中的一部分,图书馆将变成什么 工具链。 3个班次,每个班次都在本repo中可见:
-
自改软件. 图书馆故意开船 多个 同类的等同解码器-
readUUID编号 :readUUIDBigInt编号 :readUUIDHiLo, (中文(简体) ),formatUUID编号 :formatUUIDTable, (中文(简体) ),new Array(n)对[]+push 组合键, 因为最快的速度取决于工作量, 不是那种类型 今天代理商在一代人的时间里从量子中挑选 基准。 下一步是将技能与运行的追踪层联系起来 备选案文A与备选案文B 现场工作量 并保持任意的胜负 此数据形状和访问模式 —— 重新调整自身的解析器 而不是将作者的猜测冻结到释放中. -
定制软件. 这里的值不是固定的高级API,而是 基准的构件加上结合这些构件的判断。 所以,最终用户 他们的密码不会弯曲到作者的平面上 组装高级API 他们 实际上想要,形状为他们的查询,行 形状,以及长期/中期预算。 两个工作量不同的小组增加两个 不同的库 来自相同的原始人, 既不继承一个设计 仅对原提交人的案件适用.
-
读写库. 为了安全起见 消息来源必须 可见于LLM, 不只是可运行。 所以,这个Repo是写 读写数字 : 每一份权衡都会被评论 – 每栏的 ClickHouse 类型 说明
SAFE TO TOGGLE快速变体上的标记,每个读者的 doc 注释带有精确的单态形式. 一个LLM可以 读取 为什么 作出了决定,并满怀信心地加以深刻修改,而不是 只要调用公共功能, 但安全地重修内部.
横线:最后一英里是胶水 LLM 写得稳定,基准 因此,作者的工作缩水 输出好的原始人和记录 他们的取舍是诚实的,而不是试图烘烤正确的表现 将所有可能的工作量提前输入图书馆.