CUDA_主機內存



主機內存

系統中被CPU訪問的內存,分爲兩種類型:可分頁內存(pageable memory,通常應用中默認使用)和頁鎖定內存(page-locked或者pinned)。api

可分頁內存即爲經過操做系統api(malloc(), new())分配的存儲器空間;而頁鎖定內存始終不會被分配到低速的虛擬內存中,可以保證存在於物理內存中,而且可以經過DMA加速與設備端的通訊。緩存

爲了讓硬件使用DMA,操做系統容許主機內存進行頁鎖定,而且由於性能緣由,CUDA包含了開發者使用這些操做系統工具的API。頁鎖定後的且映射爲cuda直接訪問的鎖定的內存容許如下幾點:app

  • 更快的傳輸性能;
  • 異步的複製操做(在必要的內存複製結束以前內存複製返回控制給調用者;GPU複製操做與cpu並行的執行);
  • 映射鎖頁內存能夠被cuda內核直接訪問

主機端鎖頁內存

使用pinned memory有不少好處:能夠達到更高的主機-設備端的數據傳送帶寬,若是頁鎖定內存以write-commbined方式分配,帶寬更高;某些設備支持DMA功能,在執行內核函數的同時利用pinned memory進行主機端與設備端之間的通訊;在某些設備上,pinned memory還能夠經過zero-copy功能映射到設備地址空間,從GPU直接訪問,這樣就不用在主存和顯存之間進行數據傳輸。異步

分配鎖頁內存

經過cudaHostAlloc()和cudaFreeHost()來分配和釋放pinned memory。函數


portable memory/可共享鎖頁內存

在使用cudaHostAlloc分配頁鎖定內存時,加上cudaHostAllocPortable標誌,可使多個CPU線程經過共享一塊頁鎖定內存,從而實現cpu線程間的通訊。在默認狀況下,pinned memory由哪一個cpu線程分配,就只有該CPU線程才能訪問這塊空間。而經過portable memory則可讓控制不一樣GPU的幾個CPU線程共享同一塊pinned memory,減小CPU線程間的數據傳輸和通訊。工具


write-combined memory/寫結合鎖頁內存

當CPU對一塊內存中的數據進行處理時,會將這塊內存上的數據緩存到CPU的L一、L2 Cache中,而且須要監視這塊內存中數據的更改,以保證緩存的一致性。性能

通常狀況下,這種機制能夠減小CPU對內存的訪問,但在「CPU生產數據,GPU消費數據」模型中,CPU只須要寫這塊內存數據便可。此時不須要維護緩存一致性,對內存的監視反而會下降性能。操作系統

經過write-combined memory,就能夠不使用CPU的L一、L2 Cache對一塊pinned memory中的數據進行緩衝,而將Cache資源留給其餘程序使用。線程

write-combined memory在PCI-e總線傳輸期間不會被來自CPU的監視打斷,能夠將主機端-設備端傳輸帶寬提升多達40%。指針

在調用cudaHostAlloc()時加上cudaHostAllocWriteCombined標誌,就能夠將一塊pinned memory聲明爲write-combined memory.

因爲對write-combined memory的訪問沒有緩存,CPU從write-combined memory上讀數據時速度會有所降低。因最好只將CPU端只寫的數據存放在write-combined memory中。


mapped memory/映射鎖頁內存

mapped memory擁有兩個地址:主機端地址(內存地址)和設備地址(顯存地址),能夠在kernel中直接訪問mapped memory中的數據,而沒必要再在內存和顯存間進行數據拷貝,即zero-copy功能。若是內核程序只須要對mapped memory進行少許讀寫,這樣作能夠減小分配顯存和數據拷貝的時間。

mapped memory在主機端的指針能夠由cudaHostAlloc()函數得到;它的設備端指針能夠經過cudaHostGetDevicePointer()得到,從kernel中訪問頁鎖定內存,須要將設備端指針做爲參數傳入。

並非全部的設備都支持內存映射,經過cudaGetDeviceProperties()函數返回的cudaMapHostMemory屬性,能夠知道當前設備是否支持mapped memory。若是設備提供支持,能夠在調用cudaHostAlloc()時加上cudaHostAllocMapped標誌,將pinned memory映射到設備地址空間。

因爲mapped memory能夠在CPU端和GPU端訪問,因此必須經過同步來保證CPU和GPU對同一塊存儲器操做的順序一致性。可使用流和事件來防止讀後寫,寫後讀,以及寫後寫等錯誤。

對mapped memory的訪問應該知足與global memory相同的合併訪問要求,以得到最佳性能。

注意:

  • 在執行cuda操做以前,先調用cudaSetDeviceFlags()(加cudaDeviceMapHost標誌)進行鎖頁內存映射。不然,調用cudaHostGetDevicePointer()函數會返回一個錯誤。
  • 多個主機端線程操做的一塊portable memory同時也是mapped memory時,每個主機線程都必須調用cudaHostGetDevicePointer()得到這一塊pinned memory的設備端指針。此時,這一塊內存在每一個主機端線程中都有一個設備端指針。

註冊鎖頁內存

鎖頁內存註冊將內存分配與頁鎖定和主機內存映射分離。能夠實現操做一個已分配的虛擬地址範圍,並頁鎖定它。而後,將其映射給GPU,正如cudaHostAlloc()能夠根據須要讓內存映射到cuda地址空間或變成可共享的(全部GPU可訪問)。

函數cuMemHostRegister()/cudaHostRegister()cuMemHostUnregister()/cudaHostUnregister()分別實現吧主機內存註冊爲鎖頁內存和移除註冊的功能。

註冊的內存範圍必須是頁對齊的;不管是基地址仍是大小,都必須是可被操做系統頁面大小整除。

注意:

當UVA(同一虛擬尋址)有效,全部的鎖頁內存分配均是映射的和可共享的。這一規則的例外是寫結合內存和註冊的內存。對於這兩者,設備指針可能不一樣於主機指針,應用程序須要使用cudaHostGetDevicePointer()/cuMemHostGetDevicePointer()查詢設備指針。
相關文章
相關標籤/搜索