13.1. Measuring Performance
在開(kāi)始使用 TensorRT 進(jìn)行任何優(yōu)化工作之前,必須確定應(yīng)該測(cè)量什么。沒(méi)有衡量標(biāo)準(zhǔn),就不可能取得可靠的進(jìn)展或衡量是否取得了成功
Latency
網(wǎng)絡(luò)推理的性能度量是從輸入呈現(xiàn)給網(wǎng)絡(luò)到輸出可用所經(jīng)過(guò)的時(shí)間。這是單個(gè)推理的網(wǎng)絡(luò)延遲。較低的延遲更好。在某些應(yīng)用中,低延遲是一項(xiàng)關(guān)鍵的安全要求。在其他應(yīng)用程序中,延遲作為服務(wù)質(zhì)量問(wèn)題對(duì)用戶(hù)來(lái)說(shuō)是直接可見(jiàn)的。對(duì)于批量處理,延遲可能根本不重要。
Throughput
另一個(gè)性能測(cè)量是在固定的時(shí)間單位內(nèi)可以完成多少推理。這是網(wǎng)絡(luò)的吞吐量。吞吐量越高越好。更高的吞吐量表明更有效地利用固定計(jì)算資源。對(duì)于批量處理,所花費(fèi)的總時(shí)間將由網(wǎng)絡(luò)的吞吐量決定。
查看延遲和吞吐量的另一種方法是確定最大延遲并在該延遲下測(cè)量吞吐量。像這樣的服務(wù)質(zhì)量測(cè)量可以是用戶(hù)體驗(yàn)和系統(tǒng)效率之間的合理折衷。
在測(cè)量延遲和吞吐量之前,您需要選擇開(kāi)始和停止計(jì)時(shí)的確切點(diǎn)。根據(jù)網(wǎng)絡(luò)和應(yīng)用程序,選擇不同的點(diǎn)可能是有意義的。
在很多應(yīng)用中,都有一個(gè)處理流水線(xiàn),整個(gè)系統(tǒng)的性能可以通過(guò)整個(gè)處理流水線(xiàn)的延遲和吞吐量來(lái)衡量。由于預(yù)處理和后處理步驟在很大程度上取決于特定應(yīng)用程序,因此本節(jié)僅考慮網(wǎng)絡(luò)推理的延遲和吞吐量。
13.1.1. Wall-clock Timing
經(jīng)過(guò)時(shí)間(計(jì)算開(kāi)始和結(jié)束之間經(jīng)過(guò)的時(shí)間)可用于測(cè)量應(yīng)用程序的整體吞吐量和延遲,以及將推理時(shí)間置于更大系統(tǒng)的上下文中。 C++11 在標(biāo)準(zhǔn)庫(kù)中提供了高精度計(jì)時(shí)器。例如,std::chrono::system_clock表示系統(tǒng)范圍的經(jīng)過(guò)時(shí)間,而std::chrono::high_resolution_clock以可用的最高精度測(cè)量時(shí)間。
以下示例代碼片段顯示了測(cè)量網(wǎng)絡(luò)推理主機(jī)時(shí)間:
#includeauto startTime = std::chrono::high_resolution_clock::now(); context->enqueueV2(&buffers[0], stream, nullptr); cudaStreamSynchronize(stream); auto endTime = std::chrono::high_resolution_clock::now(); float totalTime = std::chrono::duration (endTime - startTime).count();
如果設(shè)備上一次只發(fā)生一個(gè)推理,那么這可能是一種簡(jiǎn)單的方法來(lái)分析各種操作所花費(fèi)的時(shí)間。推理通常是異步的,因此請(qǐng)確保添加顯式 CUDA 流或設(shè)備同步以等待結(jié)果可用。
13.1.2. CUDA Events
僅在主機(jī)上計(jì)時(shí)的一個(gè)問(wèn)題是它需要主機(jī)/設(shè)備同步。優(yōu)化的應(yīng)用程序可能會(huì)在設(shè)備上并行運(yùn)行許多推理,并具有重疊的數(shù)據(jù)移動(dòng)。此外,同步本身給定時(shí)測(cè)量增加了一些噪聲。 為了幫助解決這些問(wèn)題,CUDA 提供了一個(gè)事件 API 。此 API 允許您將事件放入 CUDA 流中,這些事件將在遇到事件時(shí)由 GPU 打上時(shí)間戳。然后,時(shí)間戳的差異可以告訴您不同操作花費(fèi)了多長(zhǎng)時(shí)間。
以下示例代碼片段顯示了計(jì)算兩個(gè) CUDA 事件之間的時(shí)間:
cudaEvent_t start, end; cudaEventCreate(&start); cudaEventCreate(&end); cudaEventRecord(start, stream); context->enqueueV2(&buffers[0], stream, nullptr); cudaEventRecord(end, stream); cudaEventSynchronize(end); float totalTime; cudaEventElapsedTime(&totalTime, start, end);
13.1.3. Built-In TensorRT Profiling
深入挖掘推理性能需要在優(yōu)化網(wǎng)絡(luò)中進(jìn)行更細(xì)粒度的時(shí)序測(cè)量。 TensorRT 有一個(gè)Profiler ( C++ , Python ) 接口,您可以實(shí)現(xiàn)該接口以便讓 TensorRT 將分析信息傳遞給您的應(yīng)用程序。調(diào)用時(shí),網(wǎng)絡(luò)將以分析模式運(yùn)行。完成推理后,將調(diào)用您的類(lèi)的分析器對(duì)象以報(bào)告網(wǎng)絡(luò)中每一層的時(shí)間。這些時(shí)序可用于定位瓶頸、比較序列化引擎的不同版本以及調(diào)試性能問(wèn)題。
分析信息可以從常規(guī)推理enqueueV2()啟動(dòng)或 CUDA 圖啟動(dòng)中收集。有關(guān)詳細(xì)信息,請(qǐng)參閱IExecutionContext::setProfiler()和IExecutionContext::reportToProfiler() ( C++ 、 Python )。
循環(huán)內(nèi)的層編譯為單個(gè)單片層,因此,這些層的單獨(dú)時(shí)序不可用。
公共示例代碼 ( common.h ) 中提供了一個(gè)展示如何使用IProfiler接口的示例,然后在位于 GitHub 存儲(chǔ)庫(kù)中的sampleNMT中使用。
您還可以使用trtexec在給定輸入網(wǎng)絡(luò)或計(jì)劃文件的情況下使用 TensorRT 分析網(wǎng)絡(luò)。有關(guān)詳細(xì)信息,請(qǐng)參閱trtexec部分。
13.1.4. CUDA Profiling Tools
推薦的 CUDA 分析器是NVIDIA Nsight? Systems 。一些 CUDA 開(kāi)發(fā)人員可能更熟悉 nvprof 和 nvvp,但是,這些已被棄用。在任何情況下,這些分析器都可以用于任何 CUDA 程序,以報(bào)告有關(guān)在執(zhí)行期間啟動(dòng)的內(nèi)核、主機(jī)和設(shè)備之間的數(shù)據(jù)移動(dòng)以及使用的 CUDA API 調(diào)用的時(shí)序信息。
Nsight Systems 可以通過(guò)多種方式配置,以?xún)H報(bào)告程序執(zhí)行的一部分的時(shí)序信息,或者也可以將傳統(tǒng)的 CPU 采樣配置文件信息與 GPU 信息一起報(bào)告。
僅分析推理階段
分析 TensorRT 應(yīng)用程序時(shí),您應(yīng)該僅在構(gòu)建引擎后啟用分析。在構(gòu)建階段,所有可能的策略都被嘗試和計(jì)時(shí)。分析這部分執(zhí)行將不會(huì)顯示任何有意義的性能測(cè)量,并將包括所有可能的內(nèi)核,而不是實(shí)際選擇用于推理的內(nèi)核。限制分析范圍的一種方法是:
第一階段:構(gòu)建應(yīng)用程序,然后在一個(gè)階段序列化引擎。
第二階段:加載序列化引擎并在第二階段運(yùn)行推理并僅對(duì)第二階段進(jìn)行分析。
如果應(yīng)用程序無(wú)法序列化引擎,或者應(yīng)用程序必須連續(xù)運(yùn)行兩個(gè)階段,您還可以在第二階段周?chē)砑觕udaProfilerStart() / cudaProfilerStop() CUDA API,并在 Nsight Systems 命令中添加-c cudaProfilerApi標(biāo)志以?xún)H配置文件cudaProfilerStart()和cudaProfilerStop()之間的部分。
在 Nsight Systems 中使用 NVTX 跟蹤 啟用NVIDIA 工具擴(kuò)展 SDK (NVTX)跟蹤允許 Nsight Compute 和 Nsight Systems 收集由 TensorRT 應(yīng)用程序生成的數(shù)據(jù)。 NVTX 是一個(gè)基于 C 的 API,用于標(biāo)記應(yīng)用程序中的事件和范圍。
將內(nèi)核名稱(chēng)解碼回原始網(wǎng)絡(luò)中的層可能很復(fù)雜。因此,TensorRT 使用 NVTX 為每一層標(biāo)記一個(gè)范圍,然后允許 CUDA 分析器將每一層與調(diào)用來(lái)實(shí)現(xiàn)它的內(nèi)核相關(guān)聯(lián)。在 TensorRT 中,NVTX 有助于將運(yùn)行時(shí)引擎層的執(zhí)行與 CUDA內(nèi)核調(diào)用相關(guān)聯(lián)。 Nsight Systems 支持在時(shí)間軸上收集和可視化這些事件和范圍。 Nsight Compute 還支持在應(yīng)用程序掛起時(shí)收集和顯示給定線(xiàn)程中所有活動(dòng) NVTX 域和范圍的狀態(tài)。
在 TensorRT 中,每一層都可以啟動(dòng)一個(gè)或多個(gè)內(nèi)核來(lái)執(zhí)行其操作。啟動(dòng)的確切內(nèi)核取決于優(yōu)化的網(wǎng)絡(luò)和存在的硬件。根據(jù)構(gòu)建器的選擇,可能會(huì)有多個(gè)額外的操作對(duì)穿插在層計(jì)算中的數(shù)據(jù)進(jìn)行重新排序;這些重新格式化操作可以作為設(shè)備到設(shè)備的內(nèi)存副本或自定義內(nèi)核來(lái)實(shí)現(xiàn)。
例如,以下屏幕截圖來(lái)自 Nsight Systems。
控制 NVTX 跟蹤中的詳細(xì)程度
默認(rèn)情況下,TensorRT 僅在 NVTX 標(biāo)記中顯示層名稱(chēng),而用戶(hù)可以在構(gòu)建引擎時(shí)通過(guò)設(shè)置IBuilderConfig中的 ProfilingVerbosity 來(lái)控制細(xì)節(jié)級(jí)別。例如,要禁用 NVTX 跟蹤,請(qǐng)將 ProfilingVerbosity 設(shè)置為kNONE :
C++
builderConfig-》setProfilingVerbosity(ProfilingVerbosity::kNONE);
Python
builder_config.profiling_verbosity = trt.ProfilingVerbosity.NONE
另一方面,您可以通過(guò)將ProfilingVerbosity設(shè)置為kDETAILED來(lái)選擇允許 TensorRT 在 NVTX 標(biāo)記中打印更詳細(xì)的層信息,包括輸入和輸出尺寸、操作、參數(shù)、順序編號(hào)等:
C++
builderConfig-》setProfilingVerbosity(ProfilingVerbosity::kDETAILED);
Python
builder_config.profiling_verbosity = trt.ProfilingVerbosity.DETAILED
trtexec運(yùn)行 Nsight 系統(tǒng) 以下是使用trtexec工具收集 Nsight Systems 配置文件的命令示例:
trtexec --onnx=foo.onnx --profilingVerbosity=detailed --saveEngine=foo.plan
nsys profile -o foo_profile trtexec --loadEngine=foo.plan --warmUp=0 --duration=0 --iterations=50
第一個(gè)命令構(gòu)建引擎并將其序列化為foo.plan ,第二個(gè)命令使用foo.plan運(yùn)行推理并生成一個(gè)foo_profile.qdrep文件,然后可以在 Nsight Systems GUI 界面中打開(kāi)該文件以進(jìn)行可視化。
--profilingVerbosity=detailed標(biāo)志允許 TensorRT 在 NVTX 標(biāo)記中顯示更詳細(xì)的層信息,而--warmUp =0 --duration=0 --iterations=50標(biāo)志允許您控制要運(yùn)行的推理迭代次數(shù)。默認(rèn)情況下, trtexec運(yùn)行推理三秒鐘,這可能會(huì)導(dǎo)致輸出 qdrep 文件非常大。
13.1.5. Tracking Memory
跟蹤內(nèi)存使用情況與執(zhí)行性能一樣重要。通常,設(shè)備上的內(nèi)存比主機(jī)上的內(nèi)存更受限制。為了跟蹤設(shè)備內(nèi)存,推薦的機(jī)制是創(chuàng)建一個(gè)簡(jiǎn)單的自定義 GPU 分配器,它在內(nèi)部保留一些統(tǒng)計(jì)信息,然后使用常規(guī) CUDA 內(nèi)存分配函數(shù)cudaMalloc和cudaFree 。
可以為構(gòu)建器IBuilder設(shè)置自定義 GPU 分配器以進(jìn)行網(wǎng)絡(luò)優(yōu)化,并在使用IGpuAllocator API反序列化引擎時(shí)為IRuntime 設(shè)置。自定義分配器的一個(gè)想法是跟蹤當(dāng)前分配的內(nèi)存量,并將帶有時(shí)間戳和其他信息的分配事件推送到分配事件的全局列表中。查看分配事件列表可以分析一段時(shí)間內(nèi)的內(nèi)存使用情況。
在移動(dòng)平臺(tái)上,GPU 內(nèi)存和 CPU 內(nèi)存共享系統(tǒng)內(nèi)存。在內(nèi)存大小非常有限的設(shè)備上,如 Nano,系統(tǒng)內(nèi)存可能會(huì)因大型網(wǎng)絡(luò)而耗盡;甚至所需的 GPU 內(nèi)存也小于系統(tǒng)內(nèi)存。在這種情況下,增加系統(tǒng)交換大小可以解決一些問(wèn)題。一個(gè)示例腳本是:
echo "######alloc swap######" if [ ! -e /swapfile ];then sudo fallocate -l 4G /swapfile sudo chmod 600 /swapfile sudo mkswap /swapfile sudo /bin/sh -c 'echo "/swapfile \t none \t swap \t defaults \t 0 \t 0" >> /etc/fstab' sudo swapon -a fi
13.2. Optimizing TensorRT Performance
以下部分重點(diǎn)介紹 GPU 上的一般推理流程和一些提高性能的一般策略。這些想法適用于大多數(shù) CUDA 程序員,但對(duì)于來(lái)自其他背景的開(kāi)發(fā)人員可能并不那么明顯。
13.2.1. Batching
最重要的優(yōu)化是使用批處理并行計(jì)算盡可能多的結(jié)果。在 TensorRT 中,批次是可以統(tǒng)一處理的輸入的集合。批次中的每個(gè)實(shí)例都具有相同的形狀,并以完全相同的方式流經(jīng)網(wǎng)絡(luò)。因此,每個(gè)實(shí)例都可以簡(jiǎn)單地并行計(jì)算。
網(wǎng)絡(luò)的每一層都有計(jì)算前向推理所需的一定數(shù)量的開(kāi)銷(xiāo)和同步。通過(guò)并行計(jì)算更多結(jié)果,這種開(kāi)銷(xiāo)可以更有效地得到回報(bào)。此外,許多層的性能受到輸入中最小維度的限制。如果批量大小為 1 或較小,則此大小通常可能是性能限制維度。例如,具有V個(gè)輸入和K個(gè)輸出的完全連接層可以針對(duì)一個(gè)批次實(shí)例實(shí)現(xiàn)為1xV矩陣與VxK權(quán)重矩陣的矩陣乘法。如果對(duì)N個(gè)實(shí)例進(jìn)行批處理,則這將變?yōu)镹xV乘以VxK矩陣。向量矩陣乘法器變成矩陣矩陣乘法器,效率更高。
更大的批量大小幾乎總是在 GPU 上更有效。非常大的批次,例如N 》 2^16 ,有時(shí)可能需要擴(kuò)展索引計(jì)算,因此應(yīng)盡可能避免。但通常,增加批量大小會(huì)提高總吞吐量。此外,當(dāng)網(wǎng)絡(luò)包含 MatrixMultiply 層或完全連接層時(shí),如果硬件支持,由于使用了 Tensor Cores,32 的倍數(shù)的批大小往往對(duì) FP16 和 INT8 推理具有最佳性能。
由于應(yīng)用程序的組織,有時(shí)無(wú)法進(jìn)行批處理推理工作。在一些常見(jiàn)的應(yīng)用程序中,例如根據(jù)請(qǐng)求進(jìn)行推理的服務(wù)器,可以實(shí)現(xiàn)機(jī)會(huì)批處理。對(duì)于每個(gè)傳入的請(qǐng)求,等待時(shí)間T 。如果在此期間有其他請(qǐng)求進(jìn)來(lái),請(qǐng)將它們一起批處理。否則,繼續(xù)進(jìn)行單實(shí)例推理。這種類(lèi)型的策略為每個(gè)請(qǐng)求增加了固定的延遲,但可以將系統(tǒng)的最大吞吐量提高幾個(gè)數(shù)量級(jí)。
使用批處理
如果在創(chuàng)建網(wǎng)絡(luò)時(shí)使用顯式批處理模式,則批處理維度是張量維度的一部分,您可以通過(guò)添加優(yōu)化配置文件來(lái)指定批處理大小和批處理大小的范圍以?xún)?yōu)化引擎。有關(guān)更多詳細(xì)信息,請(qǐng)參閱使用動(dòng)態(tài)形狀部分。
如果在創(chuàng)建網(wǎng)絡(luò)時(shí)使用隱式批處理模式,則IExecutionContext::execute ( Python 中的IExecutionContext.execute )和IExecutionContext::enqueue ( Python 中的IExecutionContext.execute_async )方法采用批處理大小參數(shù)。在使用IBuilder::setMaxBatchSize ( Python中的 Builder.max_batch_size )構(gòu)建優(yōu)化網(wǎng)絡(luò)時(shí),還應(yīng)該為構(gòu)建器設(shè)置最大批量大小。當(dāng)調(diào)用IExecutionContext::execute或enqueue時(shí),作為綁定參數(shù)傳遞的綁定是按張量組織的,而不是按實(shí)例組織的。換句話(huà)說(shuō),一個(gè)輸入實(shí)例的數(shù)據(jù)沒(méi)有組合到一個(gè)連續(xù)的內(nèi)存區(qū)域中。相反,每個(gè)張量綁定都是該張量的實(shí)例數(shù)據(jù)數(shù)組。
另一個(gè)考慮因素是構(gòu)建優(yōu)化的網(wǎng)絡(luò)會(huì)針對(duì)給定的最大批量大小進(jìn)行優(yōu)化。最終結(jié)果將針對(duì)最大批量大小進(jìn)行調(diào)整,但對(duì)于任何較小的批量大小仍然可以正常工作。可以運(yùn)行多個(gè)構(gòu)建操作來(lái)為不同的批量大小創(chuàng)建多個(gè)優(yōu)化引擎,然后在運(yùn)行時(shí)根據(jù)實(shí)際批量大小選擇要使用的引擎。
13.2.2. Streaming
一般來(lái)說(shuō),CUDA 編程流是一種組織異步工作的方式。放入流中的異步命令保證按順序運(yùn)行,但相對(duì)于其他流可能會(huì)亂序執(zhí)行。特別是,兩個(gè)流中的異步命令可以被調(diào)度為同時(shí)運(yùn)行(受硬件限制)。
在 TensorRT 和推理的上下文中,優(yōu)化的最終網(wǎng)絡(luò)的每一層都需要在 GPU 上工作。但是,并非所有層都能夠充分利用硬件的計(jì)算能力。在單獨(dú)的流中安排請(qǐng)求允許在硬件可用時(shí)立即安排工作,而無(wú)需進(jìn)行不必要的同步。即使只有一些層可以重疊,整體性能也會(huì)提高。
使用流式傳輸
識(shí)別獨(dú)立的推理批次。
為網(wǎng)絡(luò)創(chuàng)建一個(gè)引擎。
cudaStreamCreate為每個(gè)獨(dú)立批次創(chuàng)建一個(gè) CUDA 流,并為每個(gè)獨(dú)立批次創(chuàng)建一個(gè)IExecutionContext 。
IExecutionContext::enqueue從適當(dāng)?shù)腎ExecutionContext請(qǐng)求異步結(jié)果并傳入適當(dāng)?shù)牧鱽?lái)啟動(dòng)推理工作。
在所有工作啟動(dòng)后,與所有流同步以等待結(jié)果。執(zhí)行上下文和流可以重用于以后的獨(dú)立工作批次。
多個(gè)流 運(yùn)行多個(gè)并發(fā)流通常會(huì)導(dǎo)致多個(gè)流同時(shí)共享計(jì)算資源的情況。這意味著與優(yōu)化 TensorRT 引擎時(shí)相比,推理期間網(wǎng)絡(luò)可用的計(jì)算資源可能更少。這種資源可用性的差異可能會(huì)導(dǎo)致 TensorRT 選擇一個(gè)對(duì)于實(shí)際運(yùn)行時(shí)條件不是最佳的內(nèi)核。為了減輕這種影響,您可以在引擎創(chuàng)建期間限制可用計(jì)算資源的數(shù)量,使其更接近實(shí)際運(yùn)行時(shí)條件。這種方法通常以延遲為代價(jià)來(lái)提高吞吐量。有關(guān)更多信息,請(qǐng)參閱限制計(jì)算資源。
也可以將多個(gè)主機(jī)線(xiàn)程與流一起使用。一種常見(jiàn)的模式是將傳入的請(qǐng)求分派到等待工作線(xiàn)程池中。在這種情況下,工作線(xiàn)程池將每個(gè)都有一個(gè)執(zhí)行上下文和 CUDA 流。當(dāng)工作變得可用時(shí),每個(gè)線(xiàn)程將在自己的流中請(qǐng)求工作。每個(gè)線(xiàn)程將與其流同步以等待結(jié)果,而不會(huì)阻塞其他工作線(xiàn)程。
13.2.3. CUDA Graphs
CUDA 圖是一種表示內(nèi)核序列(或更一般地是圖)的方式,其調(diào)度方式允許由 CUDA 優(yōu)化。當(dāng)您的應(yīng)用程序性能對(duì)將內(nèi)核排入隊(duì)列所花費(fèi)的 CPU 時(shí)間敏感時(shí),這可能特別有用。 TensorRT 的enqueuev2()方法支持對(duì)不需要 CPU 交互的模型進(jìn)行 CUDA 圖捕獲。例如:
C++
// Capture a CUDA graph instance cudaGraph_t graph; cudaGraphExec_t instance; cudaStreamBeginCapture(stream, cudaStreamCaptureModeGlobal); context->enqueueV2(buffers, stream, nullptr); cudaStreamEndCapture(stream, &graph); cudaGraphInstantiate(&instance, graph, NULL, NULL, 0); // To run inferences: cudaGraphLaunch(instance, stream); cudaStreamSynchronize(stream);
不支持圖的模型包括帶有循環(huán)或條件的模型。在這種情況下, cudaStreamEndCapture()將返回cudaErrorStreamCapture*錯(cuò)誤,表示圖捕獲失敗,但上下文可以繼續(xù)用于沒(méi)有 CUDA 圖的正常推理。
捕獲圖時(shí),重要的是要考慮在存在動(dòng)態(tài)形狀時(shí)使用的兩階段執(zhí)行策略。
更新模型的內(nèi)部狀態(tài)以考慮輸入大小的任何變化
將工作流式傳輸?shù)?GPU
對(duì)于在構(gòu)建時(shí)輸入大小固定的模型,第一階段不需要每次調(diào)用工作。否則,如果自上次調(diào)用以來(lái)輸入大小發(fā)生了變化,則可能需要進(jìn)行一些工作來(lái)更新派生屬性。
第一階段的工作不是為捕獲而設(shè)計(jì)的,即使捕獲成功也可能會(huì)增加模型執(zhí)行時(shí)間。因此,在更改輸入的形狀或形狀張量的值后,調(diào)用enqueueV2()一次以在捕獲圖形之前刷新延遲更新。
使用 TensorRT 捕獲的圖特定于捕獲它們的輸入大小,以及執(zhí)行上下文的狀態(tài)。修改捕獲圖表的上下文將導(dǎo)致執(zhí)行圖表時(shí)未定義的行為 – 特別是,如果應(yīng)用程序通過(guò)createExecutionContextWithoutDeviceMemory()為激活提供自己的內(nèi)存,則內(nèi)存地址也會(huì)作為圖表的一部分被捕獲。綁定位置也被捕獲為圖表的一部分。
trtexec允許您檢查構(gòu)建的 TensorRT 引擎是否與 CUDA 圖形捕獲兼容。有關(guān)詳細(xì)信息,請(qǐng)參閱trtexec部分。
13.2.4. Enabling Fusion
13.2.4.1. Layer Fusion
TensorRT 嘗試在構(gòu)建階段在網(wǎng)絡(luò)中執(zhí)行許多不同類(lèi)型的優(yōu)化。在第一階段,盡可能將層融合在一起。融合將網(wǎng)絡(luò)轉(zhuǎn)換為更簡(jiǎn)單的形式,但保持相同的整體行為。在內(nèi)部,許多層實(shí)現(xiàn)具有在創(chuàng)建網(wǎng)絡(luò)時(shí)無(wú)法直接訪(fǎng)問(wèn)的額外參數(shù)和選項(xiàng)。相反,融合優(yōu)化步驟檢測(cè)支持的操作模式,并將多個(gè)層融合到一個(gè)具有內(nèi)部選項(xiàng)集的層中。
考慮卷積后跟 ReLU 激活的常見(jiàn)情況。要?jiǎng)?chuàng)建具有這些操作的網(wǎng)絡(luò),需要使用 addConvolution 添加卷積層,然后使用addActivation和kRELU的ActivationType添加激活層。未優(yōu)化的圖將包含用于卷積和激活的單獨(dú)層。卷積的內(nèi)部實(shí)現(xiàn)支持直接從卷積核一步計(jì)算輸出上的 ReLU 函數(shù),而無(wú)需第二次內(nèi)核調(diào)用。融合優(yōu)化步驟將檢測(cè) ReLU 之后的卷積,驗(yàn)證實(shí)現(xiàn)是否支持這些操作,然后將它們?nèi)诤系揭粚印?/p>
為了調(diào)查哪些融合已經(jīng)發(fā)生或沒(méi)有發(fā)生,構(gòu)建器將其操作記錄到構(gòu)建期間提供的記錄器對(duì)象。優(yōu)化步驟在kINFO日志級(jí)別。要查看這些消息,請(qǐng)確保將它們記錄在ILogger回調(diào)中。
融合通常通過(guò)創(chuàng)建一個(gè)新層來(lái)處理,該層的名稱(chēng)包含被融合的兩個(gè)層的名稱(chēng)。例如,在 MNIST 中,名為 ip1 的全連接層(InnerProduct)與名為relu1的 ReLU 激活層融合,以創(chuàng)建名為ip1 + relu1的新層。
13.2.4.2. Types Of Fusions
以下列表描述了支持的融合類(lèi)型。
支持的層融合
ReLU ReLU Activation
執(zhí)行 ReLU 的激活層,然后執(zhí)行 ReLU 的激活將被單個(gè)激活層替換。
Convolution and ReLU Activation 卷積層可以是任何類(lèi)型,并且對(duì)值沒(méi)有限制。激活層必須是 ReLU 類(lèi)型。
Convolution and GELU Activation
輸入輸出精度要一致;它們都是 FP16 或 INT8。激活層必須是 GELU 類(lèi)型。 TensorRT 應(yīng)該在具有 CUDA 10.0 或更高版本的 Turing 或更高版本的設(shè)備上運(yùn)行。
Convolution and Clip Activation
卷積層可以是任何類(lèi)型,并且對(duì)值沒(méi)有限制。激活層必須是Clip類(lèi)型。
Scale and Activation
Scale 層后跟一個(gè) Activation 層可以融合成一個(gè) Activation 層。
Convolution And ElementWise Operation
卷積層后跟 ElementWise 層中的簡(jiǎn)單求和、最小值或最大值可以融合到卷積層中。總和不得使用廣播,除非廣播跨越批量大小。
Padding and Convolution/Deconvolution
如果所有填充大小都是非負(fù)的,則可以將后跟卷積或反卷積的填充融合到單個(gè)卷積/反卷積層中。
Shuffle and Reduce
一個(gè)沒(méi)有 reshape 的 Shuffle 層,然后是一個(gè) Reduce 層,可以融合成一個(gè) Reduce 層。 Shuffle 層可以執(zhí)行排列,但不能執(zhí)行任何重塑操作。 Reduce 層必須有一組keepDimensions維度。
Shuffle and Shuffle
每個(gè) Shuffle 層由轉(zhuǎn)置、重塑和第二個(gè)轉(zhuǎn)置組成。一個(gè) Shuffle 層后跟另一個(gè) Shuffle 層可以被單個(gè) Shuffle 替換(或什么都沒(méi)有)。如果兩個(gè) Shuffle 層都執(zhí)行 reshape 操作,則只有當(dāng)?shù)谝粋€(gè) shuffle 的第二個(gè)轉(zhuǎn)置是第二個(gè) shuffle 的第一個(gè)轉(zhuǎn)置的逆時(shí),才允許這種融合。
Scale 可以擦除添加0 、乘以1或計(jì)算 1 的冪的Scale 層。
Convolution and Scale
卷積層后跟kUNIFORM或kCHANNEL的 Scale 層融合為單個(gè)卷積。如果秤具有非恒定功率參數(shù),則禁用此融合。
Reduce
執(zhí)行平均池化的 Reduce 層將被 Pooling 層取代。 Reduce 層必須有一個(gè)keepDimensions集,使用kAVG操作在批處理之前從CHW輸入格式減少H和W維度。
Convolution and Pooling 卷積層和池化層必須具有相同的精度。卷積層可能已經(jīng)具有來(lái)自先前融合的融合激活操作。
Depthwise Separable Convolution 帶有激活的深度卷積,然后是帶有激活的卷積,有時(shí)可能會(huì)融合到單個(gè)優(yōu)化的 DepSepConvolution 層中。兩個(gè)卷積的精度必須為 INT8,并且設(shè)備的計(jì)算能力必須為 7.2 或更高版本。
SoftMax and Log
如果 SoftMax 尚未與先前的日志操作融合,則可以將其融合為單個(gè) Softmax 層。
SoftMax 和 TopK
可以融合成單層。 SoftMax 可能包含也可能不包含 Log 操作。
FullyConnected
FullyConnected 層將被轉(zhuǎn)換為 Convolution 層,所有用于卷積的融合都會(huì)生效。
Supported Reduction Operation Fusions
GELU
一組表示以下方程的 Unary 層和 ElementWise 層可以融合到單個(gè) GELU 歸約操作中。
$0.5x × (1+tanh (2/π (x+0.044715x^3)))$
或替代表示: $0.5x × (1+erf (x/\sqrt{2}))$
L1Norm
一個(gè)一元層kABS操作和一個(gè) Reduce 層kSUM操作可以融合成一個(gè) L1Norm 歸約操作。
Sum of Squares
具有相同輸入(平方運(yùn)算)的乘積 ElementWise 層后跟kSUM 約簡(jiǎn)可以融合為單個(gè)平方和約簡(jiǎn)運(yùn)算。
L2Norm
kSQRT UnaryOperation之后的平方和運(yùn)算可以融合到單個(gè) L2Norm 歸約運(yùn)算中。
LogSum
一個(gè)縮減層kSUM后跟一個(gè)kLOG UnaryOperation 可以融合成一個(gè)單一的 LogSum 縮減操作。
LogSumExp
一個(gè)一元kEXP ElementWise操作后跟一個(gè) LogSum 融合可以融合成一個(gè)單一的 LogSumExp 約簡(jiǎn)。
13.2.4.3. PointWise Fusion
多個(gè)相鄰的 PointWise 層可以融合到一個(gè) PointWise 層中,以提高性能。
支持以下類(lèi)型的 PointWise 層,但有一些限制:
Activation
每個(gè)ActivationType 。
Constant
僅具有單個(gè)值的常量(大小 == 1)。
ElementWise
每個(gè)ElementWiseOperation 。
PointWise
PointWise本身也是一個(gè) PointWise 層。
Scale
僅支持ScaleMode::kUNIFORM 。
Unary
每個(gè)UnaryOperation 。
融合的 PointWise 層的大小不是無(wú)限的,因此,某些 PointWise 層可能無(wú)法融合。
Fusion 創(chuàng)建一個(gè)新層,其名稱(chēng)由融合的兩個(gè)層組成。例如,名為add1的 ElementWise 層與名為relu1的 ReLU 激活層融合,新層名稱(chēng)為: fusedPointwiseNode(add1, relu1) 。
13.2.4.4. Q/DQ Fusion
從 QAT 工具(如NVIDIA 的 PyTorch 量化工具包)生成的量化 INT8 圖由具有比例和零點(diǎn)的onnx::QuantizeLinear和onnx::DequantizeLinear節(jié)點(diǎn)對(duì) (Q/DQ) 組成。從 TensorRT 7.0 開(kāi)始,要求zero_point為0 。
Q/DQ 節(jié)點(diǎn)幫助將 FP32 值轉(zhuǎn)換為 INT8,反之亦然。這樣的圖在 FP32 精度上仍然會(huì)有權(quán)重和偏差。
權(quán)重之后是 Q/DQ 節(jié)點(diǎn)對(duì),以便在需要時(shí)可以對(duì)它們進(jìn)行量化/去量化。偏置量化是使用來(lái)自激活和權(quán)重的尺度執(zhí)行的,因此偏置輸入不需要額外的 Q/DQ 節(jié)點(diǎn)對(duì)。偏差量化的假設(shè)是$S_weights * S_input = S_bias$ 。
與 Q/DQ 節(jié)點(diǎn)相關(guān)的融合包括量化/去量化權(quán)重,在不改變模型數(shù)學(xué)等價(jià)性的情況下對(duì) Q/DQ 節(jié)點(diǎn)進(jìn)行交換,以及擦除冗余 Q/DQ 節(jié)點(diǎn)。應(yīng)用 Q/DQ 融合后,其余的構(gòu)建器優(yōu)化將應(yīng)用于圖。
Fuse Q/DQ with weighted node (Conv, FC, Deconv)
如果我們有一個(gè)
[DequantizeLinear (Activations), DequantizeLinear (weights)] > Node > QuantizeLinear
( [DQ, DQ] 》 Node 》 Q ) 序列,然后融合到量化節(jié)點(diǎn) ( QNode )。
支持權(quán)重的 Q/DQ 節(jié)點(diǎn)對(duì)需要加權(quán)節(jié)點(diǎn)支持多個(gè)輸入。因此,我們支持添加第二個(gè)輸入(用于權(quán)重張量)和第三個(gè)輸入(用于偏置張量)。可以使用setInput(index, tensor) API 為卷積、反卷積和全連接層設(shè)置其他輸入,其中 index = 2 用于權(quán)重張量, index = 3 用于偏置張量。
在與加權(quán)節(jié)點(diǎn)融合期間,我們會(huì)將 FP32 權(quán)重量化為 INT8,并將其與相應(yīng)的加權(quán)節(jié)點(diǎn)融合。類(lèi)似地,F(xiàn)P32 偏差將被量化為 INT32 并融合。
使用非加權(quán)節(jié)點(diǎn)融合 Q/DQ
如果我們有一個(gè)DequantizeLinear 》 Node 》 QuantizeLinear ( DQ 》 Node 》 Q ) 序列,那么它將融合到量化節(jié)點(diǎn) ( QNode )。
Commutate Q/DQ nodes
DequantizeLinear commutation is allowed when $Φ (DQ (x)) == DQ (Φ (x))$ 。 QuantizeLinear commutation is allowed when $Q (Φ (x)) == Φ (Q (x))$ 。
此外,交換邏輯還考慮了可用的內(nèi)核實(shí)現(xiàn),從而保證了數(shù)學(xué)等價(jià)性。
Insert missing Q/DQ nodes
如果一個(gè)節(jié)點(diǎn)缺少一個(gè) Q/DQ 節(jié)點(diǎn)對(duì),并且$max (abs (Φ (x))) == max (abs (x))$ ; (例如,MaxPool),將插入缺少的 Q/DQ 對(duì)以運(yùn)行更多具有 INT8 精度的節(jié)點(diǎn)。
Erase redundant Q/DQ nodes
有可能在應(yīng)用所有優(yōu)化之后,該圖仍然有 Q/DQ 節(jié)點(diǎn)對(duì),它們本身就是一個(gè)空操作。 Q/DQ 節(jié)點(diǎn)擦除融合將刪除此類(lèi)冗余對(duì)。
13.2.5. Limiting Compute Resources
當(dāng)減少的數(shù)量更好地代表運(yùn)行時(shí)的預(yù)期條件時(shí),限制在引擎創(chuàng)建期間可用于 TensorRT 的計(jì)算資源量是有益的。例如,當(dāng)期望 GPU 與 TensorRT 引擎并行執(zhí)行額外工作時(shí),或者當(dāng)期望引擎在資源較少的不同 GPU 上運(yùn)行時(shí)(請(qǐng)注意,推薦的方法是在 GPU 上構(gòu)建引擎,即將用于推理,但這可能并不總是可行的)。
您可以通過(guò)以下步驟限制可用計(jì)算資源的數(shù)量:
啟動(dòng) CUDA MPS 控制守護(hù)進(jìn)程。nvidia-cuda-mps-control -d
CUDA_MPS_ACTIVE_THREAD_PERCENTAGE環(huán)境變量一起使用的計(jì)算資源量。例如,導(dǎo)出 CUDA_MPS_ACTIVE_THREAD_PERCENTAGE=50 。
構(gòu)建網(wǎng)絡(luò)引擎。
停止 CUDA MPS 控制守護(hù)程序。echo quit | nvidia-cuda-mps-control
生成的引擎針對(duì)減少的計(jì)算核心數(shù)量(本例中為 50%)進(jìn)行了優(yōu)化,并在推理期間使用類(lèi)似條件時(shí)提供更好的吞吐量。鼓勵(lì)您嘗試不同數(shù)量的流和不同的 MPS 值,以確定網(wǎng)絡(luò)的最佳性能。
有關(guān)nvidia-cuda-mps-control 的更多詳細(xì)信息,請(qǐng)參閱nvidia-cuda-mps-control文檔和此處的相關(guān) GPU 要求。
13.3. Optimizing Layer Performance
以下描述詳細(xì)說(shuō)明了如何優(yōu)化列出的層。
Concatenation Layer
如果使用隱式批處理維度,連接層的主要考慮是如果多個(gè)輸出連接在一起,它們不能跨批處理維度廣播,必須顯式復(fù)制。大多數(shù)層支持跨批次維度的廣播以避免不必要地復(fù)制數(shù)據(jù),但如果輸出與其他張量連接,這將被禁用。
Gather Layer 請(qǐng)使用0軸。 Gather 層沒(méi)有可用的融合。
Reduce Layer
要從 Reduce 層獲得最大性能,請(qǐng)?jiān)谧詈笠粋€(gè)維度上執(zhí)行歸約(尾部歸約)。這允許最佳內(nèi)存通過(guò)順序內(nèi)存位置讀取/寫(xiě)入模式。如果進(jìn)行常見(jiàn)的歸約操作,請(qǐng)盡可能以將融合為單個(gè)操作的方式表達(dá)歸約。
RNN Layer
如果可能,請(qǐng)選擇使用較新的 RNNv2 接口而不是傳統(tǒng)的 RNN 接口。較新的接口支持可變序列長(zhǎng)度和可變批量大小,以及具有更一致的接口。為了獲得最佳性能,更大的批量大小更好。通常,大小為 64 的倍數(shù)可獲得最高性能。雙向 RNN 模式由于增加了依賴(lài)性而阻止了波前傳播,因此,它往往更慢。
此外,新引入的基于 ILoop 的 API 提供了一種更靈活的機(jī)制,可以在循環(huán)中使用通用層,而不受限于一小組預(yù)定義的 RNNv2 接口。 ILoop 循環(huán)實(shí)現(xiàn)了一組豐富的自動(dòng)循環(huán)優(yōu)化,包括循環(huán)融合、展開(kāi)和循環(huán)不變的代碼運(yùn)動(dòng),僅舉幾例。例如,當(dāng)同一 MatrixMultiply 或 FullyConnected 層的多個(gè)實(shí)例正確組合以在沿序列維度展開(kāi)循環(huán)后最大化機(jī)器利用率時(shí),通常會(huì)獲得顯著的性能提升。如果您可以避免 MatrixMultiply 或 FullyConnected 層沿序列維度具有循環(huán)數(shù)據(jù)依賴(lài)性,則此方法效果最佳。
Shuffle
如果輸入張量?jī)H用于 shuffle 層,并且該層的輸入和輸出張量不是網(wǎng)絡(luò)的輸入和輸出張量,則省略相當(dāng)于對(duì)基礎(chǔ)數(shù)據(jù)的身份操作的 shuffle 操作。 TensorRT 不會(huì)為此類(lèi)操作執(zhí)行額外的內(nèi)核或內(nèi)存副本。
TopK
要從 TopK 層中獲得最大性能,請(qǐng)使用較小的K值來(lái)減少數(shù)據(jù)的最后一維,以實(shí)現(xiàn)最佳的順序內(nèi)存訪(fǎng)問(wèn)。通過(guò)使用 Shuffle 層來(lái)重塑數(shù)據(jù),然后適當(dāng)?shù)刂匦陆忉屗饕担梢砸淮?a href="http://www.asorrir.com/analog/" target="_blank">模擬沿多個(gè)維度的縮減。
有關(guān)層的更多信息,請(qǐng)參閱TensorRT 層。
13.4. Optimizing for Tensor Cores
Tensor Core 是在 NVIDIA GPU 上提供高性能推理的關(guān)鍵技術(shù)。在 TensorRT 中,所有計(jì)算密集型層(MatrixMultiply、FullyConnected、Convolution 和 Deconvolution)都支持 Tensor Core 操作。
如果輸入/輸出張量維度與某個(gè)最小粒度對(duì)齊,則張量核心層往往會(huì)獲得更好的性能:
在卷積和反卷積層中,對(duì)齊要求是輸入/輸出通道維度
在 MatrixMultiply 和 FullyConnected 層中,對(duì)齊要求是在 MatrixMultiply 中的矩陣維度K和N上,即M x K乘以K x N
下表捕獲了建議的張量維度對(duì)齊,以獲得更好的張量核心性能。
在不滿(mǎn)足這些要求的情況下使用 Tensor Core 實(shí)現(xiàn)時(shí),TensorRT 會(huì)隱式地將張量填充到最接近的對(duì)齊倍數(shù),將模型定義中的維度向上舍入,以在不增加計(jì)算或內(nèi)存流量的情況下允許模型中的額外容量。
TensorRT 總是對(duì)層使用最快的實(shí)現(xiàn),因此在某些情況下,即使可用,也可能不使用 Tensor Core 實(shí)現(xiàn)。
13.5. Optimizing Plugins
TensorRT 提供了一種注冊(cè)執(zhí)行層操作的自定義插件的機(jī)制。插件創(chuàng)建者注冊(cè)后,您可以在序列化/反序列化過(guò)程中查找注冊(cè)表找到創(chuàng)建者并將相應(yīng)的插件對(duì)象添加到網(wǎng)絡(luò)中。
加載插件庫(kù)后,所有 TensorRT 插件都會(huì)自動(dòng)注冊(cè)。有關(guān)自定義插件的更多信息,請(qǐng)參閱使用自定義層擴(kuò)展 TensorRT 。
插件的性能取決于執(zhí)行插件操作的 CUDA 代碼。適用標(biāo)準(zhǔn)CUDA 最佳實(shí)踐。在開(kāi)發(fā)插件時(shí),從執(zhí)行插件操作并驗(yàn)證正確性的簡(jiǎn)單獨(dú)立 CUDA 應(yīng)用程序開(kāi)始會(huì)很有幫助。然后可以通過(guò)性能測(cè)量、更多單元測(cè)試和替代實(shí)現(xiàn)來(lái)擴(kuò)展插件程序。代碼運(yùn)行并優(yōu)化后,可以作為插件集成到 TensorRT 中。 為了盡可能獲得最佳性能,在插件中支持盡可能多的格式非常重要。這消除了在網(wǎng)絡(luò)執(zhí)行期間對(duì)內(nèi)部重新格式化操作的需要。有關(guān)示例,請(qǐng)參閱使用自定義層擴(kuò)展 TensorRT部分。
13.6. Optimizing Python Performance
使用 Python API 時(shí),大多數(shù)相同的性能注意事項(xiàng)都適用。在構(gòu)建引擎時(shí),構(gòu)建器優(yōu)化階段通常會(huì)成為性能瓶頸;不是 API 調(diào)用來(lái)構(gòu)建網(wǎng)絡(luò)。 Python API 和 C++ API 的推理時(shí)間應(yīng)該幾乎相同。
在 Python API 中設(shè)置輸入緩沖區(qū)涉及使用pycuda或其他 CUDA Python 庫(kù)(如cupy )將數(shù)據(jù)從主機(jī)傳輸?shù)皆O(shè)備內(nèi)存。其工作原理的詳細(xì)信息將取決于主機(jī)數(shù)據(jù)的來(lái)源。在內(nèi)部, pycuda支持允許有效訪(fǎng)問(wèn)內(nèi)存區(qū)域的Python 緩沖區(qū)協(xié)議。這意味著,如果輸入數(shù)據(jù)在numpy數(shù)組或其他也支持緩沖區(qū)協(xié)議的類(lèi)型中以合適的格式可用,則可以有效地訪(fǎng)問(wèn)和傳輸?shù)?GPU。為了獲得更好的性能,請(qǐng)確保您使用pycuda分配一個(gè)頁(yè)面鎖定的緩沖區(qū),并在那里寫(xiě)入最終的預(yù)處理輸入。
有關(guān)使用 Python API 的更多信息,請(qǐng)參閱Python API 。
13.7. Improving Model Accuracy
TensorRT 可以根據(jù)構(gòu)建器配置以 FP32、FP16 或 INT8 精度執(zhí)行層。默認(rèn)情況下,TensorRT 選擇以可實(shí)現(xiàn)最佳性能的精度運(yùn)行層。有時(shí)這可能會(huì)導(dǎo)致準(zhǔn)確性下降。通常,以更高的精度運(yùn)行層有助于提高準(zhǔn)確性,但會(huì)影響一些性能。
我們可以采取幾個(gè)步驟來(lái)提高模型的準(zhǔn)確性:
驗(yàn)證層輸出:
使用Polygraphy轉(zhuǎn)儲(chǔ)層輸出并驗(yàn)證沒(méi)有 NaN 或 Inf。 --validate選項(xiàng)可以檢查 NaN 和 Infs 。此外,我們可以將層輸出與來(lái)自例如 ONNX 運(yùn)行時(shí)的黃金值進(jìn)行比較。
對(duì)于 FP16,模型可能需要重新訓(xùn)練以確保中間層輸出可以以 FP16 精度表示,而不會(huì)出現(xiàn)上溢/下溢。
對(duì)于 INT8,考慮使用更具代表性的校準(zhǔn)數(shù)據(jù)集重新校準(zhǔn)。如果您的模型來(lái)自 PyTorch,除了 TensorRT 中的 PTQ,我們還提供了 NVIDIA 的 Quantization Toolkit for PyTorch for QAT 框架中的 QAT。您可以嘗試這兩種方法并選擇更準(zhǔn)確的方法。
操縱層精度:
有時(shí)以特定精度運(yùn)行層會(huì)導(dǎo)致輸出不正確。這可能是由于固有的層約束(例如,LayerNorm 輸出不應(yīng)為 INT8)、模型約束(輸出發(fā)散導(dǎo)致準(zhǔn)確性差)或報(bào)告TensorRT 錯(cuò)誤。
您可以控制層執(zhí)行精度和輸出精度。
一個(gè)實(shí)驗(yàn)性的調(diào)試精度工具可以幫助自動(dòng)找到以高精度運(yùn)行的層。
使用算法選擇和可重現(xiàn)構(gòu)建來(lái)禁用不穩(wěn)定的策略:
當(dāng)構(gòu)建+運(yùn)行與構(gòu)建+運(yùn)行之間的準(zhǔn)確性發(fā)生變化時(shí),可能是由于為層選擇了錯(cuò)誤的策略。
使用算法選擇器從好的和壞的運(yùn)行中轉(zhuǎn)儲(chǔ)策略。將算法選擇器配置為僅允許策略的子集(即僅允許來(lái)自良好運(yùn)行的策略等)。 c. 您可以使用Polygraphy自動(dòng)執(zhí)行此過(guò)程。
每次運(yùn)行變化的準(zhǔn)確性不應(yīng)改變;一旦為特定 GPU 構(gòu)建了引擎,它應(yīng)該會(huì)在多次運(yùn)行中產(chǎn)生位準(zhǔn)確的輸出。如果沒(méi)有,請(qǐng)?zhí)峤籘ensorRT 錯(cuò)誤。
關(guān)于作者
Ken He 是 NVIDIA 企業(yè)級(jí)開(kāi)發(fā)者社區(qū)經(jīng)理 & 高級(jí)講師,擁有多年的 GPU 和人工智能開(kāi)發(fā)經(jīng)驗(yàn)。自 2017 年加入 NVIDIA 開(kāi)發(fā)者社區(qū)以來(lái),完成過(guò)上百場(chǎng)培訓(xùn),幫助上萬(wàn)個(gè)開(kāi)發(fā)者了解人工智能和 GPU 編程開(kāi)發(fā)。在計(jì)算機(jī)視覺(jué),高性能計(jì)算領(lǐng)域完成過(guò)多個(gè)獨(dú)立項(xiàng)目。并且,在機(jī)器人和無(wú)人機(jī)領(lǐng)域,有過(guò)豐富的研發(fā)經(jīng)驗(yàn)。對(duì)于圖像識(shí)別,目標(biāo)的檢測(cè)與跟蹤完成過(guò)多種解決方案。曾經(jīng)參與 GPU 版氣象模式GRAPES,是其主要研發(fā)者。
審核編輯:郭婷
-
NVIDIA
+關(guān)注
關(guān)注
14文章
5309瀏覽量
106348 -
計(jì)時(shí)器
+關(guān)注
關(guān)注
1文章
432瀏覽量
33733
發(fā)布評(píng)論請(qǐng)先 登錄
NVIDIA RTX AI加速FLUX.1 Kontext現(xiàn)已開(kāi)放下載
如何在魔搭社區(qū)使用TensorRT-LLM加速優(yōu)化Qwen3系列模型推理部署
NVIDIA Blackwell GPU優(yōu)化DeepSeek-R1性能 打破DeepSeek-R1在最小延遲場(chǎng)景中的性能紀(jì)錄

使用NVIDIA Triton和TensorRT-LLM部署TTS應(yīng)用的最佳實(shí)踐

天馬榮獲新財(cái)富雜志“2024 ESG最佳實(shí)踐獎(jiǎng)”
在NVIDIA TensorRT-LLM中啟用ReDrafter的一些變化

解鎖NVIDIA TensorRT-LLM的卓越性能
NVIDIA TensorRT-LLM Roadmap現(xiàn)已在GitHub上公開(kāi)發(fā)布

使用NVIDIA TensorRT提升Llama 3.2性能
TensorRT-LLM低精度推理優(yōu)化

評(píng)論