加入收藏 | 设为首页 | 会员中心 | 我要投稿 甘南站长网 (https://www.0941zz.com/)- 科技、行业物联网、开发、云计算、云管理!
当前位置: 首页 > 综合聚焦 > 服务器 > 正文

吞吐提升30倍:CV流水线迈向全栈并行化

发布时间:2023-02-14 14:18:44 所属栏目:服务器 来源:互联网
导读:斯坦福教授、Tcl 语言发明者 John Ousterhout 曾写过一本书《软件设计的哲学》,系统讨论了软件设计的通用原则和方法论,整书的核心观点是:软件设计的核心在于降低复杂性。 实际上,这个观点也适用于涉及底层硬件适配的软件设计。 具体而言,在传统的图像处
       斯坦福教授、Tcl 语言发明者 John Ousterhout 曾写过一本书《软件设计的哲学》,系统讨论了软件设计的通用原则和方法论,整书的核心观点是:软件设计的核心在于降低复杂性。
 
  实际上,这个观点也适用于涉及底层硬件适配的软件设计。
 
  具体而言,在传统的图像处理流程中,前后处理部分通常都是用 CPU 进行操作的,这会导致整个流程中 50% 到 90% 以上的工作负荷都和前后处理相关,从而它们会成为整个算法流程的性能瓶颈。
 
  01 主流CV库的局限性
 
  上述问题是目前市面上的主流 CV 库在应用场景上的主要局限性,也就是说,对底层硬件依赖的不一致性导致了复杂性和性能瓶颈。正如 John Ousterhout 总结复杂性原因时所道:复杂性源于依赖性。  
 
  主流的图像处理库 OpenCV,其应用场景非常广泛,但在实际使用的时候也会面临一些问题。
 
  要想减少推理场景的耗时,提高推理场景的性能,一般会用 OpenCV 的 GPU 版本进行加速。
 
  但是 OpenCV 的 CPU 版本和 GPU 版本之间可能会出现结果不一致的情况。典型的例子是 resize 算子,其在 CPU 版本和 GPU 版本上对于差值的计算方式是不一致的。
 
  OpenCV 在训练和推理的时候会使用不同版本的算子,在训练的时候一般用 CPU,因为其 CPU 算子覆盖度比较高,在推理的时候一般用 GPU,因为性能比较好。因此,这也会导致结果对齐的问题。也就是说,当用 CPU 做模型训练,并用 GPU 做模型推理的时候,会导致最终的输出结果无法对齐。
 
  其次,部分 GPU 算子的性能会有所退化。在 OpenCV 中,部分 GPU 算子本身的耗时比较大,从而导致整个算子的性能回退,甚至差于 CPU 版本。
 
  第三,OpenCV 的 GPU 算子覆盖度是有限的,部分算子只有 CPU 版本。还有一些 GPU 算子在参数、数据类型等方面的覆盖度也没有 CPU 版本高,从而带来使用上的限制。
 
  最后,如果在使用中将 CPU 算子和 GPU 算子交互使用,就会带来大量的 CPU 和 GPU 之间的数据拷贝和同步操作,进而导致整体的加速性能不够高。
 
  以上就是目前的主流 CV 库的局限性。
 
  02 统一 CV 流水线
 
  既然前后处理的性能瓶颈主要在于使用 CPU 计算,而模型计算阶段使用 GPU 的技术已经越来越成熟。
 
  那么,一个很自然的解决方案是,用 GPU 对前后处理进行加速,对整个算法流水线将会有非常大的性能提升。
 
  为此,NVIDIA英伟达携手字节跳动开源了图像预处理算子库 CV-CUDA。CV-CUDA 能高效地在 GPU 上运行,算子速度能达到 OpenCV 的百倍左右。
 
  将整个流程迁移到 GPU 上后,对于整个流水线可以带来近 30 倍的提升,从而节省计算开销,降低运营成本。
 
  通过图中数据对比可以看到,在相同的服务器和参数配置下,对于 30fps 1080p 视频流,OpenCV 最多可以开 2-3 个并行流,PyTorch(CPU)最多可以开 1.5 个并行流,而 CV-CUDA 最多可以开 60 个并行流。可以看出整体性能提升程度非常大,涉及到的前处理算子有 resize、padding、normalize 等,后处理算子有 crop、resize、compose 等。
 
  03 异步化
 
  为什么 GPU 可以适配前后处理的加速需求?得益于模型计算与前后处理之间的异步化,并与 GPU 的并行计算能力相适应。  
 
  我们以模型训练和模型推理的预处理异步化分别进行说明。
 
  1. 模型训练的预处理异步化 
 
  模型训练可以分为两部分,第一个是数据准备,第二个是模型计算。
 
  目前主流的机器学习框架,比如 PyTorch、TensorFlow,它们在数据准备和模型计算之间是异步的。以 PyTorch 为例,其会开启多个子进程进行数据的准备。
 
  如图中所示,其包含两个状态,即模型计算和数据准备,两者存在时间先后关系,比如当 D0 完成之后,就可以进行 B0,以此类推。
 
  从性能角度看,我们期望数据准备的速度能够跟得上模型计算的速度。但实际情况中,一些数据读取和数据预处理过程的耗时很长,导致相应的模型计算在进行前有一定的空窗期,从而导致 GPU 利用率下降。
 
  数据准备可以分成数据读取和数据预处理,这两个阶段可以串行执行,也可以并行执行,比如在 PyTorch 的框架下是串行执行的。
 
  影响数据读取的性能因素有很多,比如数据存储介质、存储格式、并行度、执行进程数等。
 
  相比之下,数据预处理的性能影响因素比较简单,就是并行度。并行度越高,数据预处理的性能越好。也就是说,让数据预处理与模型计算异步化,并提高数据预处理的并行度,可以提高数据预处理的性能。
 
  2. 模型推理的预处理异步化  
 
  在模型推理阶段,其性能有两个指标,第一个是吞吐,第二个是延时。一定程度上,这两个指标是彼此互斥的。
 
  对于单个 query 而言,当 server 接收到数据之后,会进行数据的预处理,再进行模型推理。所以对于单个 query 而言,一定程度上它是一个串行的过程。
 
  但这样做在效率上是很低的,会浪费很多计算资源。为了提高吞吐量,很多推理引擎会采用和训练阶段一样的策略,将数据准备和模型计算异步化。在数据准备阶段,会积累一定量的 query,组合成一个 batch,再进行后续的计算,以提高整体的吞吐量。
 
  从吞吐而言,模型推理和模型训练是比较类似的。把数据预处理阶段从 CPU 搬到 GPU 上,可以得到吞吐上的收益。
 
  同时,从延时的角度上看,对于每条 query 语句,如果能够减少预处理过程所花费的时间,对于每条 query 而言,其延时也会得到相应的缩短。
 
  模型推理还有一个特点是,其模型计算量比较小,因为只涉及前向计算,不涉及后向计算。这意味着模型推理对数据预处理的需求更高。
 
  3. 核心问题:CPU 资源竞争
 
  假设有足够的 CPU 资源用于计算,理论上预处理不会成为性能瓶颈。因为一旦发现性能跟不上,只需要增加进程做预处理操作即可。
 
  因此,只有当 CPU 出现资源竞争的时候,数据预处理才可能成为性能瓶颈。
 
  在实际业务中,CPU 资源竞争的情况是很常见的,这会导致后续训练和推理阶段中 GPU 利用率降低,进而训练速度降低。
 
  随着 GPU 算力不断增加,可以预见,对数据准备阶段的速度要求会越来越高。
 
  为此,将预处理部分搬上 GPU,来缓解 CPU 资源竞争问题,提高 GPU 利用率,就成了很自然的选择。
 
  总体而言,这种设计降低了系统的复杂性,将模型流水线的主体与 GPU 直接适配,对于提高 GPU 和 CPU 的利用率都能带来很大的助益。同时,它也避免了不同版本之间的结果对齐问题,减少了依赖性,符合 John Ousterhout 提出的软件设计原则。
 
  1. 硬件
 
  硬件方面,CV-CUDA 基于 GPU 的并行计算能力,能够大幅提高前后处理的速度和吞吐,减少模型计算的等待时间,提高 GPU 的利用率。
 
  CV-CUDA 支持 Batch 和 Variable Shape 模式。Batch模式支持批处理,可以充分发挥 GPU 的并行特性,而 OpenCV 不管是 CPU 还是 GPU 版本都只能对单张图片进行调用。
 
  Variable Shape 模式是指在一个batch当中,每张图片的长和宽可以不一样。网络上的图片一般长宽都是不一致的,主流框架的做法是把长和宽分别 resize 到同一个大小,再对同一长宽的图片打包为一个 batch,再对 batch 进行处理。CV-CUDA 可以直接把不同长和宽的图像直接放在一个 batch 中进行处理,不仅能提升效率,使用上也很方便。
 
  Variable Shape 的另外一层含义是在对图像进行处理的时候,可以指定每张图片的某些参数,比如 rotate,对一个batch的图像可以指定每张图片的旋转角度。
 
  2. 软件
 
  软件方面,CV-CUDA 开发了大量的软件优化方法来做进一步的优化,包括性能优化(比如访存优化)和资源利用优化(比如显存预分配),从而可以高效地运行在云端的训练和推理场景中。
 
  首先是显存预分配设置。OpenCV在 调用 GPU 版本的时候,部分算子会在内部执行 cudaMalloc,这会导致耗时大量增加。在 CV-CUDA 中,所有的显存预分配都是在初始化阶段执行,而在训练和推理阶段,不会进行任何显存分配操作,从而提高效率。
 
  其次,所有的算子都是异步操作的。CV-CUDA 对大量kernel进行了融合,从而减少 kernel 的数量,进而减少 kernel 的启动时间以及数据拷贝擦做,提高整体运行的效率。
 
  第三,CV-CUDA 还对访存进行了优化,比如合并访存、向量化读写等,提高带宽的利用率,还利用 shared memory 来提高访存读写效率。
 
  最后,CV-CUDA 在计算上也做了很多优化,比如 fast math、warp reduce/block reduce 等。
 
  3. 算法
 
  算法方面,CV-CUDA 的算子都是独立设计的、定制化的,从而可以支持非常复杂的逻辑实现,并且方便进行使用和调试。
 
  如何理解独立设计?图像处理库的算子调用有两种形式,一种是整体性的 pipeline 形式,只能获取 pipeline 的结果,比如 DALI,另一种是模块化的独立算子的形式,可以获取每一个算子的单独结果,比如 OpenCV。CV-CUDA 采用了和 OpenCV 相同的调用形式,在使用和调试上会比较方便。

(编辑:甘南站长网)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

推荐文章
    热点阅读