傲騰持久記憶體如何為數據賦能,加速應用落地?

由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 持久記憶體編程資源

英特爾®傲騰™持久記憶體介紹:
//www.intel.cn/content/www/cn/zh/products/memory-storage/optane-dc-persistent-memory.html
SNIA NVM 編程模型規範:
//www.snia.org/tech_activities/standards/curr_standards/npm
PMDK官方網站:
//pmem.io/
書籍:持久記憶體編程中文版
//item.jd.com/13201774.html
PMDK:
//github.com/pmem/pmdk
libmemkind:
//memkind.github.io/memkind/
libvmemcache:
//github.com/pmem/vmemcache
pmemkv:
//github.com/pmem/pmemkv
libpmemobj-cpp:
//github.com/pmem/libpmemobj-cpp

本文作者:胡風華/Cloud Software Architect
胡風華,英特爾傲騰事業部雲軟體架構師,致力於探索持久記憶體在雲計算,大數據、人工智慧和物聯網等領域的應用創新。曾在阿里巴巴集團和EMC中國研發中心擔任軟體架構師和軟體工程經理, 在文件系統,分散式存儲系統,雲計算和大數據等領域有超過15年的工作經驗。

6月10日晚19:30-21:00
胡風華老師將做客線上研討會
英特爾AI實踐日第31期 -AI應用與異構記憶體編程挑戰賽總動員
在這裡插入圖片描述
研討會對傲騰PMDK編程技術進行簡單介紹,並探討在人工智慧領域的應用前景。研討會同時重磅官宣AI 應用與異構記憶體編程挑戰賽,賽事官網://opensource.4paradigm.com/ai2021/
通過此次比賽,你將會對人工智慧如何在異構記憶體架構上受益有全新的認識,步入技術新境界。