0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

支持動態(tài)并行的CUDA擴(kuò)展功能和最佳應(yīng)用實(shí)踐

星星科技指導(dǎo)員 ? 來源:NVIDIA ? 作者:Ken He ? 2022-04-28 09:31 ? 次閱讀

D.1. Introduction

D.1.1. Overview

Dynamic Parallelism是 CUDA 編程模型的擴(kuò)展,使 CUDA 內(nèi)核能夠直接在 GPU 上創(chuàng)建新工作并與新工作同步。在程序中需要的任何位置動態(tài)創(chuàng)建并行性提供了令人興奮的新功能。

直接從 GPU 創(chuàng)建工作的能力可以減少在主機(jī)和設(shè)備之間傳輸執(zhí)行控制和數(shù)據(jù)的需要,因?yàn)楝F(xiàn)在可以通過在設(shè)備上執(zhí)行的線程在運(yùn)行時做出啟動配置決策。此外,可以在運(yùn)行時在內(nèi)核內(nèi)內(nèi)聯(lián)生成依賴于數(shù)據(jù)的并行工作,動態(tài)利用 GPU 的硬件調(diào)度程序和負(fù)載平衡器,并根據(jù)數(shù)據(jù)驅(qū)動的決策或工作負(fù)載進(jìn)行調(diào)整。以前需要修改以消除遞歸、不規(guī)則循環(huán)結(jié)構(gòu)或其他不適合平面、單級并行性的構(gòu)造的算法和編程模式可以更透明地表達(dá)。

本文檔描述了支持動態(tài)并行的 CUDA 的擴(kuò)展功能,包括為利用這些功能而對 CUDA 編程模型進(jìn)行必要的修改和添加,以及利用此附加功能的指南和最佳實(shí)踐。

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

只有計(jì)算能力為 3.5 或更高的設(shè)備支持動態(tài)并行。

D.1.2. Glossary

本指南中使用的術(shù)語的定義。

Grid:網(wǎng)格是線程的集合。網(wǎng)格中的線程執(zhí)行內(nèi)核函數(shù)并被劃分為線程。

Thread Block:線程塊是在同一多處理器 (SM) 上執(zhí)行的一組線程。線程塊中的線程可以訪問共享內(nèi)存并且可以顯式同步。

Kernel Function:內(nèi)核函數(shù)是一個隱式并行子程序,它在 CUDA 執(zhí)行和內(nèi)存模型下為網(wǎng)格中的每個線程執(zhí)行。

Host:Host 指的是最初調(diào)用 CUDA 的執(zhí)行環(huán)境。通常是在系統(tǒng)的 CPU 處理器上運(yùn)行的線程。

Parent:父線程、線程塊或網(wǎng)格是已啟動新網(wǎng)格、子網(wǎng)格的一種。直到所有啟動的子網(wǎng)格也完成后,父節(jié)點(diǎn)才被視為完成。

Child:子線程、塊或網(wǎng)格是由父網(wǎng)格啟動的線程、塊或網(wǎng)格。子網(wǎng)格必須在父線程、線程塊或網(wǎng)格被認(rèn)為完成之前完成。

Thread Block Scope:具有線程塊作用域的對象具有單個線程塊的生命周期。它們僅在由創(chuàng)建對象的線程塊中的線程操作時具有定義的行為,并在創(chuàng)建它們的線程塊完成時被銷毀。

Device Runtime:設(shè)備運(yùn)行時是指可用于使內(nèi)核函數(shù)使用動態(tài)并行的運(yùn)行時系統(tǒng)和 API。

D.2. Execution Environment and Memory Model

D.2.1. Execution Environment

CUDA 執(zhí)行模型基于線程、線程塊和網(wǎng)格的原語,內(nèi)核函數(shù)定義了線程塊和網(wǎng)格內(nèi)的各個線程執(zhí)行的程序。 當(dāng)調(diào)用內(nèi)核函數(shù)時,網(wǎng)格的屬性由執(zhí)行配置描述,該配置在 CUDA 中具有特殊的語法。 CUDA 中對動態(tài)并行性的支持?jǐn)U展了在新網(wǎng)格上配置、啟動和同步到設(shè)備上運(yùn)行的線程的能力。

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize() 塊)在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

D.2.1.1. Parent and Child Grids

配置并啟動新網(wǎng)格的設(shè)備線程屬于父網(wǎng)格,調(diào)用創(chuàng)建的網(wǎng)格是子網(wǎng)格。

子網(wǎng)格的調(diào)用和完成是正確嵌套的,這意味著在其線程創(chuàng)建的所有子網(wǎng)格都完成之前,父網(wǎng)格不會被認(rèn)為是完整的。 即使調(diào)用線程沒有在啟動的子網(wǎng)格上顯式同步,運(yùn)行時也會保證父子網(wǎng)格之間的隱式同步。

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

parent-child-launch-nesting.png

D.2.1.2. Scope of CUDA Primitives

在主機(jī)和設(shè)備上,CUDA 運(yùn)行時都提供了一個 API,用于啟動內(nèi)核、等待啟動的工作完成以及通過流和事件跟蹤啟動之間的依賴關(guān)系。 在主機(jī)系統(tǒng)上,啟動狀態(tài)和引用流和事件的 CUDA 原語由進(jìn)程內(nèi)的所有線程共享; 但是進(jìn)程獨(dú)立執(zhí)行,可能不共享 CUDA 對象。

設(shè)備上存在類似的層次結(jié)構(gòu):啟動的內(nèi)核和 CUDA 對象對線程塊中的所有線程都是可見的,但在線程塊之間是獨(dú)立的。 這意味著例如一個流可以由一個線程創(chuàng)建并由同一線程塊中的任何其他線程使用,但不能與任何其他線程塊中的線程共享。

D.2.1.3. Synchronization

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

來自任何線程的 CUDA 運(yùn)行時操作,包括內(nèi)核啟動,在線程塊中都是可見的。 這意味著父網(wǎng)格中的調(diào)用線程可以在由該線程啟動的網(wǎng)格、線程塊中的其他線程或在同一線程塊中創(chuàng)建的流上執(zhí)行同步。 直到塊中所有線程的所有啟動都完成后,才認(rèn)為線程塊的執(zhí)行完成。 如果一個塊中的所有線程在所有子啟動完成之前退出,將自動觸發(fā)同步操作。

D.2.1.4. Streams and Events

CUDA 流和事件允許控制網(wǎng)格啟動之間的依賴關(guān)系:啟動到同一流中的網(wǎng)格按順序執(zhí)行,事件可用于創(chuàng)建流之間的依賴關(guān)系。 在設(shè)備上創(chuàng)建的流和事件服務(wù)于這個完全相同的目的。

在網(wǎng)格中創(chuàng)建的流和事件存在于線程塊范圍內(nèi),但在創(chuàng)建它們的線程塊之外使用時具有未定義的行為。 如上所述,線程塊啟動的所有工作在塊退出時都會隱式同步; 啟動到流中的工作包含在其中,所有依賴關(guān)系都得到了適當(dāng)?shù)慕鉀Q。 已在線程塊范圍之外修改的流上的操作行為未定義。

在主機(jī)上創(chuàng)建的流和事件在任何內(nèi)核中使用時具有未定義的行為,就像在子網(wǎng)格中使用時由父網(wǎng)格創(chuàng)建的流和事件具有未定義的行為一樣。

D.2.1.5. Ordering and Concurrency

從設(shè)備運(yùn)行時啟動內(nèi)核的順序遵循 CUDA Stream 排序語義。在一個線程塊內(nèi),所有內(nèi)核啟動到同一個流中都是按順序執(zhí)行的。當(dāng)同一個線程塊中的多個線程啟動到同一個流中時,流內(nèi)的順序取決于塊內(nèi)的線程調(diào)度,這可以通過 __syncthreads() 等同步原語進(jìn)行控制。

請注意,由于流由線程塊內(nèi)的所有線程共享,因此隱式 NULL 流也被共享。如果線程塊中的多個線程啟動到隱式流中,則這些啟動將按順序執(zhí)行。如果需要并發(fā),則應(yīng)使用顯式命名流。

動態(tài)并行使并發(fā)在程序中更容易表達(dá);但是,設(shè)備運(yùn)行時不會在 CUDA 執(zhí)行模型中引入新的并發(fā)保證。無法保證設(shè)備上任意數(shù)量的不同線程塊之間的并發(fā)執(zhí)行。

缺乏并發(fā)保證延伸到父線程塊及其子網(wǎng)格。當(dāng)父線程塊啟動子網(wǎng)格時,在父線程塊到達(dá)顯式同步點(diǎn)(例如 cudaDeviceSynchronize())之前,不保證子網(wǎng)格開始執(zhí)行。

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

雖然并發(fā)通常很容易實(shí)現(xiàn),但它可能會因設(shè)備配置、應(yīng)用程序工作負(fù)載和運(yùn)行時調(diào)度而異。因此,依賴不同線程塊之間的任何并發(fā)性是不安全的。

D.2.1.6. Device Management

設(shè)備運(yùn)行時不支持多 GPU; 設(shè)備運(yùn)行時只能在其當(dāng)前執(zhí)行的設(shè)備上運(yùn)行。 但是,允許查詢系統(tǒng)中任何支持 CUDA 的設(shè)備的屬性。

D.2.2. Memory Model

父網(wǎng)格和子網(wǎng)格共享相同的全局和常量內(nèi)存存儲,但具有不同的本地和共享內(nèi)存。

D.2.2.1. Coherence and Consistency

D.2.2.1.1. Global Memory

父子網(wǎng)格可以連貫地訪問全局內(nèi)存,但子網(wǎng)格和父網(wǎng)格之間的一致性保證很弱。當(dāng)子網(wǎng)格的內(nèi)存視圖與父線程完全一致時,子網(wǎng)格的執(zhí)行有兩點(diǎn):當(dāng)子網(wǎng)格被父線程調(diào)用時,以及當(dāng)子網(wǎng)格線程完成時(由父線程中的同步 API 調(diào)用發(fā)出信號)。

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

在子網(wǎng)格調(diào)用之前,父線程中的所有全局內(nèi)存操作對子網(wǎng)格都是可見的。在父網(wǎng)格完成同步后,子網(wǎng)格的所有內(nèi)存操作對父網(wǎng)格都是可見的。

在下面的示例中,執(zhí)行 child_launch 的子網(wǎng)格只能保證看到在子網(wǎng)格啟動之前對數(shù)據(jù)所做的修改。由于父線程 0 正在執(zhí)行啟動,子線程將與父線程 0 看到的內(nèi)存保持一致。由于第一次 __syncthreads() 調(diào)用,孩子將看到 data[0]=0, data[1]=1, 。.., data[255]=255(沒有 __syncthreads() 調(diào)用,只有 data[0]將保證被孩子看到)。當(dāng)子網(wǎng)格返回時,線程 0 保證可以看到其子網(wǎng)格中的線程所做的修改。只有在第二次 __syncthreads() 調(diào)用之后,這些修改才可用于父網(wǎng)格的其他線程:

__global__ void child_launch(int *data) {
   data[threadIdx.x] = data[threadIdx.x]+1;
}

__global__ void parent_launch(int *data) {
   data[threadIdx.x] = threadIdx.x;

   __syncthreads();

   if (threadIdx.x == 0) {
       child_launch<<< 1, 256 >>>(data);
       cudaDeviceSynchronize();
   }

   __syncthreads();
}

void host_launch(int *data) {
    parent_launch<<< 1, 256 >>>(data);
}

D.2.2.1.2. Zero Copy Memory

零拷貝系統(tǒng)內(nèi)存與全局內(nèi)存具有相同的一致性和一致性保證,并遵循上面詳述的語義。 內(nèi)核可能不會分配或釋放零拷貝內(nèi)存,但可能會使用從主機(jī)程序傳入的指向零拷貝的指針。

D.2.2.1.3. Constant Memory

常量是不可變的,不能從設(shè)備修改,即使在父子啟動之間也是如此。 也就是說,所有 __constant__ 變量的值必須在啟動之前從主機(jī)設(shè)置。 所有子內(nèi)核都從各自的父內(nèi)核自動繼承常量內(nèi)存。

從內(nèi)核線程中獲取常量內(nèi)存對象的地址與所有 CUDA 程序具有相同的語義,并且自然支持將該指針從父級傳遞給子級或從子級傳遞給父級。

D.2.2.1.4. Shared and Local Memory

共享內(nèi)存和本地內(nèi)存分別是線程塊或線程私有的,并且在父子之間不可見或不連貫。 當(dāng)這些位置之一中的對象在其所屬范圍之外被引用時,行為未定義,并且可能導(dǎo)致錯誤。

如果 NVIDIA 編譯器可以檢測到指向本地或共享內(nèi)存的指針作為參數(shù)傳遞給內(nèi)核啟動,它將嘗試發(fā)出警告。 在運(yùn)行時,程序員可以使用 __isGlobal() 內(nèi)部函數(shù)來確定指針是否引用全局內(nèi)存,因此可以安全地傳遞給子啟動。

請注意,對 cudaMemcpy*Async() 或 cudaMemset*Async() 的調(diào)用可能會調(diào)用設(shè)備上的新子內(nèi)核以保留流語義。 因此,將共享或本地內(nèi)存指針傳遞給這些 API 是非法的,并且會返回錯誤。

D.2.2.1.5. Local Memory

本地內(nèi)存是執(zhí)行線程的私有存儲,在該線程之外不可見。 啟動子內(nèi)核時將指向本地內(nèi)存的指針作為啟動參數(shù)傳遞是非法的。 從子級取消引用此類本地內(nèi)存指針的結(jié)果將是未定義的。

例如,如果 child_launch 訪問 x_array,則以下內(nèi)容是非法的,具有未定義的行為:

int x_array[10];       // Creates x_array in parent's local memory 
child_launch<<< 1, 1 >>>(x_array);

程序員有時很難知道編譯器何時將變量放入本地內(nèi)存。 作為一般規(guī)則,傳遞給子內(nèi)核的所有存儲都應(yīng)該從全局內(nèi)存堆中顯式分配,或者使用cudaMalloc()、new()或通過在全局范圍內(nèi)聲明__device__存儲。 例如:

// Correct - "value" is global storage
__device__ int value; 
__device__ void x() { 
    value = 5; 
    child<<< 1, 1 >>>(&value); 
}
// Invalid - "value" is local storage
__device__ void y() { 
    int value = 5; 
    child<<< 1, 1 >>>(&value); 
}

D.2.2.1.6. Texture Memory

對紋理映射的全局內(nèi)存區(qū)域的寫入相對于紋理訪問是不連貫的。 紋理內(nèi)存的一致性在子網(wǎng)格的調(diào)用和子網(wǎng)格完成時強(qiáng)制執(zhí)行。 這意味著在子內(nèi)核啟動之前寫入內(nèi)存會反映在子內(nèi)核的紋理內(nèi)存訪問中。 類似地,子進(jìn)程對內(nèi)存的寫入將反映在父進(jìn)程對紋理內(nèi)存的訪問中,但只有在父進(jìn)程同步子進(jìn)程完成之后。 父子并發(fā)訪問可能會導(dǎo)致數(shù)據(jù)不一致。

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

D.3. Programming Interface

D.3.1. CUDA C++ Reference

內(nèi)核可以使用標(biāo)準(zhǔn) CUDA 《《《 》》》 語法從設(shè)備啟動:

kernel_name<<< Dg, Db, Ns, S >>>([kernel arguments]);

Dg 是 dim3 類型,并指定網(wǎng)格(grid)的尺寸和大小

Db 是 dim3 類型,指定每個線程塊(block)的維度和大小

Ns 是 size_t 類型,并指定為每個線程塊動態(tài)分配的共享內(nèi)存字節(jié)數(shù),用于此調(diào)用并添加到靜態(tài)分配的內(nèi)存中。 Ns 是一個可選參數(shù),默認(rèn)為 0。

S 是 cudaStream_t 類型,并指定與此調(diào)用關(guān)聯(lián)的流。 流必須已在進(jìn)行調(diào)用的同一線程塊中分配。 S 是一個可選參數(shù),默認(rèn)為 0。

D.3.1.1.1. Launches are Asynchronous

與主機(jī)端啟動相同,所有設(shè)備端內(nèi)核啟動相對于啟動線程都是異步的。 也就是說,《《《》》》 啟動命令將立即返回,啟動線程將繼續(xù)執(zhí)行,直到它命中一個明確的啟動同步點(diǎn),例如 cudaDeviceSynchronize()。

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

網(wǎng)格啟動會發(fā)布到設(shè)備,并將獨(dú)立于父線程執(zhí)行。 子網(wǎng)格可以在啟動后的任何時間開始執(zhí)行,但不能保證在啟動線程到達(dá)顯式啟動同步點(diǎn)之前開始執(zhí)行。

D.3.1.1.2. Launch Environment Configuration

所有全局設(shè)備配置設(shè)置(例如,從 cudaDeviceGetCacheConfig() 返回的共享內(nèi)存和 L1 緩存大小,以及從 cudaDeviceGetLimit() 返回的設(shè)備限制)都將從父級繼承。 同樣,堆棧大小等設(shè)備限制將保持配置不變。

對于主機(jī)啟動的內(nèi)核,從主機(jī)設(shè)置的每個內(nèi)核配置將優(yōu)先于全局設(shè)置。 這些配置也將在從設(shè)備啟動內(nèi)核時使用。 無法從設(shè)備重新配置內(nèi)核環(huán)境。

D.3.1.2. Streams

設(shè)備運(yùn)行時提供命名和未命名 (NULL) 流。線程塊中的任何線程都可以使用命名流,但流句柄不能傳遞給其他塊或子/父內(nèi)核。換句話說,流應(yīng)該被視為創(chuàng)建它的塊的私有。流句柄不能保證在塊之間是唯一的,因此在未分配它的塊中使用流句柄將導(dǎo)致未定義的行為。

與主機(jī)端啟動類似,啟動到單獨(dú)流中的工作可能會同時運(yùn)行,但不能保證實(shí)際的并發(fā)性。 CUDA 編程模型不支持依賴子內(nèi)核之間的并發(fā)性的程序,并且將具有未定義的行為。

設(shè)備不支持主機(jī)端 NULL 流的跨流屏障語義(詳見下文)。為了保持與主機(jī)運(yùn)行時的語義兼容性,必須使用 cudaStreamCreateWithFlags() API 創(chuàng)建所有設(shè)備流,并傳遞 cudaStreamNonBlocking 標(biāo)志。 cudaStreamCreate() 調(diào)用是僅限主機(jī)運(yùn)行時的 API,將無法為設(shè)備編譯。

由于設(shè)備運(yùn)行時不支持 cudaStreamSynchronize() 和 cudaStreamQuery(),因此當(dāng)應(yīng)用程序需要知道流啟動的子內(nèi)核已完成時,應(yīng)使用 cudaDeviceSynchronize()。

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

D.3.1.2.1. The Implicit (NULL) Stream

在宿主程序中,未命名(NULL)流與其他流具有額外的屏障同步語義(有關(guān)詳細(xì)信息,請參閱默認(rèn)流)。 設(shè)備運(yùn)行時提供在塊中的所有線程之間共享的單個隱式、未命名流,但由于必須使用 cudaStreamNonBlocking 標(biāo)志創(chuàng)建所有命名流,啟動到 NULL 流中的工作不會插入對任何其他流中未決工作的隱式依賴 (包括其他線程塊的 NULL 流)。

D.3.1.3. Events

僅支持 CUDA 事件的流間同步功能。 這意味著支持 cudaStreamWaitEvent(),但不支持 cudaEventSynchronize()、cudaEventElapsedTime() 和 cudaEventQuery()。 由于不支持 cudaEventElapsedTime(),cudaEvents 必須通過 cudaEventCreateWithFlags() 創(chuàng)建,并傳遞 cudaEventDisableTiming 標(biāo)志。

對于所有設(shè)備運(yùn)行時對象,事件對象可以在創(chuàng)建它們的線程塊內(nèi)的所有線程之間共享,但對于該塊是本地的,并且可能不會傳遞給其他內(nèi)核,或者在同一內(nèi)核內(nèi)的塊之間。 不保證事件句柄在塊之間是唯一的,因此在未創(chuàng)建它的塊中使用事件句柄將導(dǎo)致未定義的行為。

D.3.1.4. Synchronization

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

cudaDeviceSynchronize() 函數(shù)將同步線程塊中任何線程啟動的所有工作,直到調(diào)用 cudaDeviceSynchronize() 為止。 請注意,可以從不同的代碼中調(diào)用 cudaDeviceSynchronize()(請參閱塊范圍同步)。

如果調(diào)用線程旨在與從其他線程調(diào)用的子網(wǎng)格同步,則由程序執(zhí)行足夠的額外線程間同步,例如通過調(diào)用 __syncthreads()。

D.3.1.4.1. Block Wide Synchronization

cudaDeviceSynchronize() 函數(shù)并不意味著塊內(nèi)同步。 特別是,如果沒有通過 __syncthreads() 指令進(jìn)行顯式同步,則調(diào)用線程無法對除自身之外的任何線程啟動的工作做出任何假設(shè)。 例如,如果一個塊中的多個線程都在啟動工作,并且所有這些工作都需要一次同步(可能是因?yàn)榛谑录囊蕾囮P(guān)系),則由程序來保證在調(diào)用之前由所有線程提交這項(xiàng)工作 cudaDeviceSynchronize()。

因?yàn)樵试S實(shí)現(xiàn)在從塊中的任何線程啟動時同步,所以很可能多個線程同時調(diào)用 cudaDeviceSynchronize() 將耗盡第一次調(diào)用中的所有工作,然后對后面的調(diào)用沒有影響。

D.3.1.5. Device Management

只有運(yùn)行內(nèi)核的設(shè)備才能從該內(nèi)核控制。 這意味著設(shè)備運(yùn)行時不支持諸如 cudaSetDevice() 之類的設(shè)備 API。 從 GPU 看到的活動設(shè)備(從 cudaGetDevice() 返回)將具有與從主機(jī)系統(tǒng)看到的相同的設(shè)備編號。 cudaDeviceGetAttribute() 調(diào)用可能會請求有關(guān)另一個設(shè)備的信息,因?yàn)榇?API 允許將設(shè)備 ID 指定為調(diào)用的參數(shù)。 請注意,設(shè)備運(yùn)行時不提供包羅萬象的 cudaGetDeviceProperties() API – 必須單獨(dú)查詢屬性。

D.3.1.6. Memory Declarations

D.3.1.6.1. Device and Constant Memory

使用 __device__ 或 __constant__ 內(nèi)存空間說明符在文件范圍內(nèi)聲明的內(nèi)存在使用設(shè)備運(yùn)行時行為相同。 所有內(nèi)核都可以讀取或?qū)懭朐O(shè)備變量,無論內(nèi)核最初是由主機(jī)還是設(shè)備運(yùn)行時啟動的。 等效地,所有內(nèi)核都將具有與在模塊范圍內(nèi)聲明的 __constant__ 相同的視圖。

D.3.1.6.2. Textures & Surfaces

CUDA 支持動態(tài)創(chuàng)建的紋理和表面對象,其中紋理引用可以在主機(jī)上創(chuàng)建,傳遞給內(nèi)核,由該內(nèi)核使用,然后從主機(jī)銷毀。 設(shè)備運(yùn)行時不允許從設(shè)備代碼中創(chuàng)建或銷毀紋理或表面對象,但從主機(jī)創(chuàng)建的紋理和表面對象可以在設(shè)備上自由使用和傳遞。 不管它們是在哪里創(chuàng)建的,動態(tài)創(chuàng)建的紋理對象總是有效的,并且可以從父內(nèi)核傳遞給子內(nèi)核。

注意:設(shè)備運(yùn)行時不支持從設(shè)備啟動的內(nèi)核中的遺留模塊范圍(即費(fèi)米風(fēng)格)紋理和表面。 模塊范圍(遺留)紋理可以從主機(jī)創(chuàng)建并在設(shè)備代碼中用于任何內(nèi)核,但只能由頂級內(nèi)核(即從主機(jī)啟動的內(nèi)核)使用。

D.3.1.6.3. Shared Memory Variable Declarations

在 CUDA C++ 中,共享內(nèi)存可以聲明為靜態(tài)大小的文件范圍或函數(shù)范圍的變量,也可以聲明為外部變量,其大小由內(nèi)核調(diào)用者在運(yùn)行時通過啟動配置參數(shù)確定。 這兩種類型的聲明在設(shè)備運(yùn)行時都有效。

__global__ void permute(int n, int *data) {
   extern __shared__ int smem[];
   if (n <= 1)
       return;

   smem[threadIdx.x] = data[threadIdx.x];
   __syncthreads();

   permute_data(smem, n);
   __syncthreads();

   // Write back to GMEM since we can't pass SMEM to children.
   data[threadIdx.x] = smem[threadIdx.x];
   __syncthreads();

   if (threadIdx.x == 0) {
       permute<<< 1, 256, n/2*sizeof(int) >>>(n/2, data);
       permute<<< 1, 256, n/2*sizeof(int) >>>(n/2, data+n/2);
   }
}

void host_launch(int *data) {
    permute<<< 1, 256, 256*sizeof(int) >>>(256, data);
}

D.3.1.6.4. Symbol Addresses

設(shè)備端符號(即標(biāo)記為 __device__ 的符號)可以簡單地通過 & 運(yùn)算符從內(nèi)核中引用,因?yàn)樗腥址秶脑O(shè)備變量都在內(nèi)核的可見地址空間中。 這也適用于 __constant__ 符號,盡管在這種情況下指針將引用只讀數(shù)據(jù)。

鑒于可以直接引用設(shè)備端符號,那些引用符號的 CUDA 運(yùn)行時 API(例如 cudaMemcpyToSymbol() 或 cudaGetSymbolAddress())是多余的,因此設(shè)備運(yùn)行時不支持。 請注意,這意味著常量數(shù)據(jù)不能在正在運(yùn)行的內(nèi)核中更改,即使在子內(nèi)核啟動之前也是如此,因?yàn)閷?__constant__ 空間的引用是只讀的。

D.3.1.7. API Errors and Launch Failures

與 CUDA 運(yùn)行時一樣,任何函數(shù)都可能返回錯誤代碼。 最后返回的錯誤代碼被記錄下來,并且可以通過 cudaGetLastError() 調(diào)用來檢索。 每個線程都會記錄錯誤,以便每個線程都可以識別它最近生成的錯誤。 錯誤代碼的類型為 cudaError_t。

與主機(jī)端啟動類似,設(shè)備端啟動可能由于多種原因(無效參數(shù)等)而失敗。 用戶必須調(diào)用 cudaGetLastError() 來確定啟動是否產(chǎn)生錯誤,但是啟動后沒有錯誤并不意味著子內(nèi)核成功完成。

對于設(shè)備端異常,例如,訪問無效地址,子網(wǎng)格中的錯誤將返回給主機(jī),而不是由父調(diào)用 cudaDeviceSynchronize() 返回。

D.3.1.7.1. Launch Setup APIs

內(nèi)核啟動是通過設(shè)備運(yùn)行時庫公開的系統(tǒng)級機(jī)制,因此可通過底層 cudaGetParameterBuffer() 和 cudaLaunchDevice() API 直接從 PTX 獲得。 允許 CUDA 應(yīng)用程序自己調(diào)用這些 API,其要求與 PTX 相同。 在這兩種情況下,用戶都負(fù)責(zé)根據(jù)規(guī)范以正確的格式正確填充所有必要的數(shù)據(jù)結(jié)構(gòu)。 這些數(shù)據(jù)結(jié)構(gòu)保證了向后兼容性。

與主機(jī)端啟動一樣,設(shè)備端操作符 《《《》》》 映射到底層內(nèi)核啟動 API。 這樣一來,以 PTX 為目標(biāo)的用戶將能夠啟動加載,并且編譯器前端可以將 《《《》》》 轉(zhuǎn)換為這些調(diào)用。

D.3.1.8. API Reference

此處詳細(xì)介紹了設(shè)備運(yùn)行時支持的 CUDA 運(yùn)行時 API 部分。 主機(jī)和設(shè)備運(yùn)行時 API 具有相同的語法; 語義是相同的,除非另有說明。 下表提供了與主機(jī)可用版本相關(guān)的 API 概覽。

D.3.2. Device-side Launch from PTX

本部分適用于以并行線程執(zhí)行 (PTX) 為目標(biāo)并計(jì)劃在其語言中支持動態(tài)并行的編程語言和編譯器實(shí)現(xiàn)者。 它提供了與在 PTX 級別支持內(nèi)核啟動相關(guān)的底層詳細(xì)信息。

D.3.2.1. Kernel Launch APIs

可以使用可從 PTX 訪問的以下兩個 API 來實(shí)現(xiàn)設(shè)備端內(nèi)核啟動:cudaLaunchDevice() 和 cudaGetParameterBuffer()。 cudaLaunchDevice() 使用通過調(diào)用 cudaGetParameterBuffer() 獲得的參數(shù)緩沖區(qū)啟動指定的內(nèi)核,并將參數(shù)填充到啟動的內(nèi)核。 參數(shù)緩沖區(qū)可以為 NULL,即,如果啟動的內(nèi)核不帶任何參數(shù),則無需調(diào)用 cudaGetParameterBuffer()。

D.3.2.1.1. cudaLaunchDevice

在 PTX 級別,cudaLaunchDevice() 需要在使用前以如下所示的兩種形式之一聲明。

// PTX-level Declaration of cudaLaunchDevice() when .address_size is 64
.extern .func(.param .b32 func_retval0) cudaLaunchDevice 
( 
  .param .b64 func, 
  .param .b64 parameterBuffer, 
  .param .align 4 .b8 gridDimension[12], 
  .param .align 4 .b8 blockDimension[12], 
  .param .b32 sharedMemSize, 
  .param .b64 stream 
) 
;
// PTX-level Declaration of cudaLaunchDevice() when .address_size is 32
.extern .func(.param .b32 func_retval0) cudaLaunchDevice
(
  .param .b32 func,
  .param .b32 parameterBuffer,
  .param .align 4 .b8 gridDimension[12],
  .param .align 4 .b8 blockDimension[12],
  .param .b32 sharedMemSize,
  .param .b32 stream
)
;

下面的 CUDA 級聲明映射到上述 PTX 級聲明之一,可在系統(tǒng)頭文件cuda_device_runtime_api.h中找到。 該函數(shù)在cudadevrt系統(tǒng)庫中定義,必須與程序鏈接才能使用設(shè)備端內(nèi)核啟動功能。

// CUDA-level declaration of cudaLaunchDevice()
extern "C" __device__ 
cudaError_t cudaLaunchDevice(void *func, void *parameterBuffer, 
                             dim3 gridDimension, dim3 blockDimension, 
                             unsigned int sharedMemSize, 
                             cudaStream_t stream);

第一個參數(shù)是指向要啟動的內(nèi)核的指針,第二個參數(shù)是保存已啟動內(nèi)核的實(shí)際參數(shù)的參數(shù)緩沖區(qū)。 參數(shù)緩沖區(qū)的布局在下面的參數(shù)緩沖區(qū)布局中進(jìn)行了說明。 其他參數(shù)指定啟動配置,即網(wǎng)格維度、塊維度、共享內(nèi)存大小以及啟動關(guān)聯(lián)的流(啟動配置的詳細(xì)說明請參見執(zhí)行配置)。

D.3.2.1.2. cudaGetParameterBuffer

cudaGetParameterBuffer()需要在使用前在 PTX 級別聲明。 PTX 級聲明必須采用以下兩種形式之一,具體取決于地址大?。?/p>

// PTX-level Declaration of cudaGetParameterBuffer() when .address_size is 64
// When .address_size is 64
.extern .func(.param .b64 func_retval0) cudaGetParameterBuffer
(
  .param .b64 alignment,
  .param .b64 size
)
;
 // PTX-level Declaration of cudaGetParameterBuffer() when .address_size is 32
.extern .func(.param .b32 func_retval0) cudaGetParameterBuffer
(
  .param .b32 alignment,
  .param .b32 size
)
;

cudaGetParameterBuffer()的以下 CUDA 級聲明映射到上述 PTX 級聲明:

// CUDA-level Declaration of cudaGetParameterBuffer()
extern "C" __device__
void *cudaGetParameterBuffer(size_t alignment, size_t size);

第一個參數(shù)指定參數(shù)緩沖區(qū)的對齊要求,第二個參數(shù)以字節(jié)為單位的大小要求。 在當(dāng)前實(shí)現(xiàn)中,cudaGetParameterBuffer() 返回的參數(shù)緩沖區(qū)始終保證為 64 字節(jié)對齊,忽略對齊要求參數(shù)。 但是,建議將正確的對齊要求值(即要放置在參數(shù)緩沖區(qū)中的任何參數(shù)的最大對齊)傳遞給 cudaGetParameterBuffer() 以確保將來的可移植性。

D.3.2.2. Parameter Buffer Layout

禁止參數(shù)緩沖區(qū)中的參數(shù)重新排序,并且要求放置在參數(shù)緩沖區(qū)中的每個單獨(dú)的參數(shù)對齊。 也就是說,每個參數(shù)必須放在參數(shù)緩沖區(qū)中的第 n 個字節(jié),其中 n 是參數(shù)大小的最小倍數(shù),它大于前一個參數(shù)占用的最后一個字節(jié)的偏移量。 參數(shù)緩沖區(qū)的最大大小為 4KB。

有關(guān) CUDA 編譯器生成的 PTX 代碼的更詳細(xì)說明,請參閱 PTX-3.5 規(guī)范。

D.3.3. Toolkit Support for Dynamic Parallelism

D.3.3.1. Including Device Runtime API in CUDA Code

與主機(jī)端運(yùn)行時 API 類似,CUDA 設(shè)備運(yùn)行時 API 的原型會在程序編譯期間自動包含在內(nèi)。 無需明確包含 cuda_device_runtime_api.h。

D.3.3.2. Compiling and Linking

當(dāng)使用帶有 nvcc 的動態(tài)并行編譯和鏈接 CUDA 程序時,程序?qū)⒆詣渔溄拥届o態(tài)設(shè)備運(yùn)行時庫 libcudadevrt。

設(shè)備運(yùn)行時作為靜態(tài)庫(Windows 上的 cudadevrt.lib,Linux 下的 libcudadevrt.a)提供,必須鏈接使用設(shè)備運(yùn)行時的 GPU 應(yīng)用程序。設(shè)備庫的鏈接可以通過 nvcc 或 nvlink 完成。下面顯示了兩個簡單的示例。

如果可以從命令行指定所有必需的源文件,則可以在一個步驟中編譯和鏈接設(shè)備運(yùn)行時程序:

$ nvcc -arch=sm_35 -rdc=true hello_world.cu -o hello -lcudadevrt

也可以先將 CUDA .cu 源文件編譯為目標(biāo)文件,然后在兩個階段的過程中將它們鏈接在一起:

$ nvcc -arch=sm_35 -dc hello_world.cu -o hello_world.o

$ nvcc -arch=sm_35 -rdc=true hello_world.o -o hello -lcudadevrt

有關(guān)詳細(xì)信息,請參閱 The CUDA Driver Compiler NVCC的使用單獨(dú)編譯部分。

D.4. Programming Guidelines

D.4.1. Basics

設(shè)備運(yùn)行時是主機(jī)運(yùn)行時的功能子集。 API 級別的設(shè)備管理、內(nèi)核啟動、設(shè)備 memcpy、流管理和事件管理從設(shè)備運(yùn)行時公開。

已經(jīng)有 CUDA 經(jīng)驗(yàn)的人應(yīng)該熟悉設(shè)備運(yùn)行時的編程。 設(shè)備運(yùn)行時語法和語義與主機(jī) API 基本相同,但本文檔前面詳細(xì)介紹了任何例外情況。

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

以下示例顯示了一個包含動態(tài)并行性的簡單 Hello World 程序:

#include  

__global__ void childKernel() 
{ 
    printf("Hello "); 
} 

__global__ void parentKernel() 
{ 
    // launch child 
    childKernel<<<1,1>>>(); 
    if (cudaSuccess != cudaGetLastError()) { 
        return; 
    }

    // wait for child to complete 
    if (cudaSuccess != cudaDeviceSynchronize()) { 
        return; 
    } 

    printf("World!\n"); 
} 

int main(int argc, char *argv[]) 
{ 
    // launch parent 
    parentKernel<<<1,1>>>(); 
    if (cudaSuccess != cudaGetLastError()) { 
        return 1; 
    } 

    // wait for parent to complete 
    if (cudaSuccess != cudaDeviceSynchronize()) { 
        return 2; 
    } 

    return 0; 
}

該程序可以從命令行一步構(gòu)建,如下所示:

$ nvcc -arch=sm_35 -rdc=true hello_world.cu -o hello -lcudadevrt

D.4.2. Performance

D.4.2.1. Synchronization

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

一個線程的同步可能會影響同一線程塊中其他線程的性能,即使這些其他線程自己不調(diào)用 cudaDeviceSynchronize() 也是如此。 這種影響將取決于底層實(shí)現(xiàn)。 通常,與顯式調(diào)用 cudaDeviceSynchronize() 相比,在線程塊結(jié)束時完成子內(nèi)核的隱式同步更有效。 因此,如果需要在線程塊結(jié)束之前與子內(nèi)核同步,建議僅調(diào)用 cudaDeviceSynchronize()。

D.4.2.2. Dynamic-parallelism-enabled Kernel Overhead

在控制動態(tài)啟動時處于活動狀態(tài)的系統(tǒng)軟件可能會對當(dāng)時正在運(yùn)行的任何內(nèi)核施加開銷,無論它是否調(diào)用自己的內(nèi)核啟動。 這種開銷來自設(shè)備運(yùn)行時的執(zhí)行跟蹤和管理軟件,并且可能導(dǎo)致性能下降,例如,與從主機(jī)端相比,從設(shè)備進(jìn)行庫調(diào)用時。 通常,鏈接到設(shè)備運(yùn)行時庫的應(yīng)用程序會產(chǎn)生這種開銷。

D.4.3. Implementation Restrictions and Limitations

動態(tài)并行保證本文檔中描述的所有語義,但是,某些硬件和軟件資源依賴于實(shí)現(xiàn),并限制了使用設(shè)備運(yùn)行時的程序的規(guī)模、性能和其他屬性。

D.4.3.1. Runtime

D.4.3.1.1. Memory Footprint

設(shè)備運(yùn)行時系統(tǒng)軟件為各種管理目的預(yù)留內(nèi)存,特別是用于在同步期間保存父網(wǎng)格狀態(tài)的一個預(yù)留,以及用于跟蹤未決網(wǎng)格啟動的第二個預(yù)留。 配置控制可用于減少這些預(yù)留的大小,以換取某些啟動限制。 有關(guān)詳細(xì)信息,請參閱下面的配置選項(xiàng)。

大多數(shù)保留內(nèi)存被分配為父內(nèi)核狀態(tài)的后備存儲,用于在子啟動時進(jìn)行同步。 保守地說,該內(nèi)存必須支持為設(shè)備上可能的最大活動線程數(shù)存儲狀態(tài)。 這意味著可調(diào)用 cudaDeviceSynchronize() 的每個父代可能需要多達(dá) 860MB 的設(shè)備內(nèi)存,具體取決于設(shè)備配置,即使它沒有全部消耗,也將無法供程序使用。

D.4.3.1.2. Nesting and Synchronization Depth

使用設(shè)備運(yùn)行時,一個內(nèi)核可能會啟動另一個內(nèi)核,而該內(nèi)核可能會啟動另一個內(nèi)核,以此類推。每個從屬啟動都被認(rèn)為是一個新的嵌套層級,層級總數(shù)就是程序的嵌套深度。同步深度定義為程序在子啟動時顯式同步的最深級別。通常這比程序的嵌套深度小一,但如果程序不需要在所有級別調(diào)用 cudaDeviceSynchronize() ,則同步深度可能與嵌套深度有很大不同。

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

總體最大嵌套深度限制為 24,但實(shí)際上,真正的限制將是系統(tǒng)為每個新級別所需的內(nèi)存量(請參閱上面的內(nèi)存占用量)。任何會導(dǎo)致內(nèi)核處于比最大值更深的級別的啟動都將失敗。請注意,這也可能適用于 cudaMemcpyAsync(),它本身可能會生成內(nèi)核啟動。有關(guān)詳細(xì)信息,請參閱配置選項(xiàng)。

默認(rèn)情況下,為兩級同步保留足夠的存儲空間。這個最大同步深度(以及因此保留的存儲)可以通過調(diào)用 cudaDeviceSetLimit() 并指定 cudaLimitDevRuntimeSyncDepth 來控制。必須在主機(jī)啟動頂層內(nèi)核之前配置要支持的層數(shù),以保證嵌套程序的成功執(zhí)行。在大于指定最大同步深度的深度調(diào)用 cudaDeviceSynchronize() 將返回錯誤。

在父內(nèi)核從不調(diào)用 cudaDeviceSynchronize() 的情況下,如果系統(tǒng)檢測到不需要為父狀態(tài)保留空間,則允許進(jìn)行優(yōu)化。在這種情況下,由于永遠(yuǎn)不會發(fā)生顯式父/子同步,因此程序所需的內(nèi)存占用量將遠(yuǎn)小于保守的最大值。這樣的程序可以指定較淺的最大同步深度,以避免過度分配后備存儲。

D.4.3.1.3. Pending Kernel Launches

啟動內(nèi)核時,會跟蹤所有關(guān)聯(lián)的配置和參數(shù)數(shù)據(jù),直到內(nèi)核完成。 此數(shù)據(jù)存儲在系統(tǒng)管理的啟動池中。

啟動池分為固定大小的池和性能較低的虛擬化池。 設(shè)備運(yùn)行時系統(tǒng)軟件將首先嘗試跟蹤固定大小池中的啟動數(shù)據(jù)。 當(dāng)固定大小的池已滿時,虛擬化池將用于跟蹤新的啟動。

固定大小啟動池的大小可通過從主機(jī)調(diào)用 cudaDeviceSetLimit() 并指定 cudaLimitDevRuntimePendingLaunchCount 來配置。

D.4.3.1.4. Configuration Options

設(shè)備運(yùn)行時系統(tǒng)軟件的資源分配通過主機(jī)程序的 cudaDeviceSetLimit() API 進(jìn)行控制。 限制必須在任何內(nèi)核啟動之前設(shè)置,并且在 GPU 正在運(yùn)行程序時不得更改。

警告:與父塊的子內(nèi)核顯式同步(即在設(shè)備代碼中使用 cudaDeviceSynchronize())在 CUDA 11.6 中已棄用,并計(jì)劃在未來的 CUDA 版本中刪除。

可以設(shè)置以下命名限制:

D.4.3.1.5. Memory Allocation and Lifetime

cudaMalloc() 和 cudaFree() 在主機(jī)和設(shè)備環(huán)境之間具有不同的語義。 當(dāng)從主機(jī)調(diào)用時,cudaMalloc() 從未使用的設(shè)備內(nèi)存中分配一個新區(qū)域。 當(dāng)從設(shè)備運(yùn)行時調(diào)用時,這些函數(shù)映射到設(shè)備端的 malloc() 和 free()。 這意味著在設(shè)備環(huán)境中,總可分配內(nèi)存限制為設(shè)備 malloc() 堆大小,它可能小于可用的未使用設(shè)備內(nèi)存。 此外,在設(shè)備上由 cudaMalloc() 分配的指針上從主機(jī)程序調(diào)用 cudaFree() 是錯誤的,反之亦然。

請注意,在 PTX 中,%smid 和 %warpid 被定義為 volatile 值。 設(shè)備運(yùn)行時可以將線程塊重新調(diào)度到不同的 SM 上,以便更有效地管理資源。 因此,依賴 %smid 或 %warpid 在線程或線程塊的生命周期內(nèi)保持不變是不安全的。

D.4.3.1.7. ECC Errors

CUDA 內(nèi)核中的代碼沒有可用的 ECC 錯誤通知。 整個啟動樹完成后,主機(jī)端會報(bào)告 ECC 錯誤。 在嵌套程序執(zhí)行期間出現(xiàn)的任何 ECC 錯誤都將生成異?;蚶^續(xù)執(zhí)行(取決于錯誤和配置)。

關(guān)于作者

Ken He 是 NVIDIA 企業(yè)級開發(fā)者社區(qū)經(jīng)理 & 高級講師,擁有多年的 GPU 和人工智能開發(fā)經(jīng)驗(yàn)。自 2017 年加入 NVIDIA 開發(fā)者社區(qū)以來,完成過上百場培訓(xùn),幫助上萬個開發(fā)者了解人工智能和 GPU 編程開發(fā)。在計(jì)算機(jī)視覺,高性能計(jì)算領(lǐng)域完成過多個獨(dú)立項(xiàng)目。并且,在機(jī)器人無人機(jī)領(lǐng)域,有過豐富的研發(fā)經(jīng)驗(yàn)。對于圖像識別,目標(biāo)的檢測與跟蹤完成過多種解決方案。曾經(jīng)參與 GPU 版氣象模式GRAPES,是其主要研發(fā)者。

審核編輯:郭婷

聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報(bào)投訴
  • API
    API
    +關(guān)注

    關(guān)注

    2

    文章

    1501

    瀏覽量

    62025
  • CUDA
    +關(guān)注

    關(guān)注

    0

    文章

    121

    瀏覽量

    13628
收藏 人收藏

    評論

    相關(guān)推薦

    立訊精密入選2024可持續(xù)發(fā)展最佳實(shí)踐案例

    近日,由中國上市公司協(xié)會(以下簡稱中上協(xié))主辦的2024上市公司可持續(xù)發(fā)展大會在京召開。會上,中上協(xié)發(fā)布了2024上市公司可持續(xù)發(fā)展最佳及優(yōu)秀實(shí)踐案例。立訊精密憑借“立志高遠(yuǎn) 訊航可持續(xù)高質(zhì)量發(fā)展”的卓越實(shí)踐,從596篇投稿中脫
    的頭像 發(fā)表于 11-28 13:50 ?238次閱讀

    4G模組之UDP應(yīng)用的最佳實(shí)踐!

    今天說的是4G模組之UDP應(yīng)用,展示最佳實(shí)踐,送你參考。
    的頭像 發(fā)表于 11-08 09:24 ?361次閱讀
    4G模組之UDP應(yīng)用的<b class='flag-5'>最佳</b><b class='flag-5'>實(shí)踐</b>!

    MES系統(tǒng)的最佳實(shí)踐案例

    效率、降低成本、保證產(chǎn)品質(zhì)量。 MES系統(tǒng)的最佳實(shí)踐案例 引言 在當(dāng)今競爭激烈的制造業(yè)環(huán)境中,企業(yè)必須不斷尋求創(chuàng)新和改進(jìn)的方法來保持競爭力。MES系統(tǒng)作為一種關(guān)鍵的信息技術(shù)工具,已經(jīng)被廣泛應(yīng)用于各種制造行業(yè),以實(shí)現(xiàn)生產(chǎn)過程的優(yōu)化和管理。本文將探討MES系統(tǒng)的
    的頭像 發(fā)表于 10-27 09:33 ?923次閱讀

    愛芯元速榮膺最佳技術(shù)實(shí)踐應(yīng)用獎

    愛芯元智車載事業(yè)部(品牌“愛芯元速”)憑借在車載芯片領(lǐng)域的創(chuàng)新技術(shù)研發(fā)實(shí)力以及在推動量產(chǎn)上車方面的卓越成績收獲本屆“金輯獎”的“2024最佳技術(shù)實(shí)踐應(yīng)用獎”。
    的頭像 發(fā)表于 10-25 11:39 ?340次閱讀

    邊緣計(jì)算架構(gòu)設(shè)計(jì)最佳實(shí)踐

    邊緣計(jì)算架構(gòu)設(shè)計(jì)最佳實(shí)踐涉及多個方面,以下是一些關(guān)鍵要素和最佳實(shí)踐建議: 一、核心組件與架構(gòu)設(shè)計(jì) 邊緣設(shè)備與網(wǎng)關(guān) 邊緣設(shè)備 :包括各種嵌入式設(shè)備、傳感器、智能手機(jī)、智能攝像頭等,負(fù)責(zé)采
    的頭像 發(fā)表于 10-24 14:17 ?429次閱讀

    云計(jì)算平臺的最佳實(shí)踐

    云計(jì)算平臺的最佳實(shí)踐涉及多個方面,以確保高效、安全、可擴(kuò)展和成本優(yōu)化的云環(huán)境。以下是一些關(guān)鍵的最佳實(shí)踐: 一、云成本優(yōu)化 詳細(xì)分析云使用情況
    的頭像 發(fā)表于 10-24 09:17 ?362次閱讀

    TMCS110x 布局挑戰(zhàn)和最佳實(shí)踐

    電子發(fā)燒友網(wǎng)站提供《TMCS110x 布局挑戰(zhàn)和最佳實(shí)踐.pdf》資料免費(fèi)下載
    發(fā)表于 09-12 09:23 ?0次下載
    TMCS110x 布局挑戰(zhàn)和<b class='flag-5'>最佳</b><b class='flag-5'>實(shí)踐</b>

    衰減 AMC3301 系列輻射發(fā)射 EMI 的最佳實(shí)踐

    電子發(fā)燒友網(wǎng)站提供《衰減 AMC3301 系列輻射發(fā)射 EMI 的最佳實(shí)踐.pdf》資料免費(fèi)下載
    發(fā)表于 09-11 09:59 ?0次下載
    衰減 AMC3301 系列輻射發(fā)射 EMI 的<b class='flag-5'>最佳</b><b class='flag-5'>實(shí)踐</b>

    毫米波雷達(dá)器件的放置和角度最佳實(shí)踐應(yīng)用

    電子發(fā)燒友網(wǎng)站提供《毫米波雷達(dá)器件的放置和角度最佳實(shí)踐應(yīng)用.pdf》資料免費(fèi)下載
    發(fā)表于 09-09 09:57 ?2次下載
    毫米波雷達(dá)器件的放置和角度<b class='flag-5'>最佳</b><b class='flag-5'>實(shí)踐</b>應(yīng)用

    電機(jī)驅(qū)動器電路板布局的最佳實(shí)踐

    電子發(fā)燒友網(wǎng)站提供《電機(jī)驅(qū)動器電路板布局的最佳實(shí)踐.pdf》資料免費(fèi)下載
    發(fā)表于 09-05 11:33 ?17次下載
    電機(jī)驅(qū)動器電路板布局的<b class='flag-5'>最佳</b><b class='flag-5'>實(shí)踐</b>

    MSP430 FRAM技術(shù)–使用方法和最佳實(shí)踐

    電子發(fā)燒友網(wǎng)站提供《MSP430 FRAM技術(shù)–使用方法和最佳實(shí)踐.pdf》資料免費(fèi)下載
    發(fā)表于 08-23 09:23 ?0次下載
    MSP430 FRAM技術(shù)–使用方法和<b class='flag-5'>最佳</b><b class='flag-5'>實(shí)踐</b>

    RTOS開發(fā)最佳實(shí)踐

    基于RTOS編寫應(yīng)用程序時,有一些要注意事項(xiàng)。在本節(jié)中,您將學(xué)習(xí)RTOS開發(fā)最佳實(shí)踐,例如POSIX合規(guī)性、安全性和功能安全認(rèn)證。
    的頭像 發(fā)表于 08-20 11:24 ?456次閱讀

    熱烈恭賀|開盛暉騰入圍APEC?ESCI最佳實(shí)踐獎候選

    喜訊!固德威智慧能源合作伙伴開盛暉騰成功入圍APEC能源智慧社區(qū)倡議最佳實(shí)踐獎候選名單。在智能電網(wǎng)類中,全國僅4個項(xiàng)目入圍! 04:3 APEC ESCI是于2010年由亞太經(jīng)濟(jì)合作組織
    的頭像 發(fā)表于 04-29 17:31 ?412次閱讀
    熱烈恭賀|開盛暉騰入圍APEC?ESCI<b class='flag-5'>最佳</b><b class='flag-5'>實(shí)踐</b>獎候選

    Keil使用AC6編譯提示CUDA版本過高怎么解決?

    \' ArmClang: warning: Unknown CUDA version 10.2. Assuming the latest supported version 10.1
    發(fā)表于 04-11 07:56

    谷歌Play商店推出并行下載功能,支持多應(yīng)用下載

    此非 Google Play 的首次嘗試。早于 2020 年,Google Play 已啟動多項(xiàng)安卓應(yīng)用的并行下載測試,然而后因技術(shù)緣故而被暫停。如今,再度回歸該功能試驗(yàn)。隨著時間推移,有望逐步推廣至廣大用戶群體。
    的頭像 發(fā)表于 03-08 14:20 ?756次閱讀