前端工程化七-前端可视化优化

# 万级节点优化

10w 条原始数据,经过去重、去无效点、预加工之后可得约 5w 个节点,以及 4w 多条连线。这些数据保存在一个 Object 里,数据格式如下,约占用 10M 内存。

{
  "nodes": ["A", "B", "C", ...],
  "links": [{
    "source": "A",
    "target": "B"
  }, {
    "source": "C",
    "target": "D"
  }, ...]
}
1
2
3
4
5
6
7
8
9
10

在 Three.js 中构造物体时,最常使用 THREE.Geometry 构造几何体。Geometry 是 Three.js 中的一种数据结构,其包含了几何体的顶点位置、颜色等等信息,储存信息时使用了 THREE.Vector3, THREE.Color 等数据结构,读写非常直观方便,但是性能一般。按照最寻常的思路,对于每个节点,我们需要使用 THREE.CircleGeometry 构造一个圆,对于每条线,我们需要使用 THREE.Line 构造一条线。

// 最初版本
// 每个节点绘制一个圆
this.paintData.nodes.forEach((node) => {
  node.geometry = new THREE.CircleGeometry(5, 12)
  node.material = new THREE.MeshBasicMaterial({color: 0xAAAAAA})
  node.circle = new THREE.Mesh(node.geometry, node.material)
  this.scene.add(node.circle)
})
// 每条线绘制一个线段
this.paintData.links.forEach((link) => {
  link.lineMaterial = new THREE.LineBasicMaterial({color: 0xAAAAAA})
  link.lineGeometry = new THREE.Geometry()
  link.line = new THREE.Line(link.lineGeometry, link.lineMaterial)
  link.line.frustumCulled = false
  this.scene.add(link.line)
})
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

# 复杂度优化

原始数据总是要预处理的,比如统计最有影响力的(分享数最多)的节点,筛掉没有分享关系的无用节点,进行数据剪裁等等。海量数据情况下,使用合适的算法就很重要了;初版写的很随意,遍历套遍历,复杂度较高,1w 数据还能接受,跑个几百 ms 出来了,10w 数据直接卡住六七秒。后来优化,多用 hashmap,空间换时间,改写了两三版,最终将计算耗时控制在 2s 以内,还算理想。

# 多web worker拆分

将计算过程迁移到 worker 中可以避免阻塞主线程,保证交互的流畅;然而为了最大化加速计算,我们可以拆分至多个 web worker 中,以此充分利用多核性能。Javascript Web Workers Test v1.4.0 (opens new window) 是一个 web worker 测试,测试可知在多核机器上,拆分确实可以显著缩短计算时间。借助浏览器接口 navigator.hardwareConcurrency (opens new window) 我们可以获得处理器核心数,然后就可以拆分,比如 8 核机器拆出 7 个 worker 线程可以实现最大化利用核心。计算逻辑的拆分和结果的合并都需要自行设计,本文仅作了调研,由于计算耗时已经较短没有再做拆分工作。

# GPU加速

服务器上头像图片都是方形的,但是绘制时我们想要圆形图像,怎么处理出圆角效果呢?按通常思路,我们可以借助 canvas api,画个圆填充图片,最后导出新图片(见张鑫旭大大文章:小tip: SVG和Canvas分别实现图片圆角效果 (opens new window))。但由于我们具有操作片元着色器的能力,于是可以直接在着色器上进行纹理的修改 (opens new window),这里不但裁成了圆角,顺便还做了描边和抗锯齿。着色器直接运行在 GPU 上,性能很好。如果用软件模拟抗锯齿,开销肯定大得多。

# 节流

在节点数量庞大时,节点头像的拉取和绘制会成为一个性能问题,一般来说当视野范围很大时,节点很小,图片无需加载,可以设置只有在经过缩放,节点大于一定程度(即场景相机 Z 坐标小于一定值)时才加载视口内头像。『判断视野内有哪些节点并加载』这个操作若在每帧都执行频率太高了,可以使用 throttle 技术限制到每秒执行一次;同时头像物体缓存起来,视野移动时进行动态卸载与加载,避免头像加载过多带来性能问题。

https://juejin.cn/post/6844903709982326792#heading-6

Last Updated: 11/15/2022, 9:34:42 PM