5.1. Object Lifetimes
TensorRT 的 API 是基于類的,其中一些類充當(dāng)其他類的工廠。對(duì)于用戶擁有的對(duì)象,工廠對(duì)象的生命周期必須跨越它創(chuàng)建的對(duì)象的生命周期。例如, NetworkDefinition和BuilderConfig類是從構(gòu)建器類創(chuàng)建的,這些類的對(duì)象應(yīng)該在構(gòu)建器工廠對(duì)象之前銷毀。
此規(guī)則的一個(gè)重要例外是從構(gòu)建器創(chuàng)建引擎。創(chuàng)建引擎后,您可以銷毀構(gòu)建器、網(wǎng)絡(luò)、解析器和構(gòu)建配置并繼續(xù)使用引擎。
5.2. Error Handling and Logging
創(chuàng)建 TensorRT 頂級(jí)接口(builder、runtime 或 refitter)時(shí),您必須提供Logger ( C++ 、 Python )接口的實(shí)現(xiàn)。記錄器用于診斷和信息性消息;它的詳細(xì)程度是可配置的。由于記錄器可用于在 TensorRT 生命周期的任何時(shí)間點(diǎn)傳回信息,因此它的生命周期必須跨越應(yīng)用程序中對(duì)該接口的任何使用。實(shí)現(xiàn)也必須是線程安全的,因?yàn)?TensorRT 可以在內(nèi)部使用工作線程。
對(duì)對(duì)象的 API 調(diào)用將使用與相應(yīng)頂級(jí)接口關(guān)聯(lián)的記錄器。例如,在對(duì)ExecutionContext::enqueue()的調(diào)用中,執(zhí)行上下文是從引擎創(chuàng)建的,該引擎是從運(yùn)行時(shí)創(chuàng)建的,因此 TensorRT 將使用與該運(yùn)行時(shí)關(guān)聯(lián)的記錄器。
錯(cuò)誤處理的主要方法是ErrorRecorde ( C++ , Python ) 接口。您可以實(shí)現(xiàn)此接口,并將其附加到 API 對(duì)象以接收與該對(duì)象關(guān)聯(lián)的錯(cuò)誤。對(duì)象的記錄器也將傳遞給它創(chuàng)建的任何其他記錄器 – 例如,如果您將錯(cuò)誤記錄器附加到引擎,并從該引擎創(chuàng)建執(zhí)行上下文,它將使用相同的記錄器。如果您隨后將新的錯(cuò)誤記錄器附加到執(zhí)行上下文,它將僅接收來(lái)自該上下文的錯(cuò)誤。如果生成錯(cuò)誤但沒有找到錯(cuò)誤記錄器,它將通過關(guān)聯(lián)的記錄器發(fā)出。
請(qǐng)注意,CUDA 錯(cuò)誤通常是異步的 – 因此,當(dāng)執(zhí)行多個(gè)推理或其他 CUDA 流在單個(gè) CUDA 上下文中異步工作時(shí),可能會(huì)在與生成它的執(zhí)行上下文不同的執(zhí)行上下文中觀察到異步 GPU 錯(cuò)誤。
5.3 Memory
TensorRT 使用大量設(shè)備內(nèi)存,即 GPU 可直接訪問的內(nèi)存,而不是連接到 CPU 的主機(jī)內(nèi)存。由于設(shè)備內(nèi)存通常是一種受限資源,因此了解 TensorRT 如何使用它很重要。
5.3.1. The Build Phase
在構(gòu)建期間,TensorRT 為時(shí)序?qū)訉?shí)現(xiàn)分配設(shè)備內(nèi)存。一些實(shí)現(xiàn)可能會(huì)消耗大量臨時(shí)內(nèi)存,尤其是在使用大張量的情況下。您可以通過構(gòu)建器的maxWorkspace屬性控制最大臨時(shí)內(nèi)存量。這默認(rèn)為設(shè)備全局內(nèi)存的完整大小,但可以在必要時(shí)進(jìn)行限制。如果構(gòu)建器發(fā)現(xiàn)由于工作空間不足而無(wú)法運(yùn)行的適用內(nèi)核,它將發(fā)出一條日志消息來(lái)指示這一點(diǎn)。
然而,即使工作空間相對(duì)較小,計(jì)時(shí)也需要為輸入、輸出和權(quán)重創(chuàng)建緩沖區(qū)。 TensorRT 對(duì)操作系統(tǒng)因此類分配而返回內(nèi)存不足是穩(wěn)健的,但在某些平臺(tái)上,操作系統(tǒng)可能會(huì)成功提供內(nèi)存,隨后內(nèi)存不足killer進(jìn)程觀察到系統(tǒng)內(nèi)存不足,并終止 TensorRT 。如果發(fā)生這種情況,請(qǐng)?jiān)谥卦囍氨M可能多地釋放系統(tǒng)內(nèi)存。
在構(gòu)建階段,通常在主機(jī)內(nèi)存中至少有兩個(gè)權(quán)重拷貝:來(lái)自原始網(wǎng)絡(luò)的權(quán)重拷貝,以及在構(gòu)建引擎時(shí)作為引擎一部分包含的權(quán)重拷貝。此外,當(dāng) TensorRT 組合權(quán)重(例如卷積與批量歸一化)時(shí),將創(chuàng)建額外的臨時(shí)權(quán)重張量。
5.3.2. The Runtime Phase
在運(yùn)行時(shí),TensorRT 使用相對(duì)較少的主機(jī)內(nèi)存,但可以使用大量的設(shè)備內(nèi)存。
引擎在反序列化時(shí)分配設(shè)備內(nèi)存來(lái)存儲(chǔ)模型權(quán)重。由于序列化引擎幾乎都是權(quán)重,因此它的大小非常接近權(quán)重所需的設(shè)備內(nèi)存量。
ExecutionContext使用兩種設(shè)備內(nèi)存:
一些層實(shí)現(xiàn)所需的持久內(nèi)存——例如,一些卷積實(shí)現(xiàn)使用邊緣掩碼,并且這種狀態(tài)不能像權(quán)重那樣在上下文之間共享,因?yàn)樗拇笮∪Q于層輸入形狀,這可能因上下文而異。該內(nèi)存在創(chuàng)建執(zhí)行上下文時(shí)分配,并在其生命周期內(nèi)持續(xù)。
暫存內(nèi)存,用于在處理網(wǎng)絡(luò)時(shí)保存中間結(jié)果。該內(nèi)存用于中間激活張量。它還用于層實(shí)現(xiàn)所需的臨時(shí)存儲(chǔ),其邊界由IBuilderConfig::setMaxWorkspaceSize()控制。
您可以選擇通過ICudaEngine::createExecutionContextWithoutDeviceMemory()創(chuàng)建一個(gè)沒有暫存內(nèi)存的執(zhí)行上下文,并在網(wǎng)絡(luò)執(zhí)行期間自行提供該內(nèi)存。這允許您在未同時(shí)運(yùn)行的多個(gè)上下文之間共享它,或者在推理未運(yùn)行時(shí)用于其他用途。 ICudaEngine::getDeviceMemorySize()返回所需的暫存內(nèi)存量。
構(gòu)建器在構(gòu)建網(wǎng)絡(luò)時(shí)發(fā)出有關(guān)執(zhí)行上下文使用的持久內(nèi)存和暫存內(nèi)存量的信息,嚴(yán)重性為 kINFO 。檢查日志,消息類似于以下內(nèi)容:
[08/12/2021-17:39:11] [I] [TRT] Total Host Persistent Memory: 106528 [08/12/2021-17:39:11] [I] [TRT] Total Device Persistent Memory: 29785600 [08/12/2021-17:39:11] [I] [TRT] Total Scratch Memory: 9970688
默認(rèn)情況下,TensorRT 直接從 CUDA 分配設(shè)備內(nèi)存。但是,您可以將 TensorRT 的IGpuAllocator ( C++ 、 Python )接口的實(shí)現(xiàn)附加到構(gòu)建器或運(yùn)行時(shí),并自行管理設(shè)備內(nèi)存。如果您的應(yīng)用程序希望控制所有 GPU 內(nèi)存并子分配給 TensorRT,而不是讓 TensorRT 直接從 CUDA 分配,這將非常有用。
TensorRT 的依賴項(xiàng)( cuDNN和cuBLAS )會(huì)占用大量設(shè)備內(nèi)存。 TensorRT 允許您通過構(gòu)建器配置中的TacticSources ( C++ 、 Python )屬性控制這些庫(kù)是否用于推理。請(qǐng)注意,某些層實(shí)現(xiàn)需要這些庫(kù),因此當(dāng)它們被排除時(shí),網(wǎng)絡(luò)可能無(wú)法編譯。
CUDA 基礎(chǔ)設(shè)施和 TensorRT 的設(shè)備代碼也會(huì)消耗設(shè)備內(nèi)存。內(nèi)存量因平臺(tái)、設(shè)備和 TensorRT 版本而異。您可以使用cudaGetMemInfo來(lái)確定正在使用的設(shè)備內(nèi)存總量。
注意:由于 CUDA 無(wú)法控制統(tǒng)一內(nèi)存設(shè)備上的內(nèi)存,因此cudaGetMemInfo返回的結(jié)果在這些平臺(tái)上可能不準(zhǔn)確。
5.4. Threading
一般來(lái)說,TensorRT 對(duì)象不是線程安全的。預(yù)期的運(yùn)行時(shí)并發(fā)模型是不同的線程將在不同的執(zhí)行上下文上操作。上下文包含執(zhí)行期間的網(wǎng)絡(luò)狀態(tài)(激活值等),因此在不同線程中同時(shí)使用上下文會(huì)導(dǎo)致未定義的行為。 為了支持這個(gè)模型,以下操作是線程安全的:
運(yùn)行時(shí)或引擎上的非修改操作。
從 TensorRT 運(yùn)行時(shí)反序列化引擎。
從引擎創(chuàng)建執(zhí)行上下文。
注冊(cè)和注銷插件。
在不同線程中使用多個(gè)構(gòu)建器沒有線程安全問題;但是,構(gòu)建器使用時(shí)序來(lái)確定所提供參數(shù)的最快內(nèi)核,并且使用具有相同 GPU 的多個(gè)構(gòu)建器將擾亂時(shí)序和 TensorRT 構(gòu)建最佳引擎的能力。使用多線程使用不同的 GPU 構(gòu)建不存在此類問題。
5.5. Determinism
TensorRT builder 使用時(shí)間來(lái)找到最快的內(nèi)核來(lái)實(shí)現(xiàn)給定的運(yùn)算符。時(shí)序內(nèi)核會(huì)受到噪聲的影響——GPU 上運(yùn)行的其他工作、GPU 時(shí)鐘速度的波動(dòng)等。時(shí)序噪聲意味著在構(gòu)建器的連續(xù)運(yùn)行中,可能不會(huì)選擇相同的實(shí)現(xiàn)。
AlgorithmSelector ( C++ , Python )接口允許您強(qiáng)制構(gòu)建器為給定層選擇特定實(shí)現(xiàn)。您可以使用它來(lái)確保構(gòu)建器從運(yùn)行到運(yùn)行選擇相同的內(nèi)核。有關(guān)更多信息,請(qǐng)參閱算法選擇和可重現(xiàn)構(gòu)建部分。
一旦構(gòu)建了引擎,它就是確定性的:在相同的運(yùn)行時(shí)環(huán)境中提供相同的輸入將產(chǎn)生相同的輸出。
關(guān)于作者
Ken He 是 NVIDIA 企業(yè)級(jí)開發(fā)者社區(qū)經(jīng)理 & 高級(jí)講師,擁有多年的 GPU 和人工智能開發(fā)經(jīng)驗(yàn)。自 2017 年加入 NVIDIA 開發(fā)者社區(qū)以來(lái),完成過上百場(chǎng)培訓(xùn),幫助上萬(wàn)個(gè)開發(fā)者了解人工智能和 GPU 編程開發(fā)。在計(jì)算機(jī)視覺,高性能計(jì)算領(lǐng)域完成過多個(gè)獨(dú)立項(xiàng)目。并且,在機(jī)器人和無(wú)人機(jī)領(lǐng)域,有過豐富的研發(fā)經(jīng)驗(yàn)。對(duì)于圖像識(shí)別,目標(biāo)的檢測(cè)與跟蹤完成過多種解決方案。曾經(jīng)參與 GPU 版氣象模式GRAPES,是其主要研發(fā)者。
審核編輯:郭婷
-
NVIDIA
+關(guān)注
關(guān)注
14文章
5026瀏覽量
103288 -
gpu
+關(guān)注
關(guān)注
28文章
4754瀏覽量
129087 -
CUDA
+關(guān)注
關(guān)注
0文章
121瀏覽量
13644
發(fā)布評(píng)論請(qǐng)先 登錄
相關(guān)推薦
評(píng)論