傲騰持久內存如何為數據賦能,加速應用落地?

由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/
通過此次比賽,你將會對人工智能如何在異構內存架構上受益有全新的認識,步入技術新境界。