当前位置: 首页 > 网络应用技术

前 - 端多线程编程探索

时间:2023-03-08 17:46:58 网络应用技术

  本文探讨了使用共享arrayBuffer(后来称为SAB),Web Worker和Atomics API的多线程编程的可行性,并构建了一个视频处理场景以进行比较实验。示例代码仅是演示级别,并且有很多改进空间。我希望共同交流并共同改进。

  很久以前,我做了Web RTC的脸部贴纸功能。当时,痛点是帧速率不高,主线程CPU占据过高。从天然中,我想到了网络工作者的方案分享主线程的压力。但是,为了确保一个一定的帧速率并确保较低的延迟,主线程和子线程之间的频繁通信。在后母亲的复制模式下,数据复制的计算功率消耗几乎等于面部识别计算功率消耗。在传输模式下,CPU职业率将大大降低,但是高帧速率将导致高延迟(当前的铬似乎没有这个问题)。

  仅限于当时知识的局限性,没有尝试过新的计划。我发现SAB重新开放,我想使用SAB在线程之间进行交流以降低沟通成本。

  MDN门户网站:https://developer.mozilla.org/zh-docs/web/javascript/global_objects/sharedarrearaybuffer

  简而言之,它会根据ArrayBuffer添加内容共享函数。当您将SAB传递给Web Worker时,它没有数据的数据,也没有传输到内存控制记忆。修改数据的线程之一是可见的。

  短篇故事:

  这实际上是一个非常古老的API,但是由于幽灵脆弱性曾经被禁止(避免使用共享arraybuffer来达到高精度计时器)。直到似乎有些更安全的交叉策略似乎再次打开了SharedArrayBuffer。

  在多线程下,我们的可选解决方案包括邮政和sab。postMessage将带来通信延迟和CPU消耗,随着传输数据和传输频率的增加,并且邮政随着工人的交流,并且无法在工人之间进行交流,并且不能在兄弟的兄弟之间进行交流,。

  SAB可以减少线程之间的沟通延迟并实现兄弟工人之间的沟通。但是,使用SAB会增加编码的复杂性,尤其是对于不熟悉的多线程编程的前端,它很容易同时出现,并造成意外的书写bugs.和SAB可以与WebAssembly结合使用,以实现WebAssembly的多线程编程。

  从遥控端导入视频。在播放过程中,实时添加了滤波器效果。过滤器的处理涉及大量计算。C.CPU职业中的单线程和多线程之间的差异和输出帧速率。我们使用WASM和JS实现过滤器部分以模拟一般计算任务和重型计算任务中的任务。

  总体交互过程如下所示。下一个

  仅设计了一个工人进行计算。必须降低帧速率,因为计算成本不会降低,但通信成本增加。每个帧所需的时间必须更长。因此,如果您想查看效果,则必须设计两个以上的计算线程。作为渲染线程,我们可以使用主线程,或者我们可以使用OffScreenCanvas来设计一个单独的渲染线程。在这里我们使用主线程。

  并行阅读和写作可能会产生不可预测的结果。为了避免这种情况,JS是为单个线程语言而设计的。即使出现了Web Worker,该操作也被禁止到DOM,以避免在同一时间同时操作两个线程。EssenceHowever,SharedArrayBuffer的出现使两个线程同时运行相同的内存,这使该程序导致了意外的错误。要避免这种事情,我们需要加入锁定机制。

  JS的锁定机制使用原子API,该API提供了一系列原子操作,例如Atomics.Add(),可用于替换众所周知的i ++订单。

  在此处使用开源库直接使用原子学并不容易。该库主要实现锁定,线程等待,解锁和唤醒的机制。在线程操作共享arrayArrayBuffer之前,请确保您必须占据锁定并释放它手术后的时间。

  我们将SAB设计为以下内存区域

  其中,读取器被写入该区域时,作为原始视频数据,并在添加过滤器后写入WriteBuffer。锁被用作锁定实例所需的内存。

  初始代码

  在计算线程方面,有5个状态:wait_to_read,读取,计算,wait_to_write,writing.hom yous yous,wait_to_read正在等待渲染线程编写原始数据。该状态一直处于等待状态。从SAB读取阅读以读取原始数据。该状态需要锁定。该线程已共同执行;wait_to_write正在等待将其写入计算的数据中;写作是计算后的数据,需要在此状态下锁定。每个状态的循环如下图所示。

  如果将主线程用作渲染线程,则它存在于3个状态下,读取,写作,iDle.为了避免阻止主线程,无法在主线程下执行。因此,当锁定锁时,即使未抓住锁,也无法输入线程等待状态。取而代之的是,选择抓住它的机会。因此,在闲置状态下更换等待状态也解放了主线程。租赁不是一个时间 - 耗时的过程,因此它没有单独作为状态,请参见下面的图。在各个州

  基于线程状态的上述设计,我们将程序设计为两个独立状态,具有/无原始数据和/无计算数据。根据比较,该程序由特价表示。

  查看主线程的圆形代码

  计算线程相对简单

  首先将滤波器效应效果的场景与JS进行比较,然后使用主线程与1-4工人进行比较。用于模拟大量计算数量,SAB带来的性能改进

  13帧,12帧,24帧,35帧,47帧,CPU的帧速率为99.9%12%24%35%44%。接下来,我们使用WASM来达到过滤效果,并比较主线程和1-4个工人。用于模拟计算量,SAB带来的性能改进

  帧速率22帧,18帧,40帧,57帧,60帧,CPU占99.9%29%38%52%61%。从两个比较可以看出:

  进一步的绩效分析

  就主线程而言,大多数计算能力都在数据读取中消耗了,而720p视频的Getimagedata操作为12M。在此场景中,这部分工作必须放在主线程上。这是为什么帧速率与主线程CPU.CANVAS2D呈正相关。尝试节省一半的阅读时间,但增加了写作时间。总体上是负收入,这个场景不适用。

  对于JS计算任务,计算帧大约需要70-120ms。对于WASM任务,计算帧大约需要20-30ms。在计算任务之前和之后,还可能有一个额外的开销用于多行冲突检测,而且这个开销的时间尚未固定。您可以参考下面的场景。

  从上面的3个图中,您可以看到:

  应该注意的是,该场景仅用于比较实验,而不是最合适的SAB场景。我们还有很多优化解决方案可以尝试,包括比SAB更有效的解决方案。

  在音频和视频场景方面,RTC方案中更应用了这组解决方案。在这种情况下,帧速率不高,通常30帧足以满足;分辨率要求不高,可以降低getimagedata的成本;RTC PairsReal - 时间要求相对较高,需要尽可能减少延迟;可能有相对较大的计算功耗的场景,例如面部贴纸,美容,背景更换等。

  在某些帆布渲染方案中,我们还将为子线程提供渲染,使用SAB在子线之间实现通信以彻底解放主线程。对于Flutter Web,您可以使用多个工人完全分开UI布局,交互式互动性,逻辑,渲染等,并使用SAB实现状态共享。

  SAB与WebGL和WebAssembly的组合相对较好。这两种技术在前端游戏场景中具有更多的应用程序,因此在大型游戏场景中,SAB将非常低迷。

  高计算方案可以尝试多个工人来加快计算速度,使用SAB来实现生产者和消费者之间的通信,一次编写,多次读取。例如,文件MD5计算。

  原始:https://juejin.cn/post/7101608088100143118