DAY90:閱讀Data Migration and Coherency

我們正帶領大家開始閱讀英文的《CUDA C Programming Guide》,今天是第90天,我們正在講解Unified Memory Programming,希望在接下來的10天裏,您可以學習到原汁原味的CUDA,同時能養成英文閱讀的習慣。

關注微信公衆號,可以看到之前的閱讀

本文共計621字,閱讀時間15分鐘

K.1.3. Data Migration and Coherency

Unified Memory attempts to optimize memory performance by migrating data towards the device where it is being accessed (that is, moving data to host memory if the CPU is accessing it and to device memory if the GPU will access it). Data migration is fundamental to Unified Memory, but is transparent to a program. The system will try to place data in the location where it can most efficiently be accessed without violating coherency.

The physical location of data is invisible to a program and may be changed at any time, but accesses to the data’s virtual address will remain valid and coherent from any processor regardless of locality. Note that maintaining coherence is the primary requirement, ahead of performance; within the constraints of the host operating system, the system is permitted to either fail accesses or move data in order to maintain global coherence between processors.

GPU architectures of compute capability lower than 6.x do not support fine-grained movement of the managed data to GPU on-demand. Whenever a GPU kernel is launched all managed memory generally has to be transfered to GPU memory to avoid faulting on memory access. With compute capability 6.x a new GPU page faulting mechanism is introduced that provides more seamless Unified Memory functionality. Combined with the system-wide virtual address space, page faulting provides several benefits. First, page faulting means that the CUDA system software doesn’t need to synchronize all managed memory allocations to the GPU before each kernel launch. If a kernel running on the GPU accesses a page that is not resident in its memory, it faults, allowing the page to be automatically migrated to the GPU memory on-demand. Alternatively, the page may be mapped into the GPU address space for access over the PCIe or NVLink interconnects (mapping on access can sometimes be faster than migration). Note that Unified Memory is system-wide: GPUs (and CPUs) can fault on and migrate memory pages either from CPU memory or from the memory of other GPUs in the system.

K.1.4. GPU Memory Oversubscription

Devices of compute capability lower than 6.x cannot allocate more managed memory than the physical size of GPU memory.

Devices of compute capability 6.x extend addressing mode to support 49-bit virtual addressing. This is large enough to cover the 48-bit virtual address spaces of modern CPUs, as well as the GPU’s own memory. The large virtual address space and page faulting capability enable applications to access the entire system virtual memory, not limited by the physical memory size of any one processor. This means that applications can oversubscribe the memory system: in other words they can allocate, access, and share arrays larger than the total physical capacity of the system, enabling out-of-core processing of very large datasets. cudaMallocManaged will not run out of memory as long as there is enough system memory available for the allocation.

K.1.5. Multi-GPU Support

For devices of compute capability lower than 6.x managed memory allocation behaves identically to unmanaged memory allocated using cudaMalloc(): the current active device is the home for the physical allocation, and all other GPUs receive peer mappings to the memory. This means that other GPUs in the system will access the memory at reduced bandwidth over the PCIe bus. Note that if peer mappings are not supported between the GPUs in the system, then the managed memory pages are placed in CPU system memory (“zero-copy” memory), and all GPUs will experience PCIe bandwidth restrictions. See Managed Memory with Multi-GPU Programs on pre-6.x Architectures for details.

Managed allocations on systems with devices of compute capability 6.x are visible to all GPUs and can migrate to any processor on-demand.

本文備註/經驗分享:

今天的章節則繼續介紹Unified Memory的一些底層細節,以便讓用戶知道這種便利從何而來。此外,今天后兩段落還將介紹新老架構上,兩代Unified Memory的優缺點(3.x/5.x的Kepler/Maxwell上的老Unified Memory,和Pascal+上的新Unified Memory)。

首先,Unified Memory提供了全自動的數據移動:在維護了系統內部的訪存一致性(Coherence)的前提下,Unified Memory將通過全自動的數據移動(或者數據映射),來顯示各個存儲器的訪存效果最優化。例如說,在沒有全面的NVLink的機器上,CPU訪存可能需要將數據移動到內存中,然後訪問。而在有全面的CPU<--->GPU的NVLink的機器上(例如某POWER?),CPU可能會直接通過NVLink訪問某GPU的顯存。這些都是有可能的。請注意本章節強調了“一致性”是Unified Memory最重要的目標,不能出現爲了Unified Memory的方便性,而讓CPU或者GPU訪問到錯誤的,或者不一致的數據。

按照本章節的原話說,一致性是要比性能更重要,這也是顯然的。再快的實現,一旦結果是錯誤的,將無意義。請注意非CS出身的用戶,一致(coherency)的問題在多個處理器同時能訪存的系統上都存在,

建議自行搜索瞭解相關信息.

此外,在有了一致性保證的基礎上,Unified Memory所提供的全自動的數據遷移服務,是對用戶程序(或者說代碼)透明的,用戶不需要手工的寫怎麼去做,這種遷移就將自動的發生,將自動的將屬於放置到某處理器所能訪到的最佳位置(例如某GPU自己的顯存,也可能是其他地方),而不需要用戶去操心。這樣,有了一致性和全自動的數據移動,就構建成了Unified Memory所提供的這種所有的CPU和GPU都能訪問到的,虛擬的,統一的存儲器結構---它的方便性昨天你已經看到了,現在你知道它是大致怎麼實現的了。

然後今天章節還介紹一下老Unified Memory的缺點,請注意老Unified Memory所能提供的便利和數據優化位置存儲,自動遷移,依然都是有的,這裏說的缺點,只是說老一代的卡(精確的說,兩代,Kepler和Maxwell)具有不好的方面。主要缺點在於,老卡的MMU(內存管理單元,昨天的章節已經讓你搜索它的基本功能了,不要讓名字迷惑---這很大程度的是一種內存虛擬化單元),不具有細粒度(例如按照頁面大小)的Page Fault能力。這裏又引入了一個新的操作系統課程名詞,缺頁異常/錯誤(Page Fault),

對於非CS專業的用戶來說,可以簡單的理解成,這是實現現代的、高級的虛擬內存服務的基礎,(具體細節你需要搜索一下,然後閱讀一下,這是一個大話題),

老鳥玩家以前在DOS時代玩遊戲見到的DOS/4GW,還是現在的Windows/Linux上提供的虛擬內存/交換文件/交換分區,都依賴於他。這不過這些是CPU上的。你看缺頁異常處理能力,在CPU上能幹這麼多的事情。我們今天章節裏面遇到的Page Fault Handling能力,則是對GPU說的。老一代的卡不具有精細的缺頁異常處理能力,所以在老的Unified Memory上,在kernel啓動前,要麼(1)它所需要訪存的數據只能提前被整體遷移到自己的顯存上準備好,要麼(2)能在支持P2P Access的平臺上,遷移到另外一張卡的顯存上,要麼(3)存放在內存上,使用類似的Zero-Copy Memory的訪問(內存映射的顯存的方式)訪問。但是無論這三種的哪一種方式爲你提供Unified Memory的底層存儲,都存在問題。如果提前將數據全部移動到卡的顯存上,則顯然你的kernel能用的Unified Memory大小無法超過當前卡的顯存容量,同時這還增加了數據整體移動的成本。這是顯然的。

而如果將Unified Memory的數據放到夥伴卡的顯存上,通過P2P Access訪問,則還存在訪問速度慢(老卡只能用PCI-E走數據訪問夥伴卡的顯存),甚至有些系統不能支持P2P Access的情況(例如:當前芯片組或者CPU集成的PCI-E交換機不能支持的情況下)。

而放置到內存,則多卡訪問的時候內存瓶頸或者PCI-E瓶頸都可能是個性能上的災難。所以你看,雖然老卡(Kepler/Maxwell)上能用,但是還存在諸多缺點的。用戶應當認爲,老卡上的Unified Memory,在適當輕量使用的情況下,Just Works,但性能不會太好。不僅僅如此,老卡因爲需要提前數據準備好位置(在Kernel啓動前),還會導致CPU和GPU無法同時訪問數據的情況,請考慮kernel正在運行中,數據在顯存裏,kernel還沒結束,突然CPU說,我想訪問一下里面的數據。。。這就尷尬了(這種情況下在老卡上會直接掛掉)。不過幸好時代在進步,Pascal起,雖然作爲通用計算並無太大的變化(增加了幾條通用的FP16或者INT8指令),但是在Unified Memory這種CUDA的輔助特性上,則進行了很強大的增強。6.X+的GPU的Unified Memory進化到了第二代,上面所遇到的所有問題難點都消失了。新卡的MMU單元,具有完善的缺頁異常和硬件/Driver處理能力。

數據現在可以任意存在在內存,自己卡的顯存(你即將運行kernel的卡),或者夥伴卡的顯存上,

kernel啓動前,不需要關心數據具體在哪裏,只要kernel在執行過程中,SM發現數據不在顯存,它會動態的細粒度的將數據訪問到,或者遷移到自己的顯存訪問到。並不需要整體在啓動前遷移好或者準備好數據。剛纔你已經知道,哪怕是全自動的遷移或者移動/複製數據,這成本是很大的。例如某kernel需要使用1GB的緩衝區,但是實際上該kernel某次運行只用了裏面的256MB,但是具體哪256MB的數據和問題的動態變化有關,不能提前知道,則之前老卡不僅僅需要將這256MB自動的複製或者移動到自己的顯存,剩下的1GB的768MB數據也都需要被整體移動,這浪費了3/4的傳輸。現在的新卡上的新MMU的細粒度缺頁能力,改善了這一點。同時,之前因爲往往需要數據整體移動,導致Unified Memory不能超過自己的顯存容量的問題也解決了,

因爲隨時可以細粒度的小部分小部分的移動,某卡完全可以自己,例如3GB的顯存,使用6GB的容量,反正又不需要一次性的移動完數據,我用到哪些移動哪些就是了。

(這是GPU硬件和顯卡驅動驅動,顯卡驅動聰明) Unified Memory的改進是硬件和驅動上的,kernel不需要改動。這樣,就帶來了今天章節說的,Pascal+上的超量分配能力: 我一張只有3GB顯存的卡,在使用了Unified Memory的情況下,我可以分配8GB的大小,具體細節可以看一下本章節,這裏我簡單的介紹一下。 在這種情況下,8GB的實際總數量可能分佈在內存中一部分,夥伴卡中一部分,以及,本卡中一部分。而實際上,本卡中的具體某時某刻,裏面的3GB,是總8GB的具體哪一部分,則是動態變化的。所以實際上,這時候,在超量分配的情況下,本卡的小容量顯存,是總的大容量的Unified Memory的一個動態的緩衝,你可以認爲此時,本卡的3GB顯存變成了, 整個GPU系統(例如一個雙路CPU+4卡GPU的服務器)的緩存,根據你已經學過的計算機課程(好吧,似乎沒有照顧非CS專業出身的用戶。。), 此時等效的,將變成,在增強的二代Unified Memory的情況下,此時一張卡的顯存將變成整體系統的大容量存儲器的一個緩存。例如,你可以看成是一個3GB的L3 Cache。此時根據緩存的一定特性,理想狀態下(注意是理想狀態,和訪存的模型有關),你等效於能最好情況以將近這3GB顯存的訪問速度,使用將近整個系統的大容量。還是非常誘人的特性。這也是競爭對手A家從Vega卡還是(4代GCN),所提供的特性, 競爭對手A家在這個情況下,叫自己的HBM顯存爲“High Bandwidth Cache”,可見一斑。實際上,這個特性非常重要(注意,Windows下用不了)。歷年來NV的GTC總是會宣傳一下。 此圖演示了CPU在使用大容量存儲器,老卡(K40)在使用較大容量的顯存,以及,新卡(P100)在NVLink和PCI-E,以及附帶的Unified Memory的Hints操作下(這個我們後續章節說),的性能和應用情況。 首先大家看到藍色的是CPU,該U在使用從1.4GB到58.6GB的working set(即數據工作集,具體概念請參考操作系統課程)的情況下,都能正常(內存較大麼)工作,但是性能也是最低的(因爲CPU最慢)。而K40(Kepler,老卡,1代Unified Memory)則在自己的顯存容量限制內,能正常工作。而到了P100(新卡,2代Unified Memory),則出現了較好的現象(橙色,綜合,綠色),它能在自己的顯存容量限制內工作(16GB,P100),性能不錯。 而隨着問題的規模的增加,例如數據增加到28.9到58.6這麼大,超出了P100的16GB顯存容量限制,P100依然成功通過新的Unified Memory超量分配機制,正常的運行了kernel。此時依然比直接能用大內存的CPU,還是快很多的。這是一個很迷人的特性。也解決了之前的一個難題:內存容量大,但是CPU慢;顯存容量小,但是GPU快。如今通過Unified Memory在新卡(P100)上的超量分配能力,你使用的Unified Memory容量大如內存,卻依然能發揮GPU的高性能。

有不明白的地方,請在本文後留言

或者在我們的技術論壇bbs.gpuworld.cn上發帖

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章