由Intel AI 實踐日工作組和第四正規化發起的“英特爾AI實踐日第31期&AI應用與異構記憶體程式設計挑戰賽總動員”線上研討會將於6月10日晚上開播。
屆時,來自英特爾的分享嘉賓胡風華是傲騰事業部雲軟體架構師,將對傲騰PMDK程式設計技術進行簡單介紹,並探討在人工智慧領域的應用前景。
傲騰持久記憶體自2019年正式推出以來,已經在在眾多領域展現出非凡實力,獲得了廣泛讚譽。特別是在人工智慧方面,傲騰已經成功地應用在許多網際網路公司的人工智慧關鍵業務。
傲騰持久記憶體是如何為資料賦能,加速應用落地,本次特邀胡風華撰寫詳解持久記憶體程式設計技術。
01 傲騰持久記憶體及其使用模式
英特爾®傲騰™持久記憶體以創新的記憶體技術重新定義了傳統儲存架構,將高價效比的大容量記憶體與資料永續性巧妙地結合在一起,以合理的價格提供大型持久記憶體層級。憑藉在記憶體密集型工作負載,虛擬機器密度和快速儲存容量方面的突破性效能水平,英特爾®傲騰™持久記憶體 (Intel® Optane™PMem) 可加速IT轉型,以支援資料時代對算力的需求。
全新的PMem 200 系列與第三代英特爾®至強®可擴充套件處理器(Ice Lake, ICX)搭配,入門級PMem系列與第二代英特爾®至強®可擴充套件處理器(Cascade Lake, CLX)搭配,與針對資料庫,資料分析和虛擬化等基礎設施等工作負載打造的軟體生態系統保持相容,有助於更加有效地挖掘資料的潛在價值。開發人員可以利用行業標準的持久記憶體程式設計模式,構建更簡單,更強大的應用,確保對資料中心的投資能夠適應未來的需求。
持久記憶體產品可通過不同的方法使用,有些用法對應用來說是透明的。例如,所有持久記憶體產品都支援儲存介面和標準檔案 API,就像固態盤 (Solid State Disk, SSD) 一樣;或者將持久記憶體配置成記憶體模式,系統的持久記憶體的使用方式和系統記憶體一樣。通過這兩種方式使用持久記憶體非常簡單直接,我們不需要對應用做任何更改就可使用,使用者除了感受到效能的大幅提升以外,甚至感受不到它們的存在。
但這兩種方式也都有各自的弱點,對於第一種方式,因為基本上仍然沿用過去的軟體棧,無法完全發揮傲騰持久記憶體的效能優勢;而對於第二種方式,雖然能獲得大容量的記憶體,但是在訪問延遲效能方面與記憶體相比仍有差距,並不能很好地應對對記憶體延遲要求較高的場景,另外它也無法對資料進行持久化。
要充分發揮傲騰持久記憶體在效能和持久化方面的強大優勢,我們引入了AppDirect模式。在這種模式下,應用可以從使用者空間以類似記憶體的方式直接訪問持久記憶體,不但能夠完全發揮持久記憶體的效能優勢,並對資料進行就地持久化。這種模式需要使用者對應用做出少量修改。
02 持久記憶體的特徵
每項新技術的興起總會引發新的思考,持久記憶體也不例外。構建與開發解決方案時,請考慮持久記憶體的以下特徵:
-
持久記憶體的效能(吞吐量、延遲和頻寬)遠高於 NAND,但是稍低於 DRAM。
-
不同於 NAND,持久記憶體很耐用。其耐用性通常比 NAND 高出多個數量級,可以超過伺服器的生命週期。
-
持久記憶體模組的容量遠大於 DRAM模組,並且可以共享相同的記憶體通道。支援持久記憶體的應用可原地更新資料,無需對資料進行序列化/反序列化處理。
-
持久記憶體支援位元組定址(類似於記憶體)。應用可以只更新所需的資料,不會產生任何讀取-修改-寫入(read-modify-write,RMW)開銷。
-
資料與 CPU 快取記憶體保持一致。持久記憶體可提供直接記憶體訪問 (direct memory access, DMA) 和遠端直接記憶體訪問
(remote direct memory access, RDMA) 操作。 -
寫入持久記憶體的資料不會在斷電後丟失。
-
許可權檢查完成後,可以直接從使用者空間訪問持久記憶體上的資料。資料訪問不經過任何核心程式碼、檔案系統頁面快取(page cache)或中斷。
-
持久記憶體上的資料可立即使用,也就是說:
o 系統通電後即可使用資料。
o 應用不需要花時間來預熱快取記憶體。
o 它們可在記憶體對映後立即訪問資料。 -
持久記憶體上的資料不佔用 DRAM 空間,除非應用將資料複製到 DRAM,以便更快地訪問資料。
-
寫入持久記憶體模組的資料位於系統本地。應用負責在不同系統之間複製資料。
應用開發人員通常會考慮記憶體駐留(memory-resident)資料結構和儲存駐留(storage-resident)資料結構。就資料中心應用而言,開發人員要謹慎地在儲存中保持一致的資料結構,即使系統崩潰時也不例外。
這個問題通常可以使用日誌技巧(如預寫日誌)來解決,先將更改寫入日誌,然後再將其重新整理到持久儲存中。如果資料修改過程中斷,應用可以藉助日誌中的資訊,在重啟時完成恢復操作。這樣的技巧已存在多年;但正確的實施方法開發難度很大,維護起來也很耗時。開發人員通常要依賴於資料庫、程式設計庫和現代檔案系統的組合來提供一致性。
即便如此,最終還是要應用開發人員設計一種策略,在執行時和從應用和系統崩潰中恢復系統時確儲存儲中資料結構的一致性。
03 SNIA NVM程式設計模型
持久記憶體可以被應用程式直接訪問,並將資料執行就地持久化,因此可以讓作業系統支援一種全新的程式設計模型,在提供媲美記憶體的高效能的同時,像非易失性儲存裝置一樣持久儲存資料。
對開發人員而言幸運的是,在第一代持久記憶體還處於開發階段時, Microsoft Windows 和 Linux 設計師、架構師和開發人員已與儲存網路工業協會( SNIA)合作定義了一個持久記憶體程式設計通用程式設計模型 SNIA NVM程式設計模型,如圖1所示。
▲圖1 SNIA NVM程式設計模型
持久記憶體用作塊儲存
作業系統能夠檢測是否存在持久記憶體模組,並將裝置驅動程式載入到作業系統的 I/O 子系統中,如圖 1左半部分所示。非易失性雙列直插式記憶體模組( NVDIMM)驅動程式具有兩項重要功能。
首先,它為系統管理員提供了管理程式介面,用於配置和監控持久記憶體硬體的狀態。其次,它具有類似於儲存裝置驅動程式的功能。NVDIMM 驅動程式將持久記憶體作為快速塊儲存裝置呈現給應用程式和作業系統模組。
這意味著應用程式、檔案系統、卷管理器和其他儲存中介軟體層可以不經修改,像當前使用儲存那樣使用持久記憶體。
持久記憶體感知型檔案系統
作業系統的另一項擴充套件是支援檔案系統感知並針對持久記憶體進行優化。這類檔案系統被稱為持久記憶體感知型檔案系統(PMem-Aware File System)。
當前持久記憶體感知檔案系統包括 Linux ext4 和 XFS,以及 Microsoft Windows NTFS。如圖 1所示,這些檔案系統既可以使用 I/O 子系統中的塊驅動程式(如上文所述),也可以繞過 I/O 子系統,直接將持久記憶體用作可位元組定址載入 / 儲存的記憶體,以最快、最短的路徑訪問儲存在持久記憶體中的資料。
除了清除了傳統的 I/O 操作外,這條路徑使得小資料塊寫入的執行速度比傳統塊儲存裝置更快,因為相比之下,傳統塊儲存裝置要求檔案系統讀取裝置的原生塊大小,修改塊,然後將整個資料塊寫回到裝置。這些持久記憶體感知型檔案系統嚮應用程式提供熟悉的標準檔案 API,包括 open、close、 read 和 write 系統呼叫。這樣應用程式可以繼續使用熟悉的檔案 API,同時發揮持久記憶體更高效能的優勢。
持久記憶體直接訪問
作業系統中的持久記憶體直接訪問特性(在 Linux 和 Windows 中被稱為 Direct Access,DAX)使用作業系統所提供的記憶體對映檔案介面,但卻能充分利用持久記憶體的原生功能,既能儲存資料,又能用作記憶體。持久記憶體可以原生地對映成應用程式記憶體,因此作業系統無須在易失性主記憶體中快取檔案。
為了使用 DAX,系統管理員需要在持久記憶體模組上建立一個檔案系統,並將該檔案系統掛載在作業系統的檔案系統樹中。對於 Linux 使用者,持久記憶體裝置將顯示為 /dev/pmem*裝置特殊檔案。若要顯示持久記憶體物理裝置,系統管理員可以使用ndctl 和 ipmctl 程式。
04 持久記憶體開發套件PMDK
如前文提到,應用程式可以通過使用記憶體對映檔案的方式來直接訪問持久記憶體,但是這需要使用者修改它們的應用程式。為了降低使用者修改應用的成本,我們引入了持久記憶體開發套件(PMDK)。我們將介紹如何利用PMDK來修改應用,使得其高效管理駐留在持久記憶體中的可位元組定址的資料結構。
PMDK 庫基於 SNIA NVM 程式設計模型構建。它們對該模型進行了不同程度的擴充套件,一些只是將作業系統提供的原語封裝成簡單易用的函式,另一些則提供了複雜的資料結構和演算法以便用於持久記憶體。這意味著你要負責決定哪個抽象級別最適合你的用例。
PMDK 經過多年的發展,目前已經包含了大量的開源庫,如圖2所示。這些庫能幫助應用開發人員和系統管理員簡化持久記憶體裝置的應用開發和管理。
PMDK 提供兩種型別的庫:
1) 易失性庫:適用於只想利用持久記憶體大容量這一優勢的使用者和場景。
2) 永續性庫:適用於想實現故障安全持久記憶體演算法的軟體。
▲ 圖 2 PMDK 相關的開發庫
libmemkind
libmemkind是構建在 jemalloc 之上的使用者可擴充套件堆管理器,支援控制記憶體特性以及在不同型別的記憶體之間對堆進行分割槽。記憶體的型別由應用於虛擬地址範圍的作業系統記憶體策略進行定義。在沒有使用者擴充套件的情況下, memkind 支援的記憶體特性包括控制非一致性記憶體訪問( NUMA)和記憶體頁大小特性。
jemalloc 非標準介面經過擴充套件,支援專門的型別通過 memkind 分割槽介面從作業系統請求虛擬記憶體。通過其他 memkind 介面,你可以控制和擴充套件記憶體分割槽特性和分配記憶體,同時選擇已啟用的特性。藉助 memkind 介面,你可以從支援 PMEM 型別的持久記憶體建立和控制基於檔案支援的記憶體。
libmemcache
libvmemcache 是一種可嵌入式輕量級記憶體快取解決方案,可以通過高效、可擴充套件的記憶體對映,充分利用大容量儲存,例如支援 DAX的持久記憶體。libvmemcache 具有自身的獨特性:
- 基於區間的記憶體分配器可避免出現影響大多數記憶體資料庫的碎片化問題,並支援快取在大多數工作負載中實現極高的空間利用率。
- 緩衝的最近最少使用( LRU)演算法將傳統的 LRU 雙向連結串列和非阻塞環形緩衝區相結合,可在現代多核 CPU 上實現較高的可擴充套件性。
- critnib 索引結構可提供高效能,同時非常節省空間。快取經過調優,能夠以最佳方式處理大小相對較大的值,最小為 256 位元組,但libvmemcache 最適用於預期數值大小超過 1 KB 的情況。
pmemkv
pmemkv 是為持久記憶體而優化設計的通用嵌入式本地鍵值儲存。它易於使用,且附帶許多不同的語言整合,包括 C、 C++ 和 JavaScript。
該庫還有面向不同儲存引擎可插拔的後端外掛。儘管設計之初主要是用於支援永續性的應用程式場景,然而它也可用作易失性庫。pmemkv 作為單獨的專案建立,不僅為 PMDK 中的一套庫提供雲原生支援,還 提 供 面 向 持 久 內 存 構 建 的 鍵 值 API。pmemkv 開發人員的主要目標之一是為開源社群建立一個友好的環境,支援他們在PMDK 的幫助下開發新引擎,並將它與其他程式語言整合。
pmemkv API 與大多數鍵值資料庫類似在底層,它實現了多種儲存引擎。這些儲存引擎都具備出色的靈活性和功能性。每種引擎都具有不同的效能特點,用於解決不同的問題。因此,每種引擎提供的功能各不相同,可以通過以下特性來描述:
- 持久:持久引擎確保修改能夠得到儲存,而且是斷電安全的,而易失性引擎僅在應用程式生命週期內保留其內容。
- 併發:併發引擎確保某些方法(如 get()、 put()、 remove())是執行緒安全的。
- 鍵的排序:“排序”(sorted)引擎提供範圍查詢方法(如 get_above())。pmemkv
與其他鍵值資料庫的不同之處在於,它支援直接訪問資料。這意味著從持久記憶體中讀取資料不需要複製到 DRAM 中。
直接訪問資料可以顯著加快應用程式的速度。在程式僅對資料庫中儲存的部分資料感興趣時,這種優勢最為明顯。在傳統方法中,需要將所有資料複製到某個緩衝區中,然後將其返回給應用程式。藉助 pmemkv,我們為應用程式提供直接指標,應用程式僅讀取所需的資料即可。
憑藉其模組化設計、靈活的引擎 API 以及與許多常見雲程式語言的整合, pmemkv 已經成為許多雲原生軟體開發人員的首選。作為一種開源輕量級庫,它可以輕鬆整合到現有應用程式中,並立即發揮持久記憶體的優勢;或者以此為基礎針對特定的應用需求進行開發。
libpmemobj
libpmemobj 是一種提供事務物件儲存的 C 庫,可為持久記憶體程式設計提供動態記憶體分配器、事務和常規功能。libpmemobj 庫為需要事務和持久記憶體管理的應用程式提供了事務性物件,該物件以直接訪問( DAX)的方式儲存在持久記憶體中。該庫可以解決在持久記憶體程式設計時遇到的許多常見的演算法和資料問題。
libpmemobj支援應用程式在持久記憶體感知型檔案系統上記憶體對映檔案,提供直接載入 / 儲存操作,而無須從塊儲存裝置中對塊進行分頁。它可以繞過核心,避免上下文切換和中斷,並支援應用程式在可位元組定址的持久記憶體中直接讀寫。
libpmemobj 庫提供便捷的 API,用於輕鬆管理記憶體池的建立和訪問,避免直接對映和資料同步的複雜性。PMDK 還提供了 pmempool 程式,用於通過命令列來管理記憶體池。對於持久記憶體,可以使用 pmemobj_alloc()、 pmemobj_reserve()或 pmemobj_ xreserve() 為臨時物件保留記憶體,其使用方法與 malloc() 相同。
libpmemobj 提供以下 三組API:原子操作 API,保留 / 釋出 API,事務 API,可以使用它們來實現資料的持久化。這些API都提供了故障安全原子性和一致性,可減少建立應用程式時的錯誤,同時確保資料的完整性。
Libpmemobj-cpp
libpmemobj 庫面向底層系統軟體開發人員和語言建立人員,提供了分配器、事務以及自動操作物件的方法。然而由於它不會修改編譯器,導致它的 API 冗長,且包含大量的巨集。為了簡化持久記憶體程式設計,減少錯誤,英特爾建立了針對 libpmemobj 的高階語言繫結,並將其新增至 PMDK。其中C++語言繫結是libpmemobj-cpp ,也稱 libpmemobj++,是一種 C++ 僅標頭檔案(header-only)庫,可以使用C++ 的超程式設計特性,為 libpmemobj 提供更簡單且更不易出錯的介面。
它通過重複使用 C++程式設計師熟悉的概念(如智慧指標和閉包事務),提供了便捷的 API 來修改結構體和類,而只需對函式進行細微改動,從而支援快速開發持久記憶體應用程式。該庫還附帶相容 STL 的定製資料結構和容器,因此應用程式開發人員無須重新為持久記憶體開發基本演算法。
Libpmem
libpmem 是一種低階 C 庫,可針對作業系統呈現的原語提供基本抽象功能。它可以自動檢測平臺中的功能,選擇合適的永續性語義以及面向持久記憶體優化的記憶體傳輸( memcpy())方法。大多數應用程式都至少需要使用這個庫的一部分。
libpmem負責處理與持久記憶體相關的 CPU 指令,以最佳方式將資料複製到持久記憶體以及檔案對映。
libpmem 庫包含一些用於記憶體對映檔案的便捷函式。使用這些函式替代作業系統提供的記憶體對映函式,有如下優點:
- libpmem 可以保證作業系統對映呼叫的正確引數。
- libpmem 可以檢測對映是否為持久記憶體以及使用 CPU 指令直接重新整理是否安全。
- libpmem 也提供多個介面支援以最佳方式複製或清零持久記憶體區域以對資料進行持久化。
程式設計師如果只想完全原始地訪問持久記憶體並且無須庫提供分配器或事務功能,那麼可能想將libpmem 用作開發的基礎。
對於大多數程式設計師而言, libpmem 非常底層,可以獲得最大的程式設計靈活性,但是由此帶來的編碼和除錯的代價相對較高,如果不是必須,應該考慮使用更加上層的庫來提高開發效率,降低除錯成本。
05 持久記憶體程式設計資源
英特爾®傲騰™持久記憶體介紹:
https://www.intel.cn/content/www/cn/zh/products/memory-storage/optane-dc-persistent-memory.html
SNIA NVM 程式設計模型規範:
https://www.snia.org/tech_activities/standards/curr_standards/npm
PMDK官方網站:
https://pmem.io/
書籍:持久記憶體程式設計中文版
https://item.jd.com/13201774.html
PMDK:
https://github.com/pmem/pmdk
libmemkind:
http://memkind.github.io/memkind/
libvmemcache:
https://github.com/pmem/vmemcache
pmemkv:
https://github.com/pmem/pmemkv
libpmemobj-cpp:
https://github.com/pmem/libpmemobj-cpp
本文作者:胡風華/Cloud Software Architect
胡風華,英特爾傲騰事業部雲軟體架構師,致力於探索持久記憶體在雲端計算,大資料、人工智慧和物聯網等領域的應用創新。曾在阿里巴巴集團和EMC中國研發中心擔任軟體架構師和軟體工程經理, 在檔案系統,分散式儲存系統,雲端計算和大資料等領域有超過15年的工作經驗。
6月10日晚19:30-21:00
胡風華老師將做客線上研討會
英特爾AI實踐日第31期 -AI應用與異構記憶體程式設計挑戰賽總動員
研討會對傲騰PMDK程式設計技術進行簡單介紹,並探討在人工智慧領域的應用前景。研討會同時重磅官宣AI 應用與異構記憶體程式設計挑戰賽,賽事官網:https://opensource.4paradigm.com/ai2021/ 。
通過此次比賽,你將會對人工智慧如何在異構記憶體架構上受益有全新的認識,步入技術新境界。