案例簡介
騰訊廣告的開發(fā)人員改進(jìn)了視頻抽幀的實(shí)現(xiàn)方式,使得全流程的操作均在 GPU 上完成,取代了原有的 CPU 抽幀流程,提高了性能,并降低了成本。
本案例涉及 GPU 加速的視頻解碼與圖像處理
線上測(cè)試集顯示,單個(gè) GPU 的視頻解碼算力與 8 個(gè) CPU 核大致相當(dāng)
GPU 做圖像處理比 CPU 更有性能和成本優(yōu)勢(shì),尤其與 GPU 視頻解碼聯(lián)合使用時(shí)
本案例使用了 NVIDIA T4 GPU 以及相關(guān)軟件
客戶簡介及應(yīng)用背景
視頻已成為內(nèi)容和廣告的主要媒介形式,但目前的視頻內(nèi)容理解或?qū)徍说?AI 能力,主流依然是先抽幀,再基于圖像幀做特征提取和預(yù)測(cè)。
騰訊廣告部門日常處理大量的視頻信息,而抽幀是視頻分析的第一步。抽幀由于步驟多、計(jì)算重,在視頻 AI 推理場(chǎng)景很容易成為性能瓶頸。
客戶挑戰(zhàn)
在騰訊廣告的流量中,視頻所占比例逐年快速提升,視頻抽幀這里如果出現(xiàn)時(shí)耗或吞吐瓶頸(特別是針對(duì)高 FPS 抽幀的情況),很容易影響到后續(xù)的特征提取以及模型預(yù)測(cè)性能。在當(dāng)前的廣告視頻 AI 推理服務(wù)中,抽幀往往占據(jù)了其中大部分時(shí)耗,因此,視頻抽幀的性能對(duì)于視頻內(nèi)容理解服務(wù)的時(shí)耗和整體資源開銷,有著舉足輕重的地位。
視頻抽幀的幾個(gè)步驟,計(jì)算量非常大,傳統(tǒng)的 CPU 方式抽幀往往受限于 CPU 整體的計(jì)算吞吐,很難滿足低時(shí)延高性能要求。因此,使用 GPU 加速等手段,來對(duì)視頻抽幀做極致的性能優(yōu)化是必然。
應(yīng)用方案
NVIDIA GPU 具備單獨(dú)的硬件編解碼計(jì)算單元,從早期發(fā)布的 Maxwell 架構(gòu)到最新的 Ampere 架構(gòu),都有完善的 API 支持,并且 GPU 上為數(shù)眾多的 CUDA 核心也特別適用于圖像數(shù)據(jù)并行處理加速。目前廣泛使用的推理芯片 NVIDIA T4 GPU,包含兩個(gè)獨(dú)立于 CUDA 的解碼單元,且支持大部分主流的視頻格式,是本案例的應(yīng)用型號(hào)。
視頻抽幀流程大體上包括以下幾個(gè)步驟:視頻解碼、幀色彩空間轉(zhuǎn)換、落盤方式的 JPEG 編碼,如果非落盤,則對(duì)解碼出來的視頻幀做預(yù)處理,然后交給模型進(jìn)行特征提取或預(yù)測(cè)。
其中幀色彩空間轉(zhuǎn)換、JPEG 編碼都涉及像素級(jí)別計(jì)算,非常適合使用 GPU CUDA kernel 來做并行計(jì)算加速。此外,視頻解碼后得到的幀都是未經(jīng)壓縮的原始數(shù)據(jù),數(shù)據(jù)量很大,如果解碼是在 CPU 上進(jìn)行,或者 GPU 解碼后自動(dòng)傳回了 CPU,則需要頻繁做 device(顯存)與 host(主存)之間的原始幀數(shù)據(jù)來回拷貝,IO 時(shí)耗長且數(shù)據(jù)帶寬擁塞,導(dǎo)致時(shí)延明顯增加。 因此,該方案的主要目標(biāo)是盡可能減少 host 與 device 間的數(shù)據(jù) IO 交換,做到抽幀過程全流程 GPU 異構(gòu)計(jì)算,充分利用 NVIDIA GPU 自帶的硬件解碼單元 NVDEC,最大限度減少視頻解碼對(duì)于 CPU 以及 GPU CUDA 核心占用的同時(shí),盡可能低延時(shí)、高吞吐地處理視頻抽幀以及后續(xù)的模型推理。
具體來說,本方案主要從計(jì)算和 IO 兩個(gè)方面著手,解碼部分充分利用了 GPU 通常閑置的 NVDEC 解碼器,其他步驟以像素或像素塊計(jì)算為主,因此使用 CUDA kernel 做并行加速。IO 方面,由于中間過程是原始幀,GPU 數(shù)據(jù)帶寬有限,該方案實(shí)現(xiàn)了全流程 CPU 和 GPU 無幀數(shù)據(jù)交換,最大程度提升性能和吞吐,確保視頻 AI 推理服務(wù)的 GPU 利用率。
計(jì)算優(yōu)化
1. 硬解碼
當(dāng)前線上主力的 GPU 推理卡 T4、P40,以及后續(xù)即將升級(jí)的 A 系列,主流的視頻編碼格式基本都已支持,各卡型支持的具體格式如下:
調(diào)用 GPU 硬解碼主要有兩種方式,一種是直接使用 NVIDIA 官方提供的 Video Codec SDK,另一種方式是使用 FFmpeg,其已經(jīng)封裝了對(duì) GPU 硬解碼的支持??紤]到目前 T4 GPU 對(duì)視頻格式的支持還不夠完善,因此本文使用的是 FFmpeg 方式,如果遇到 GPU 不支持的視頻格式,只需修改解碼器類型即可快速降級(jí)到 CPU 解碼方案,CPU 和 GPU 兩種模式抽幀的代碼邏輯也較為統(tǒng)一。
以下分別以 FFmpeg CPU 4、8、16 線程,以及 GPU 硬解碼方式,抽取線上 100 個(gè)廣告視頻做離線測(cè)試,平均時(shí)耗對(duì)比如下(CPU 為 2020 年發(fā)布的主流服務(wù)器 CPU):
注:視頻平均大小約 15M,平均時(shí)長 26s,大部分為 720P 視頻;FFmpeg 建議最大解碼線程數(shù) 16
分配給 GPU 模型推理服務(wù)的 CPU 核數(shù)一般不會(huì)太多,因此以 FFmpeg 8 線程、2 worker(在本文中是指單進(jìn)程多實(shí)例的方式)做性能壓測(cè),1000 個(gè)廣告視頻測(cè)試數(shù)據(jù)如下:
由此可見,在 GPU 線上推理環(huán)境,如果充分利用 T4 GPU 2 個(gè) NVDEC 硬件解碼模塊,可在幾乎不影響線上服務(wù) CPU、CUDA 原有 workloads 計(jì)算的情況下,額外增加一倍解碼算力,抽幀 QPS 可在原有基礎(chǔ)上翻倍。此處應(yīng)注意,不同架構(gòu) GPU 所附帶的 NVDEC 硬解模塊數(shù)不同,并且 NVDEC 不支持外部再用多線程操作解碼,應(yīng)當(dāng)根據(jù) NVDEC 模塊數(shù)選擇正確的多實(shí)例多 worker 進(jìn)行解碼。例如 T4 GPU 有 2 個(gè) NVDEC 硬解碼模塊,如果只用單實(shí)例,則硬解模塊利用率將不會(huì)超過 50%。如果服務(wù)對(duì)吞吐的要求高于時(shí)延,則此處 GPU 硬解碼的 worker 數(shù)可以設(shè)為大于 n,充分壓榨硬件解碼模塊。
2. CUDA 色彩空間轉(zhuǎn)換
視頻解碼后得到的幀為 YUV 格式,而通常模型預(yù)測(cè)或其他后續(xù)處理一般需要 RGB/BGR 像素格式,因此需要做一次色彩空間轉(zhuǎn)換,將 YUV 幀轉(zhuǎn)換為模型需要的 RGB 格式。傳統(tǒng)方式是調(diào)用 FFmpeg 的 swscale 模塊來實(shí)現(xiàn),但是該方式只支持在 CPU 進(jìn)行計(jì)算,需要做一次 device 到 host 的數(shù)據(jù) IO,并且非常消耗 CPU 資源,計(jì)算并行度也不高。統(tǒng)計(jì)發(fā)現(xiàn),swscale 計(jì)算耗時(shí)占比接近 40%。
YUV 到 RGB 格式的轉(zhuǎn)換是 3×3 的常量矩陣與 YUV 三維向量相乘,即逐像素地將明度 Y、色度 U、濃度 V 三個(gè)分量按公式線性變換為 R、G、B 三色值(這里的常量矩陣的值取決于視頻所采用的顏色標(biāo)準(zhǔn),比如 BT.601/BT.709/BT.2020,可參見 Video Codec SDK 里面的示例),因此可以很方便地將計(jì)算過程改為一維或二維線程塊的 CUDA kernel 調(diào)用,充分利用 GPU 數(shù)以千記的 CUDA 核心并行計(jì)算來做提速。
**性能:**對(duì)線上 100 個(gè)廣告視頻做性能對(duì)比評(píng)測(cè),CUDA kernel 調(diào)用相對(duì)于 CPU 的 swscale 方式平均提速在 20 倍以上,并且視頻清晰度越高,優(yōu)勢(shì)越明顯。
3. CUDA JPEG 編碼
如果是在視頻預(yù)處理等場(chǎng)景,則需要對(duì)抽幀結(jié)果做 JPEG 編碼后再落盤保存。JPEG 編碼具體流程如下:
雖然不同于色彩空間轉(zhuǎn)換的逐像素操作,但也是將整張圖片劃分為 8×8 像素的小分塊分別進(jìn)行離散余弦變換、量化、Huffman 編碼等處理,同樣非常適合用 GPU CUDA core 計(jì)算單元來做并行加速。NVIDIA 從 CUDA Toolkit 10 開始也已經(jīng)封裝了 nvJPEG 模塊提供 JPEG 編碼能力。
需要說明的是,使用 GPU 做 JPEG 編碼,與 CPU JPEG 編碼存在一定比例的像素差異。確保 JPEG 文件頭中各項(xiàng)參數(shù)一致的情況下(壓縮質(zhì)量、量化表、Huffman 表均相同),實(shí)測(cè)像素差異比在 0.5% 左右。由于 JPEG 編碼為有損壓縮,因此解碼后依然存在像素差異,有可能導(dǎo)致模型給出的預(yù)測(cè)結(jié)果存在偏差。例如 OCR 的目標(biāo)檢測(cè)模塊,分別使用 CPU 和 GPU 編碼的 JPEG 圖像作為輸入,預(yù)測(cè)得到的檢測(cè)框坐標(biāo)值在部分 case 上存在一定偏差,從而有概率導(dǎo)致文字識(shí)別結(jié)果出現(xiàn)不一致。一種可行的解決方案,是模型訓(xùn)練也使用 GPU JPEG 編碼的圖片作為輸入,保證模型訓(xùn)練和推理的輸入一致性,從而確保模型推理效果。
**性能:**實(shí)測(cè)線上 1000 個(gè)廣告視頻,CUDA 方式 JPEG 編碼約有 15~20 倍性能提升,同樣清晰度越高性能優(yōu)勢(shì)越大:
IO優(yōu)化
FFmpeg 使用 GPU 硬解碼后,得到的視頻幀格式為 AV_PIX_FMT_NV12,通過 NVIDIA 提供的 cudaPointerGetAttributes API 做指針類型檢查,為 Host 端內(nèi)存指針。也就是說調(diào)用 NVDEC 模塊解碼后,默認(rèn)對(duì)視頻幀做了一次 device 到 host 的傳輸。
由于這里的視頻幀均為未壓縮的原始像素幀,且原始視頻的所有 FPS 幀都會(huì)做該處理,會(huì)占用大量 GPU 與 host 端內(nèi)存的數(shù)據(jù)帶寬。若有辦法做到 GPU 硬解后的視頻幀,不默認(rèn)傳回到 host 端,而是直接緩存在顯存等待后續(xù)計(jì)算,則可以無縫對(duì)接后續(xù)的模型推理或 JPEG 落盤,省去 device 與 host 端的來回兩次數(shù)據(jù)交換時(shí)耗,且大幅減輕 GPU 與 CPU 間的數(shù)據(jù) IO 吞吐壓力。
為此,可使用 FFmpeg 的 hwdevice 相關(guān)接口,直接得到顯存中的視頻幀。這樣得到的視頻幀格式變?yōu)?AV_PIX_FMT_CUDA,且 Y 和 UV plane 的 data linesize 也由 1088 變?yōu)?1280,使用時(shí)需要注意。此時(shí)使用 cudaPointerGetAttributes 檢查 frame data 指針類型,已經(jīng)是 device 端指針,由此打通了全流程異構(gòu)抽幀的關(guān)鍵一環(huán)。
通過 NVIDIA Nsight Systems 抓取到的性能數(shù)據(jù)可見,cudaMemcpy 由之前的 DtoH & HtoD 來回傳輸變?yōu)橐淮物@存內(nèi)部的 DtoD,時(shí)耗由 173ms x 2 變?yōu)?25ms,吞吐也有不少提升。此外,CUDA kernel 計(jì)算時(shí)間片的連續(xù)性也得到不少改善。
**性能:**實(shí)測(cè)線上 1000 個(gè)廣告視頻,整體性能相較于非硬件緩沖區(qū)方式有 25% 左右的提升,GPU 硬解碼器 NVDEC 資源利用率提升約 30%。
工程優(yōu)化
本文以介紹 GPU 全流程抽幀方案為主,過程中為了把性能做到極致也涉及到一些工程優(yōu)化:
通過顯存預(yù)分配+復(fù)用、AVHWDeviceContext 緩沖區(qū) & JPEG 編碼器復(fù)用等手段,單次抽幀時(shí)耗可再優(yōu)化百 ms 級(jí)別。
將 NVDEC 硬解碼、色彩空間轉(zhuǎn)換、JPEG 編碼、模型推理等步驟,利用 CUDA 多流,并對(duì)每個(gè)環(huán)節(jié)做 Pipeline overlap 并行化處理,可充分釋放每個(gè)步驟的最大計(jì)算性能,進(jìn)一步提升計(jì)算吞吐和資源利用率。
目前有不少算法服務(wù)是基于 Python 進(jìn)行開發(fā)&部署,本方案為保障高性能,使用 C++ 開發(fā)。通過 pybind11 基于 C++ 封裝 Python 抽幀 API,保障算法開發(fā)部署的靈活性與效率的同時(shí),確保高性能的抽幀能力。
不落盤方式,對(duì)接模型推理之前一般需要先做預(yù)處理操作,如果要做到全流程 GPU,需要將預(yù)處理改寫為 CUDA kernel 調(diào)用。這里可以將常用的 CV 類預(yù)處理操作封裝為 CUDA 基礎(chǔ)函數(shù)庫,也可以使用 NVIDIA 已經(jīng)封裝好的 NPP 模塊、DALI 預(yù)處理加速框架等方案。
使用效果及影響
全流程時(shí)耗對(duì)比:
相較于 CPU 8 線程解碼,全流程有一倍左右的速度優(yōu)勢(shì),并且由于幾乎不占用 PCIe 數(shù)據(jù)帶寬,對(duì)模型推理等 device&host 間數(shù)據(jù) IO 基本無影響,在吞吐上也有不少提升。
相較于 Python 算法常用的 ffmpeg-python 方式,有數(shù)倍性能提升。
視頻抽幀優(yōu)化是視頻 AI 推理優(yōu)化中的重要一環(huán),本方案從 GPU 硬件加速的角度出發(fā),分別針對(duì)抽幀各步驟做性能分析&計(jì)算優(yōu)化,解決了中間過程大數(shù)據(jù)量的原始視頻幀 host 與 device 端數(shù)據(jù) IO 交換問題,避免 GPU 與 CPU 間的 PCI-E 數(shù)據(jù)帶寬瓶頸,真正做到全流程 GPU 異構(gòu)抽幀。基于此,可在 GPU 無縫對(duì)接后續(xù)的模型推理(不落盤)以及 JPEG 編碼(落盤)兩種主流的抽幀使用場(chǎng)景,是實(shí)現(xiàn)全流程 GPU 視頻 AI 推理能力的先決條件。同時(shí),充分利用了 GPU 推理環(huán)境通常閑置的 NVDEC 解碼芯片,對(duì)于整體服務(wù)時(shí)耗、吞吐,以及硬件資源利用率均有不錯(cuò)的提升,降低了視頻 AI 推理服務(wù) GPU/CPU 算力成本,在算力緊缺的 AI2.0 時(shí)代有著非常重要的意義。
目前該方案已在騰訊廣告多媒體 AI 的視頻人臉服務(wù)落地,解決了其最主要的抽幀性能瓶頸,滿足廣告流水對(duì)于服務(wù)的性能要求。更多視頻 AI 算法,特別是高 FPS 抽幀場(chǎng)景也在逐步接入優(yōu)化中。
“目前該方案已在騰訊廣告多媒體 AI 的視頻人臉服務(wù)落地,解決了其最主要的抽幀性能瓶頸,滿足廣告流水實(shí)時(shí)處理對(duì)于服務(wù)的性能要求。更多騰訊內(nèi)部視頻 AI 算法,特別是高 FPS 抽幀場(chǎng)景也在逐步接入優(yōu)化中。后續(xù),我們還將與英偉達(dá)一起,探索視頻抽幀與模型推理的最佳結(jié)合方式,力求實(shí)現(xiàn)視頻AI推理的極致性能?!?/p>
向乾彪,騰訊廣告AI工程架構(gòu)師,GPU視頻抽幀項(xiàng)目負(fù)責(zé)人
審核編輯:郭婷
-
cpu
+關(guān)注
關(guān)注
68文章
10882瀏覽量
212236 -
gpu
+關(guān)注
關(guān)注
28文章
4754瀏覽量
129074 -
AI
+關(guān)注
關(guān)注
87文章
31155瀏覽量
269494
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論