vulkan 的顯存管理
-
一個 VkBuffer 對象,多個 offset
- 使用同一塊 VkBuffer 存儲中間層的特征數(shù)據(jù)是整,不同的 blob 使用不同的offset進行區(qū)分 瞧剖。
VkDeviceMemory結(jié)構(gòu)
-
可以在內(nèi)存架構(gòu)方面做到零拷貝
- 集成顯卡和手機上采用
unified
內(nèi)存架構(gòu)(統(tǒng)一內(nèi)存架構(gòu))拭嫁,這種架構(gòu)下可免,GPU可以直接訪問CPU上的主存。利用這種架構(gòu)上的特性做粤,在GPU計算的時候就不用把 內(nèi)存上的數(shù)據(jù) 拷貝到 顯存 上浇借,計算完成后也不需要將 顯存上的數(shù)據(jù) 拷貝到 內(nèi)存。
- 集成顯卡和手機上采用
不同內(nèi)存架構(gòu)的對比
對GPU存儲布局的優(yōu)化
1.[c,h,w] 這種布局不太適合在GPU上做IO:[c, h, w] ---> [c/4, h, w, 4]
- 因為GPU訪問和讀寫顯存用的時候更多的是使用 vec4 的類型驮宴,ncnn 通過將布局改為[c/4,h,w,4]逮刨,使得GPU的IO效率得到大幅度提升
-
減少內(nèi)存帶寬的需求
- ncnn 中的 Tensor float數(shù)據(jù)可以使用半精度
- 在一些不直接支持 fp16 存儲的情況下呕缭,ncnn 使用 packHalf2x16 和unpackHalf2x16 來模擬 fp16 和 fp32 的轉(zhuǎn)換(這兩個函數(shù)是 GLSL 內(nèi)置的函數(shù))
-
更加方便的維護代碼
-
ncnn 中創(chuàng)建了一個 GLSL 的宏堵泽。
所以寫代碼的時候可以不用管類型上的事,運行時會自動轉(zhuǎn)換為設(shè)備支持的 fp32和 fp16 的對應(yīng)代碼
-
cpu-gpu 混合推理
- 模型中有些層恢总,在沒有GPU實現(xiàn)的時候迎罗,我們需要自動切換到CPU上去做推理。這就涉及到存儲布局相互轉(zhuǎn)換
CPU和GPU轉(zhuǎn)換
- ncnn 提供了一套pipline片仿,使用一套pipline實現(xiàn)端到端的完成 最佳的布局轉(zhuǎn)換纹安。在獨顯上也傾向使用 fp16 做上傳和下載,能用半精度砂豌,也會優(yōu)先使用厢岂。
并行推理
-
ncnn 在GPU上實現(xiàn)并行推理的方式。
-
Vulkan的api中同一塊gpu會暴露多個隊列阳距。
例如:nvidia的gpu中有8個隊列塔粒,那么就可以使用多線程的方式同時在8個隊列上提交8個任務(wù)。
好處:可以增加GPU的使用率 筐摘,從而提高效率卒茬。
-
11個任務(wù)同時在三塊gpu上做推理
GLSL->SPIR-V 運行編譯
- 原因:有些驅(qū)動需要對 GLSL 或者 SPIR-V 的源代碼進行特殊的處理,所以只能采用運行時編譯
- 好處:不需要在離線時編譯多個 SPIR-V 的二進制文件咖熟,減少二進制文件的體積圃酵。
Swiftshader
- swiftshader項目地址:google在cpu上實現(xiàn) vulkan驅(qū)動 的項目,可以實現(xiàn)在cpu上執(zhí)行vulkan的代碼馍管,可以保證每次代碼運行結(jié)果都是一致的郭赐。
復(fù)用 VkPipeline 和相關(guān)的 vulkan object
- 模型加載的時候, 特別是第一次加載模型的時候确沸,由于沒有離線的cache和優(yōu)化的手段堪置, pipeline的編譯是一個十分耗時的操作。
- 有些模型層的參數(shù)(kernal size, stride)是一樣的张惹。ncnn 在運行時就將 層的參數(shù) 和 vulkan對象 的關(guān)系記錄下來舀锨,當遇到具有相同參數(shù)層的時候,就可以直接復(fù)用之前創(chuàng)建好的 vulkan對象宛逗,這樣可以顯著降低第一次加載模型的耗時坎匿。
降低第一次加載模型的耗時