系統中被CPU訪問的內存,分爲兩種類型:可分頁內存(pageable memory,通常應用中默認使用)和頁鎖定內存(page-locked或者pinned)。api
可分頁內存即爲經過操做系統api(malloc(), new())分配的存儲器空間;而頁鎖定內存始終不會被分配到低速的虛擬內存中,可以保證存在於物理內存中,而且可以經過DMA加速與設備端的通訊。緩存
爲了讓硬件使用DMA,操做系統容許主機內存進行頁鎖定,而且由於性能緣由,CUDA包含了開發者使用這些操做系統工具的API。頁鎖定後的且映射爲cuda直接訪問的鎖定的內存容許如下幾點:app
使用pinned memory有不少好處:能夠達到更高的主機-設備端的數據傳送帶寬,若是頁鎖定內存以write-commbined方式分配,帶寬更高;某些設備支持DMA功能,在執行內核函數的同時利用pinned memory進行主機端與設備端之間的通訊;在某些設備上,pinned memory還能夠經過zero-copy功能映射到設備地址空間,從GPU直接訪問,這樣就不用在主存和顯存之間進行數據傳輸。異步
經過cudaHostAlloc
()和cudaFreeHost
()來分配和釋放pinned memory。函數
在使用cudaHostAlloc
分配頁鎖定內存時,加上cudaHostAllocPortable
標誌,可使多個CPU線程經過共享一塊頁鎖定內存,從而實現cpu線程間的通訊。在默認狀況下,pinned memory由哪一個cpu線程分配,就只有該CPU線程才能訪問這塊空間。而經過portable memory則可讓控制不一樣GPU的幾個CPU線程共享同一塊pinned memory,減小CPU線程間的數據傳輸和通訊。工具
當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擁有兩個地址:主機端地址(內存地址)和設備地址(顯存地址),能夠在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相同的合併訪問要求,以得到最佳性能。
注意:
cudaSetDeviceFlags()
(加cudaDeviceMapHost
標誌)進行鎖頁內存映射。不然,調用cudaHostGetDevicePointer()
函數會返回一個錯誤。鎖頁內存註冊將內存分配與頁鎖定和主機內存映射分離。能夠實現操做一個已分配的虛擬地址範圍,並頁鎖定它。而後,將其映射給GPU,正如cudaHostAlloc()
能夠根據須要讓內存映射到cuda地址空間或變成可共享的(全部GPU可訪問)。
函數cuMemHostRegister()/cudaHostRegister()
和cuMemHostUnregister()/cudaHostUnregister()
分別實現吧主機內存註冊爲鎖頁內存和移除註冊的功能。
註冊的內存範圍必須是頁對齊的;不管是基地址仍是大小,都必須是可被操做系統頁面大小整除。
注意:
當UVA(同一虛擬尋址)有效,全部的鎖頁內存分配均是映射的和可共享的。這一規則的例外是寫結合內存和註冊的內存。對於這兩者,設備指針可能不一樣於主機指針,應用程序須要使用cudaHostGetDevicePointer()/cuMemHostGetDevicePointer()查詢設備指針。