请教一个 CPU 密集计算多线程优化办法?

2021-09-28 10:43:57 +08:00
 James369

我在写一个 js 前端项目的时候,遇到一个比较耗时的操作 task,它基本结构是一个 2 层的 for 循环计算,task 大体长这样:

function task(x: Int) {
  ... 
  for(var i = 1...10000) {
    for(var j = 1...10000) {
      ...计算数据(与参数 x 相关)
    }
  }
  ...
}

这个 task 在点击按钮时触发,要耗时大概 3 秒左右,为了不阻塞 UI 界面。我想开子线程去优化。那么开子线程能否做到以下几点:

  1. 不管用户如何快速的点击按钮,这个 task 子线程始终保持在 1 ~ 2 个(避免开启过多线程)
  2. 当用户点击按钮时,如果已有 task 未执行完毕,可以先终止或跳出原 task,再执行新 task 。
  3. 假如在开启子线程执行 task 时,需要再传入一个大的 Object (例如 task(x: Int, big: Object)),那么这个 Object 要不要做多线程同步,如何做?

第 3 点如果做不到,那就放弃第 3 点。

3532 次点击
所在节点    程序员
43 条回复
wellsc
2021-09-28 10:46:22 +08:00
js 有多线程吗
James369
2021-09-28 10:50:06 +08:00
@wellsc 所以我来问一下 js 的实现技术栈,其实这 3 点在其它语言中都很好实现,但是不知道 js 怎么做?
typetraits
2021-09-28 10:50:39 +08:00
启动 Service Worker,把任务放进 worker 执行,然后发送消息回主线程进行确认
wellsc
2021-09-28 10:52:20 +08:00
@James369 js 就不太适合计算密集型,它的适用场景更偏向 io 密集
James369
2021-09-28 10:55:09 +08:00
@wellsc 现在就有这方面的需求,并且还不方便放到后端去计算。
Huelse
2021-09-28 11:07:57 +08:00
这需求就不适合 js 做,js 也没真多线程,一定要做的话考虑下 WebAssembly 吧
James369
2021-09-28 11:12:57 +08:00
@Huelse 我不信,js 都要一统江湖了
Building
2021-09-28 11:19:53 +08:00
js 没有多线程,可以试试开一个 iframe 专门帮你做计算。
zhangxh1023
2021-09-28 11:21:27 +08:00
前端遇到这种问题,大多数情况下都是用一个 loading 遮罩层,遮盖掉整个页面的。
或者看看能否从需求上砍砍,利用类似于懒加载的方式,按需求计算。
James369
2021-09-28 11:24:13 +08:00
@Building 奇怪,前端怎么这么弱 了? 那上面有 v 友提到的 service worker 或者 web worker 能否实现这个需求呢?
fengjianxinghun
2021-09-28 11:27:17 +08:00
试试 webgpu, 如果只是数值计算转到 webgpu 太合适了。
fengjianxinghun
2021-09-28 11:28:16 +08:00
webgpu compute shader
libook
2021-09-28 11:29:18 +08:00
@wellsc #1 搜一下 Web Workers,Web API 现在有挺多花活,以前很多 JS 干不了的事情现在都能干了,当然不同引擎的具体实现会有差异。

@James369 我没怎么用过 Workers,提供几个点子看看是否可行。
1. 主线程创建 worker 可以把 worker 引用放到一个数组里,然后每次用户点击按钮都检测这个数组有几个元素,销毁 worker 就减少一个元素,创建 worker 就增加一个元素。
2. 主线程每次需要终止一个 worker 的时候就向这个 worker 发送消息,worker 可以监听 message 事件,收到终止消息就改一个局部变量,然后每次循环的时候都检查这个变量,检查到终止信号就退出循环并做善后处理,直至整个 worker 执行完退出。不需要善后处理的话可以直接用 Worker.terminate()强杀。
3. Object 是静态的话可以写死在源代码里,如果是动态的,貌似现成之间只能通过 message 来传递数据。如果你需要多个线程同步数据变化,就是分布式一致性的问题了,这个问题可能也能解决,只不过比较复杂,而且如果经由主线程的交换多了,主线程就会成为瓶颈,反而没必要用多线程了。

你可以去 MDN 上看看 Worker API 的文档,没准能提供些灵感。

另外可以跳出现在的需求,从架构和算法上看看有没有其他优化方法,比如是不是每次都要处理全量数据。
cheng6563
2021-09-28 11:31:37 +08:00
就算是 nodejs 做后台服务器也是用多进程解决问题的
wellsc
2021-09-28 11:31:49 +08:00
@libook 是可以用,客户端的东西,就是怕用户体验不好🐶
FrameJack
2021-09-28 11:34:44 +08:00
GPGPU 了解一下
lisongeee
2021-09-28 11:35:05 +08:00
不阻塞 UI 界面,你可以将任务分片,类似 react fiber
libook
2021-09-28 11:40:32 +08:00
CPU 密集型也不一定 WebAssembly 就能解决,目前 V8 的很多计算效率已经被优化很好了(乌龟绑火箭,不是说着玩的),有时候上了 Wasm 也没有明显的性能提升,反而加大了开发成本。

具体要看你的计算是不是 JS 所不擅长的,比如通过一些数据结构和内存上的奇淫技巧来大幅优化计算效率,这种用 Wasm 可能会有明显的性能提升。
crackhopper
2021-09-28 11:45:09 +08:00
1. 不适合在前端做,为啥不放后端。
2. 不适合用 js 做,在后端了,反正怎么搞都有办法。
James369
2021-09-28 11:50:11 +08:00
@fengjianxinghun 应该还用不到 GPU 这么重型的武器吧,并且也不是所有的客户机都有 GPU

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/804821

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX