@
happinessnch #40
你的发言佐证了我一直以来的一种观点:专业人士很容易把自己的知识技能经验外扩到自己的专业之外,而现如今是一个“专业割裂”严重的时代,同领域同场景下看似同专业实则不然的情况太常见了,很反直觉是吧。
我们看看视频编码本质上是个什么运算,在如今仍然主流的基于像素块的编码思路下,无非是在二维矩阵上搜索、跟踪大量的像素群是如何随着时间移动、变化的,并将这些运动用尽量少的数据描述出来。这部分工作就是运动预测、运动补偿,除此之外的工作,诸如量化、采样、宏块切割、滤波、频域转换、熵编码(无损压缩)等等基础计算虽然同样重要但并不是影响最终编码效率的关键因素、或者虽然也是关键因素但影响比重明显小于运动预测。
那么,如何高效、准确地进行运动预测?最理想的情况是,在整个画面上进行搜索跟踪。但这就带来一个“负担”,这么大的矩阵是要占用不小的运行内存空间的。
如果是 CPU 跑编码算法无所谓,大把的内存条,空间管够,反正就一个计算单元,整个图像范围由它承包。一个运算单元负责一整个图像,而且现代 CPU 性能强劲,在强力的分支预测加持下,视频编码算法中大量的分支条件判断压根不算个事。
GPU 就惨了,一堆运算单元,却个个都是残废,光是一个 if-else 就够这些单元跑半天了,这些单元也就只能胜任某几种模式的数值计算罢了,让它们进行复杂逻辑判断真是要了亲命。那么单核心这么弱,是不是得把工作分一分啊,那肯定啊。那咋分呢,每个 GPU 运算单元该复杂多大范围呢?负责的范围太小的话不行,这个单元上一帧还能看到画面上那只手呢,下一帧这手直接移动到它负责的范围外面了,它直接懵逼,顺带它隔壁的单元也跟着懵逼,咋我的工作区里多出来一只手,我之前跟踪着的那只眼镜呢?那么范围大一点行不,要不干脆直接大到一整个画面范围得了,反正一帧画面是大家共用的嘛,又不用多占空间?不行,画面数据是公用的,但每个单元负责的任务数据是独有的,每个单元负责的范围越大,它要分辨的数据量就越大,它能识别并跟踪的目标就越大,目标运动范围、运动模式就更复杂,记录这些数据需要的空间就更更更大,字面意义上的几何式增长。显存一共才多大啊,就算它 8 个 G ?几百几千个核心等着分呢,笑死,根本不够分。
那取个折中行不,也不整个画面范围了,也不弄得范围太小,取个过得去的中间值吧。行,事实上也是这么做的,然而即使如此,一旦跑起来,依然是内存直接拉满,但核心却依然有大量空闲。真没办法了,每个核心负责的单元不能太小,不然动不动丢失跟踪目标,没法玩。就这样,整个画面依然被划分成了不少的范围,一旦动起来,依然频繁出现目标从一个区块跑到另一个的情况,引起巨大的编码浪费,它们要真是直接瞬移过去的还好,关键是一帧一帧慢慢过去,中间的每一帧上,这些区块的边界上充斥了各种不完整的目标碎片,严重的能造成几倍的编码浪费。
所以大家都懂,都会玩,说是 GPU 硬件加速编码,其实负责编码运算的根本不是 GPU 核心,而是另外单独的专用视频编解码电路,核心撑死了帮忙做一些外围辅助工作,视频编码的大头工作全交给专用电路,intel qsv ,nvenc ,诸如此类,概莫如是。
所以,你当初参与的那个项目的那个算法,大概率是一个本来就针对并行计算特性所设计的模式,压根就不能发挥出 CPU 这种能胜任复杂任务的强力运算单元的优势,而更偏向于照顾 GPU 单元这种多而弱的运算架构,而且既然已经是针对并行运算所设计的视频编码算法,毫无疑问逃不开上面所述的“分割画面范围导致跟踪对象丢失引起大量冗余编码”的弱点,那自然是就算用了 CPU 来跑也不会得到更好的画面或者编码效率。