ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

deck.gl 64 位属性与二进制属性生成:从 RFC 到 AttributeManager 的实现演进

deck.gl 64 位属性与二进制属性生成:从 RFC 到 AttributeManager 的实现演进 deck.gl 64 位属性与二进制属性生成从 RFC 到 AttributeManager 的实现演进【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl本篇技术指南以 dev-docs/RFCs/v7.2/64bit-attribute-rfc.md 为核心骨架剖析 deck.gl 在大数据可视化场景下的两条关键演进主线二进制列式数据到顶点属性的高速生成路径以及在 AttributeManager 中原生支持 64 位双精度属性、从而消除大量重复的自定义属性生成器。读者读完本文将理解 RFC 提出的性能度量单位Mrows/s、TransformFeedback 加速方案、fp64low标志、Float64Array输入支持等设计意图并能在当前仓库源码中定位对应实现为阅读与贡献 deck.gl 核心属性系统提供完整的知识地图。一、RFC 背景属性生成是大数据可视化的性能咽喉deck.gl 的渲染流程中每个图层Layer都需要把业务数据如 JSON 数组、CSV 解析结果、二进制列转换为 GPU 顶点缓冲vertex buffer中可用的顶点属性attribute。这一过程被称为attribute generation / attribute 更新。当数据规模达到百万行以上时属性生成往往成为整条渲染管线中耗时最高的环节之一。该 RFC 的写作目的正是围绕这一瓶颈提出一组相互关联的提案定义一套官方统一的性能度量单位让属性生成与渲染性能可以横向比较在Probe.bench基准工具中加入unit与iterations配置用TransformFeedback把二进制数据 → 属性这一典型用例搬到 GPU 上执行在AttributeManager中原生支持 64 位双精度属性以消除大量重复的自定义属性生成函数支持Float64Array输入并定义高效的 JS 拆分函数引入forEach迭代抽象以支持分块chunked数组等高级输入形态。值得注意的是RFC 中的多个提案在后续版本中已落地或部分落地本文会同时给出当前仓库中的对应实现证据帮助读者把设计意图与工程现实对应起来。二、度量标准用 Mrows/s 统一性能话语RFC 首先提出一个简单但影响深远的约定deck.gl 讨论属性生成与渲染性能时官方度量单位为 Megarows/second百万行每秒缩写 Mrows/s。为支撑这一度量RFC 给出了当时基于Probe.bench的初步基准数据Layer全量属性再生成Full regeneration仅颜色属性再生成color regenerationLineLayer10 Mrows/s30 Mrows/sScatterplotLayer12 Mrows/s—并配套提出两项工具链提案为Probe.bench增加unit与iterations两个配置项使基准输出可以直接以Mrows/s呈现或者退而求其次实现一个自定义的Bench格式化器完成单位换算。依据说明RFC 中的性能数字是特定历史版本与硬件的测量结果仅用于说明度量口径不应理解为当前版本的承诺性能。当前仓库的基准体系位于 test/bench 目录如layer.bench.js、attribute-update.bench.js其中attribute-update.bench.js正是针对属性更新路径的持续度量感兴趣的读者可以在此基础上复现与扩展 Mrows/s 级别的统计。三、二进制属性生成从 JS 逐行迭代到 TransformFeedback3.1 动机JS 访问器系统正在向二进制列扩展RFC 明确指出deck.gl 的 JS accessor 系统正在被扩展为支持bundles of binary data二进制数据列。在这一背景下二进制数据到属性的变换被视为TransformFeedback 的典型canonical用例初始阶段可以生成一个自定义 shader把输入列映射到输出属性因为整个变换在 GPU 上执行、无 JS 逐行开销二进制场景下有望把吞吐量提升到 Gigarows/s 量级。3.2 两种映射描述方式关于二进制输入列如何映射到属性数组RFC 给出了两种可选规格化方式允许图层编写 GLSL 代码片段描述映射关系——RFC 认为这可能是 GLSL accessor RFC见 dev-docs/RFCs/proposals/glsl-accessor-rfc.md的变体在项目中的首个应用场景提供一套简单的抽象操作系统类似 Arrow 的谓词系统让用户无需编码即可选取列例如getColor: [Column(intensity), Column(intensity), Column(intensity), Constant(255)]该表达式表达颜色由intensity列复制三份、alpha 固定为 255的映射无需写任何 GLSL。3.3 反面提案不为二进制数据提供 JS APIRFC 同时明确建议不为从二进制数据生成属性提供专门的 JS API理由是自定义场景被假定为罕见中JS 代码当然仍可手工完成转换但在二进制数据上逐行迭代的 JS API 相当笨拙clunky一旦 TransformFeedback 成为二进制输入的主流方案JS 方案的使用率存疑JS 方案会进一步增加属性迭代系统的复杂度应用本身完全可以自行预处理数据不一定非要用 accessor 完成尤其重要的是Transform 类来自 luma.gl已提供 Texture 回退方案来覆盖 WebGL1因此 GPU 路径的兼容性不再是障碍。RFC 的结论是与其把二进制处理内建进本就高度复杂的属性管理系统不如提供一些通用的二进制数组工具函数本仓库中的typed-array-manager、math-utils等工具即属此类基础设施。从当前仓库看这一以通用工具为主的思路已部分落地属性系统通过setBinaryValue/BinaryAttribute直接接受外部二进制缓冲见 modules/core/src/lib/attribute/attribute.ts同时toDoublePrecisionArray等纯 JS 工具函数负责数值拆分详见后文。四、Improved 64 bit supportAttributeManager 原生双精度支持4.1 核心提案为AttributeManager.add()增加fp64low标志WebGL 原生只有 32 位浮点精度fp32。deck.gl 的 64 位fp64方案把一个 double 拆成high高 32 位与 low低 32 位两个 float32分量分别存入两个缓冲/属性如instancePositions与instancePositions64Low在 shader 中用 fp64 运算库重新组合从而在保持 WebGL2 兼容性的同时获得双精度级别的坐标精度。RFC 的提案是在AttributeManager.add()上增加一个fp64low标志在attribute.js中新增一条走fp64low拆分的更新路径即_updateBufferViaStandardAccessor。RFC 给出的参考实现如下_updateBufferViaStandardAccessor(data, props) { const state this.userData; const {accessor} state; const {value, size, fp64low} this; const accessorFunc props[accessor]; assert(typeof accessorFunc function, accessor ${accessor} is not a function); let i 0; if (fp64low) { for (const object of data) { const objectValue accessorFunc(object); this._normalizeValue(objectValue, value, i); this._fp64low(objectValue); i size; } } else { for (const object of data) { const objectValue accessorFunc(object); this._normalizeValue(objectValue, value, i); i size; } } this.update({value}); }关键点在于当fp64low为真时每个顶点的数值在被_normalizeValue写入主缓冲的同时还要调用_fp64low把残差部分写入 low 缓冲。4.2 现代实现DataColumn 的type: float64与fp64: falseRFC 中的fp64low标志在现代 deck.gl 中演化为DataColumn 的双精度double precision机制核心实现在 modules/core/src/lib/attribute/data-column.ts通过logicalType float64判定双精度属性见>// 交错布局high/low 交替写入同一个 Float32Array function fp64ify-interleaved(float64Array, float32Array) { float32Array float32Array || new Float32Array(float64Array.buffer); // for (let i 0; i float64Array.length) { const value64 float64Array float32Array[i * 2] value64; float32Array[i * 2 1] value64 - Math.fround(value64); } } // 分离布局high/low 分别写入两个 Float32Array function fp64ify-split(float64Array, float32ArrayHigh, float32ArrayLow) { for (let i 0; i float64Array.length) { const value64 float64Array float32ArrayHigh[i] value64; float32ArrayLow[i] value64 - Math.fround(value64); } }两个函数的拆分核心都是value64 - Math.fround(value64)Math.fround返回 float32 可精确表示的值原值减去该值即为丢失的残差low 部分。RFC 还留下一则 TODOWebAssembly 能否进一步提升该函数的性能6.2 现代实现fp64LowPart与toDoublePrecisionArray当前仓库把上述思路落到了 modules/core/src/utils/math-utils.tsfp64LowPart(x)一行实现残差计算x - Math.fround(x)math-utils.tstoDoublePrecisionArray(typedArray, {size, startIndex, endIndex})把Float32Array | Float64Array拆分为双倍长度的Float32Array采用交错布局[1xHi, 1yHi, 1zHi, 1xLow, 1yLow, 1zLow, 2xHi, ...]并复用typedArrayManager管理 scratch 缓冲以减少分配开销math-utils.ts。调用链上attribute.ts与data-column.ts在上传外部缓冲、写入缓冲等环节统一调用toDoublePrecisionArray完成自动拆分见 modules/core/src/lib/attribute/attribute.ts 与 modules/core/src/lib/attribute/data-column.ts这正是 RFC 中_fp64low路径的当代替代。验证证据toDoublePrecisionArray的行为在 test/modules/core/utils/math-utils.spec.ts 中有完整测试——断言返回类型为Float32Array、长度翻倍并利用fromDoublePrecisionArray反解验证拆分—重组的数值一致性同时支持startIndex/endIndex的局部范围拆分。6.3 支持Float64Array作为二进制输入RFC 建议把Float64Array纳入受支持的输入类型使其可以直接作为 64 位属性的数据源。当前实现中LogicalDataType显式包含float64data-column.ts双精度属性在 GPU 上以float32存储、在 CPU 侧分配Float64Array或按fp64: false分配Float32ArraytoDoublePrecisionArray同时接受Float32Array与Float64Array两种输入并在 WebGPU 下为 fp32 源数据补充零 low 元组见 attribute.ts。七、性能优化路径的选择为哪种 JS 用例做优化RFC 用一个反直觉的分析点醒读者最快的 JS 用例并不一定值得优先优化。最快的路径是数据已按子数组就绪、可直接拷入属性缓冲的场景getPosition: row row.position该路径可以轻松达到约70Mrows/s——作为性能宣传数字很漂亮。但 RFC 明确假设这不是应该优先优化的主用例理由有三大数据百万行通常不是 JSON 格式CSV/DSV 等扁平格式更常见扁平格式下数据形态是顶层longitude、latitude两列访问器必须在运行时把两列合并成坐标对这带来额外成本即便数据是 JSON生成子对象的成本也早已发生在load-parse-attributize加载—解析—属性化链条的更早阶段访问器内部看不到的对象创建开销不代表不存在。因此 RFC 建议参考基准应选择扁平数据结构或者同时给出两种数字避免被最优路径误导。八、UseforEachin custom layer attribute generators为分块数据铺路8.1 核心机制把迭代抽象为forEachRFC 最后一项提案是让图层属性计算函数通过forEach回调迭代数据而不是直接for...of遍历从而为高级输入形态如分块数组留出扩展点for (const chunk of data) for (const row of chunk) { } }默认实现可以是一个零成本的外层迭代器——直接原样返回data因此对现有调用方不构成性能损失。8.2 利弊权衡RFC 坦诚列出了这一抽象的两面性优点把迭代保留在图层属性更新器中JS 更新路径更快、代码更优雅缺点限制了扩展性难以根据输入类型选择优化的迭代路径如并行、分块、列式访问关键判断一旦 TransformFeedback 成为二进制属性生成的主路径比 JS 快一个数量级为支持列式数据的高级用例而在 JS 侧付出少量性能代价是值得的。也就是说forEach抽象与 TransformFeedback 路线是配套设计GPU 承担吞吐量重任JS 侧则把灵活性让给更丰富的数据形态。九、总结RFC 的技术遗产与阅读路径这份 RFC 的价值不在于每一条提案都原样落地而在于它为 deck.gl 的属性系统划定了清晰的演进方向RFC 提案当前仓库对应实现Mrows/s 统一度量 属性基准test/bench/attribute-update.bench.js 等基准体系TransformFeedback 生成二进制属性依赖 luma.gl Transform 类含 WebGL1 Texture 回退与 BinaryAttribute 输入路径配合AttributeManager.add()的fp64low标志演化为 DataColumn 的type: float64与fp64: falsedata-column.ts移除冗余自定义属性生成器坐标类图层统一走instancePositions6464Low自动生成路径Float64Array输入与高效拆分函数fp64LowParttoDoublePrecisionArraymath-utils.ts自定义生成器中的forEach迭代抽象属性更新与外部缓冲路径的迭代抽象设计iterable-utils.ts对于想深入源码的读者推荐按以下顺序阅读先通读本 RFC 建立问题意识再精读 modules/core/src/lib/attribute/data-column.ts双精度判定、fp64: false、64Low缓冲共享逻辑、modules/core/src/lib/attribute/attribute.ts外部缓冲setBinaryValue与自动拆分调用点、modules/core/src/utils/math-utils.ts拆分工具实现最后用 test/modules/core/utils/math-utils.spec.ts 与 test/modules/core/lib/attribute/attribute.spec.ts 验证行为。配套的背景文档还可参考 modules/core/README.md、fp64 坐标系统说明 docs/developer-guide/fp64.md 以及 GLSL accessor 提案 dev-docs/RFCs/proposals/glsl-accessor-rfc.md。【免费下载链接】deck.glWebGL2 powered visualization framework项目地址: https://gitcode.com/GitHub_Trending/de/deck.gl创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表