日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Persistent Memory编程简介

發(fā)布時(shí)間:2024/2/28 编程问答 44 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Persistent Memory编程简介 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Persistent Memory編程簡介

    • 編程
      • libpmem
        • 持久化函數(shù)
      • libpmemobj
        • 跟對(duì)象 root object
        • 例程
          • 事務(wù)支持
        • type safety
        • 線程安全
    • 管理工具
      • ipmctl
      • ndctl
        • create-namespace
        • 例子
    • 測試工具
      • fio
      • pmembench
      • ipmwatch
      • emon
      • pcm
    • 參考鏈接


本文主要目的是介紹PM基礎(chǔ)的的編程方法、管理工具、監(jiān)測手段等

編程

  • 持久內(nèi)存開發(fā)套件(Persistent Memory Development Kit-PMDK) - pmem.io: PMDK
  • PMDK based Persistent Memory Programming
  • libpmem

    • libpmem簡介

    peme底層庫,不支持事務(wù),編程方法如下:

    #include <libpmem.h> // 其他頭文件省略 /* using 4k of pmem for this example */ #define PMEM_LEN 4096int main(int argc, char *argv[]) {int fd;char *pmemaddr;int is_pmem;/* 1. 打開pm文件 */if ((fd = open("/pmem-fs/myfile", O_CREAT|O_RDWR, 0666)) < 0) {perror("open");exit(1);}/* 2. 創(chuàng)建固定的文件大小,分配4k大小 */if ((errno = posix_fallocate(fd, 0, PMEM_LEN)) != 0) {perror("posix_fallocate");exit(1);}/* 3. mmap這個(gè)pm文件 */// 這里也可以用系統(tǒng)調(diào)用mmap,只不過pmem版本效率更高// 也可以使用pmem_map_file直接map文件if ((pmemaddr = pmem_map(fd)) == NULL) {perror("pmem_map");exit(1);}// 4. 只要mmap之后,fd就可以關(guān)閉了。close(fd);/* determine if range is true pmem */is_pmem = pmem_is_pmem(pmemaddr, PMEM_LEN);/* 使用libc系統(tǒng)調(diào)用訪問pm,但是這種方法無法確定該數(shù)據(jù)何時(shí)落盤PM,cacheline刷盤的順序也不保證 */// 這里多說一句,cpu的cacheline下刷機(jī)制本身就是沒有順序保證的。strcpy(pmemaddr, "hello, persistent memory");/* 通過正確的方式訪問PM */if (is_pmem) {// 這個(gè)函數(shù)拷貝完后會(huì)直接持久化pmem_memcpy(pmemaddr, buf, cc);} else {memcpy(pmemaddr, buf, cc);pmem_msync(pmemaddr, cc);}/* copy the file, saving ~the last flush step to the end */while ((cc = read(srcfd, buf, BUF_LEN)) > 0) {// 只拷貝,不持久化pmem_memcpy_nodrain(pmemaddr, buf, cc);pmemaddr += cc;}if (cc < 0) {perror("read");exit(1);}/* 和上述的nodrain聯(lián)合使用,持久化數(shù)據(jù) */pmem_drain();/* 持久化cacheline中的數(shù)據(jù) */if (is_pmem)// 通過在用戶態(tài)調(diào)用CLWB and CLFLUSHOPT指令,達(dá)到高效刷盤的目的pmem_persist(pmemaddr, PMEM_LEN);else// 實(shí)際上就是系統(tǒng)調(diào)用msync()pmem_msync(pmemaddr, PMEM_LEN); }

    注意,mmap的一般用法是mmap一個(gè)普通文件,其持久化的方法是使用系統(tǒng)調(diào)用msync()來flush,這個(gè)指令在pmem上是相對(duì)較慢的,所以如果使用pmem(可以用pmem_is_pmem確認(rèn))可以使用pm的persist函數(shù)pmem_persist,可以使用環(huán)境變量PMEM_IS_PMEM_FORCE=1強(qiáng)行指定不適用msync()

    持久化函數(shù)

    以下是目前所有的和持久化相關(guān)的函數(shù)

    #include <libpmem.h>void pmem_persist(const void *addr, size_t len); // 將對(duì)應(yīng)的區(qū)域強(qiáng)制持久化下去,相當(dāng)于調(diào)用msync(),調(diào)用該函數(shù)不需要考慮align(如果不align,底層會(huì)擴(kuò)大sync范圍到align) int pmem_msync(const void *addr, size_t len); // 相當(dāng)于調(diào)用msync,和pmem_persist功能一致。 Since it calls msync(), this function works on either persistent memory or a memory mapped file on traditional storage. pmem_msync() takes steps to ensure the alignment of addresses and lengths passed to msync() meet the requirements of that system call. void pmem_flush(const void *addr, size_t len); // 這個(gè)的粒度應(yīng)該是cacheline void pmem_deep_flush(const void *addr, size_t len); (EXPERIMENTAL) // 不考慮PMEM_NO_FLUSH變量,一定會(huì)flushcpu寄存器 int pmem_deep_drain(const void *addr, size_t len); (EXPERIMENTAL) int pmem_deep_persist(const void *addr, size_t len); (EXPERIMENTAL) void pmem_drain(void); int pmem_has_auto_flush(void); (EXPERIMENTAL) // 檢測CPU是否支持power failure時(shí)自動(dòng)flush cache int pmem_has_hw_drain(void);

    調(diào)用pmem_persist相當(dāng)于調(diào)用了sync和drain

    void pmem_persist(const void *addr, size_t len) {/* flush the processor caches */pmem_flush(addr, len);/* wait for any pmem stores to drain from HW buffers */pmem_drain(); }

    討論x86-64環(huán)境

    • pmem_flush含義是調(diào)用clflush將對(duì)應(yīng)的區(qū)域flush下去。flush系指令的封裝,只不過libpmem會(huì)在裝載時(shí)獲取相關(guān)信息自動(dòng)選擇最優(yōu)的指令
      • CLFLUSH會(huì)命令cpu將對(duì)應(yīng)cacheline逐出,強(qiáng)制性的寫回介質(zhì),這在一定程度上可以解決我們的問題,但是這是一個(gè)同步指令,將會(huì)阻塞流水線,損失了一定的運(yùn)行速度,于是Intel添加了新的指令CLFLUSHOPT和CLWB,這是兩個(gè)異步的指令。盡管都能寫回介質(zhì),區(qū)別在前者會(huì)清空cacheline,后者則會(huì)保留,這使得在大部分場景下CLWB可能有更高的性能。
      • 一般的pmem_memmove(), pmem_memcpy() and pmem_memset()在下發(fā)完成之后都會(huì)flush的,除非指定PMEM_F_MEM_NOFLUSH
    • pmem_drain含義是調(diào)用sfense等待所有的pipline都下刷到PM完成(等待其他的store指令都完成才會(huì)返回)
      • 上面flush異步的代價(jià)是我們對(duì)于cache下刷的順序依舊不可預(yù)測,考慮到有些操作需要順序保證,于是我們需要使用SFENCE提供保證,SFENCE強(qiáng)制sfence指令前的寫操作必須在sfence指令后的寫操作前完成。
    • 考慮到pmem_drain可能會(huì)阻塞一些操作,更好的做法是對(duì)數(shù)據(jù)結(jié)構(gòu)里互不相干的幾個(gè)字段分別flush,最后一并調(diào)用pmem_drain,以將阻塞帶來的問題降到最低。
    • programs using pmem_flush() to flush ranges of memory should still follow up by calling pmem_drain() once to ensure the flushes are complete.
    • 還有一個(gè)flagPMEM_F_MEM_NONTEMPORAL,使用這個(gè)flag下發(fā)的IO,會(huì)繞過CPU cache,直接下刷到PM里。

    The main feature of libpmem library is to provide a method to flush dirty data to persistent memory. Commonly used functions mainly include pmem_flush, pmem_drain, pmem_memcpy_nodrain. Since the timing and sequence of the CPU CACHE content flashing to the PM is not controlled by the user, a specific instruction is required for forced flashing. The function of pmem_flush is to call the CLWB, CLFLUSHOPT or CLFLUSH instructions to force the content in the CPU CACHE (in cache line as a unit) to be flushed to the PM; after the instruction is initiated, because the CPU is multi-core, the order of the content in the cache to the PM is different, so It also needs pmem_drain to call the SFENCE instruction to ensure that all CLWBs are executed. If the instruction called by pmem_flush is CLFLUSH, the instruction contains sfence, so in theory there is no need to call pmem_drain, in fact, if it is this instruction, pmem_drain does nothing.

    The above describes the function of flashing the contents of the CPU cache to the PM. The following describes memory copy, which means copying data from memory to PM. This function is completed by pmem_memcpy_nodrain, calling the MOVNT instruction (MOV or MOVNTDQ), the instruction copy does not go through the CPU CACHE, so this function does not require flush. But you need to establish a sfence at the end to ensure that all data has been copied to the PM.

    libpmemobj

    • libpmemobj簡介
    • libpmemobj api之類的文檔

    libpmem的上層封裝,所有對(duì)pmem的操作都抽象為obj pool的形式。

  • pmemobj_create創(chuàng)建obj pool
  • pmemobj_open打開已經(jīng)創(chuàng)建的obj
  • pmemobj_close關(guān)閉對(duì)應(yīng)的obj
  • pmemobj_check對(duì)metadata進(jìn)行校驗(yàn)
  • libpmemobj的內(nèi)存指針是普通指針的兩倍大,它說明了該pool是指向那個(gè)obj pool的,和其中的offset

    typedef struct pmemoid {uint64_t pool_uuid_lo; // 具體的某個(gè)obj,通過cuckoo hash table的兩層哈希對(duì)應(yīng)到實(shí)際的地址pooluint64_t off; // 對(duì)應(yīng)的offset } PMEMoid; // 我們把它叫做persistent pointer

    因此,從這個(gè)指針數(shù)據(jù)結(jié)構(gòu)需要(void *)((uint64_t)pool + oid.off)這樣的轉(zhuǎn)換,才能轉(zhuǎn)到實(shí)際的地址,這就是pmemobj_direct作的事情。

    跟對(duì)象 root object

    根據(jù)官方的說法,根對(duì)象的作用就是一個(gè)訪問持久內(nèi)存對(duì)象的入口點(diǎn),是一個(gè)錨的作用。使用如下方式

    • pmemobj_root(PMEMobjpool* pop, size_t size):非類型化的原始API。create或者resize根對(duì)象,根據(jù)官方文檔的描述,當(dāng)你初次調(diào)用這個(gè)函數(shù)的時(shí)候,如果size大于0并且沒有根對(duì)象存在,則會(huì)分配空間并創(chuàng)建一個(gè)根對(duì)象。當(dāng)size大于當(dāng)前根對(duì)象的size的時(shí)候會(huì)進(jìn)行重分配并resize。
    • POBJ_ROOT(PMEMobjpool* pop, TYPE):這是一個(gè)宏,傳入的TYPE是根對(duì)象的類型,并且最后返回值類型是一個(gè)void指針

    例程

    #include <stdio.h> #include <string.h> #include <libpmemobj.h>// layout #define LAYOUT_NAME "intro_0" /* will use this in create and open */ #define MAX_BUF_LEN 10 /* maximum length of our buffer */struct my_root {size_t len; /* = strlen(buf) */char buf[MAX_BUF_LEN]; };int main(int argc, char *argv[]) {// 創(chuàng)建poolPMEMobjpool *pop = pmemobj_create(argv[1], LAYOUT_NAME, PMEMOBJ_MIN_POOL, 0666);if (pop == NULL) {perror("pmemobj_create");return 1;}// 創(chuàng)建pm root對(duì)象(已經(jīng)zeroed了),并通過pmemobj_direct將其轉(zhuǎn)化為一個(gè)void指針PMEMoid root = pmemobj_root(pop, sizeof (struct my_root));struct my_root *rootp = pmemobj_direct(root);char buf[MAX_BUF_LEN];// 先給pm對(duì)象賦值rootp->len = strlen(buf);// 然后持久化,記得8byte原子寫pmemobj_persist(pop, &rootp->len, sizeof (rootp->len));// 寫數(shù)據(jù),順便持久化pmemobj_memcpy_persist(pop, rootp->buf, my_buf, rootp->len);// 持久化之后就可以像正常內(nèi)存那樣讀寫了if (rootp->len == strlen(rootp->buf))printf("%s\n", rootp->buf);pmemobj_close(pop);return 0; }
    事務(wù)支持
    /* TX_STAGE_NONE */TX_BEGIN(pop) {/* TX_STAGE_WORK */ } TX_ONCOMMIT {/* TX_STAGE_ONCOMMIT */ } TX_ONABORT {/* TX_STAGE_ONABORT */ } TX_FINALLY {/* TX_STAGE_FINALLY */ } TX_END /* TX_STAGE_NONE */

    整個(gè)事務(wù)的流程可以通過這幾個(gè)宏以及代碼塊來定義,并且將事務(wù)分成了多個(gè)階段,中間的三個(gè)階段為可選的,最基本的一個(gè)事務(wù)流程是TX_BEGIN-TX_END,這也是最常用的部分,其他的幾個(gè)部分在嵌套事務(wù)中使用較多。

    除了基本的事務(wù)代碼塊,libpmemobj還提供了相應(yīng)的事務(wù)操作API。

    一個(gè)是事務(wù)性數(shù)據(jù)寫入API:pmemobj_tx_add_range&pmemobj_tx_add_range_direct,add_range函數(shù)主要有三個(gè)參數(shù):root object、offset以及size,該函數(shù)表示我們將會(huì)操作[offset, offset+size)這段內(nèi)存空間,PMDK將會(huì)自動(dòng)在undo log中分配一個(gè)新的對(duì)象,然后將這段空間的內(nèi)容記錄到undo log中,這樣我們就能隨機(jī)去修改這段空間的內(nèi)容并且保證一致性。帶上direct標(biāo)志的函數(shù)用法一致,區(qū)別在于direct函數(shù)直接操作的是一段虛擬地址空間。

    type safety

    • An introduction to pmemobj (part 3) - types
    • Type safety macros in libpmemobj

    libpmemobj使用了一系列macro來將persistent pointer和某個(gè)具體類型聯(lián)系起來

    FeatureAnonymous unionsNamed unions
    Declaration+-
    Assignment-+
    Function parameter-+
    Type numbers-+

    pmdk/src/examples/libpmemobj/string_store_tx_type/writer.c例程如下:

    #include <stdio.h> #include <string.h> #include <libpmemobj.h>#include "layout.h"int main(int argc, char *argv[]) {if (argc != 2) {printf("usage: %s file-name\n", argv[0]);return 1;}PMEMobjpool *pop = pmemobj_create(argv[1],POBJ_LAYOUT_NAME(string_store), PMEMOBJ_MIN_POOL, 0666);if (pop == NULL) {perror("pmemobj_create");return 1;}char buf[MAX_BUF_LEN] = {0};int num = scanf("%9s", buf);if (num == EOF) {fprintf(stderr, "EOF\n");return 1;}TOID(struct my_root) root = POBJ_ROOT(pop, struct my_root);// D_RW 寫TX_BEGIN(pop) {TX_MEMCPY(D_RW(root)->buf, buf, strlen(buf));} TX_END// D_RO()讀printf("%s\n", D_RO(root)->buf);pmemobj_close(pop);return 0; }

    通過TOID_VALID驗(yàn)證對(duì)應(yīng)的type是否合法

    if (TOID_VALID(D_RO(root)->data)) {/* can use the data ptr safely */ } else {/* declared type doesn't match the object */ }

    在transaction里面可以使用TX_NEW創(chuàng)建新的對(duì)象

    TOID(struct my_root) root = POBJ_ROOT(pop); TX_BEGIN(pop) {TX_ADD(root); /* we are going to operate on the root object */TOID(struct rectangle) rect = TX_NEW(struct rectangle);D_RW(rect)->x = 5;D_RW(rect)->y = 10;D_RW(root)->rect = rect; } TX_END

    線程安全

    所有的libpmemobj函數(shù)都是線程安全的。除了管理obj pool的函數(shù)例如open、close和pmemobj_root,宏里面只有FOREACH的不是線程安全的。

    我們可以將pthread_mutex_t類放到pm里,叫做pmem-aware lock,下面是一個(gè)簡單的例子

    struct foo {PMEMmutex lock;int bar; };int fetch_and_add(TOID(struct foo) foo, int val) {pmemobj_mutex_lock(pop, &D_RW(foo)->lock);int ret = D_RO(foo)->bar;D_RW(foo)->bar += val;pmemobj_mutex_unlock(pop, &D_RW(foo)->lock);return ret; }

    管理工具

    ipmctl

    PM的管理工具

  • ipmctl create -goal PersistentMemoryType=AppDirect創(chuàng)建AppDirect GOAL
  • ipmctl show -firmware查看DIMM固件版本
  • ipmctl show -dimm列出DIMM
  • ipmctl show -sensor獲取更多詳細(xì)信息,類似SMART
  • ipmctl show -topology定位device位置
  • ndctl

    管理“l(fā)ibnvdimm”對(duì)應(yīng)的系統(tǒng)設(shè)備(Non-volatile Memory),常用命令:

  • ndctl list -u
  • create-namespace

    通過fsdax, devdax, sector, and raw這四種方式管理PM的namespace

    • fsdax,默認(rèn)模式,創(chuàng)建之后將在文件系統(tǒng)下創(chuàng)建塊設(shè)備/dev/pmemX[.Y],可以在其上創(chuàng)建xfs、ext4文件系統(tǒng)。**DAX(direct access) removes the page cache from the I/O path and allows mmap to establish direct mappings to persistent memory media.**使用這種的好處是可以多個(gè)進(jìn)程共享同一塊PM。
    • devdax,創(chuàng)建之后在文件系統(tǒng)下創(chuàng)建char device/dev/daxX.Y,沒有塊設(shè)備映射出來。但是使用這種方式仍然可以通過mmap映射。(只可以使用open(),close(),mmap())

    一個(gè)create-namespace的典型命令如下:

    ndctl create-namespace --type=pmem --mode=fsdax --region=X [--align=4k] # --region 指定某個(gè)pmem設(shè)備,不寫的話默認(rèn)是all,全部設(shè)備 # --align,內(nèi)部的對(duì)齊的pagesize,默認(rèn)2M,每次page fault之后讀上2M的頁

    例子

    • 通過FSDAX初始化pmem
    ndctl create-namespace mkfs.xfs -f -d su=2m,sw=1 /dev/pmem0 mkdir /pmem0 mount -o dax /dev/pmem0 /pmem0 xfs_io -c "extsize 2m" /pmem0

    測試工具

    fio

    首先要選ioengine,有以下幾種選擇:

  • libpmem:使用fsdax配置pmem namespace的模式,也是比較常用的模式。這里提供了個(gè)小例子
  • dev-dax:針對(duì)devdax的pmem設(shè)備
  • pmemblk:使用libpmemblk庫讀寫pm
  • mmap:非PM特有,使用posix系統(tǒng)調(diào)用跑IO(mmap、fdatasync…)
    • 默認(rèn)的讀操作是將PM中的數(shù)據(jù)拷貝到內(nèi)存中
    • 默認(rèn)的寫操作是將內(nèi)存中的數(shù)據(jù)拷貝到PM中,--sync=sync或者--sync=dsync或者--sync=1代表每次寫數(shù)據(jù)之后都會(huì)drain,默認(rèn)或者--sync=0代表按需調(diào)用pmem_drain()(調(diào)用pmem_memcpy的時(shí)候會(huì)增加標(biāo)志位PMEM_F_MEM_NODRAIN),使用--direct=1增加標(biāo)志位PMEM_F_MEM_NONTEMPORAL
      • 可以使用fio選項(xiàng)fsync=int或者fdatasync=int,確保在下發(fā)多少個(gè)write命令之后,會(huì)下發(fā)一個(gè)sync也就是pmem_drain()。

    pmembench

    ipmwatch

    查看吞吐,包括PM內(nèi)部真正的讀寫數(shù)據(jù),在Intel VTune Amplifier 2019 since Update 5有包含,安裝vtune_profiler_2020里面肯定有,我把一些數(shù)據(jù)名稱列在下面

    bytes_read (derived) bytes_written (derived) read_hit_ratio (derived) write_hit_ratio (derived) wdb_merge_percent (derived) media_read_ops (derived) media_write_ops (derived) read_64B_ops_received write_64B_ops_received ddrt_read_ops ddrt_write_ops

    emon

    查看耗時(shí)

    pcm

    intel的pcm工具集有一系列工具查看cpu和其訪問memory的性能指標(biāo)。例如pcm-memory.x可以查看當(dāng)前PM的性能指標(biāo)

    |---------------------------------------| |-- Socket 0 --| |---------------------------------------| |-- Memory Channel Monitoring --| |---------------------------------------| |-- Mem Ch 0: Reads (MB/s): 227.67 --| |-- Writes(MB/s): 43.34 --| |-- PMM Reads(MB/s) : 0.00 --| |-- PMM Writes(MB/s) : 0.00 --| |-- Mem Ch 1: Reads (MB/s): 0.00 --| |-- Writes(MB/s): 0.00 --| |-- PMM Reads(MB/s) : 355.99 --| |-- PMM Writes(MB/s) : 355.99 --| |-- Mem Ch 2: Reads (MB/s): 209.37 --| |-- Writes(MB/s): 42.72 --| |-- PMM Reads(MB/s) : 0.00 --| |-- PMM Writes(MB/s) : 0.00 --| |-- Mem Ch 3: Reads (MB/s): 211.65 --| |-- Writes(MB/s): 42.81 --| |-- PMM Reads(MB/s) : 0.00 --| |-- PMM Writes(MB/s) : 0.00 --| |-- Mem Ch 4: Reads (MB/s): 0.00 --| |-- Writes(MB/s): 0.00 --| |-- PMM Reads(MB/s) : 356.08 --| |-- PMM Writes(MB/s) : 356.08 --| |-- Mem Ch 5: Reads (MB/s): 205.36 --| |-- Writes(MB/s): 42.57 --| |-- PMM Reads(MB/s) : 0.00 --| |-- PMM Writes(MB/s) : 0.00 --| |-- NODE 0 Mem Read (MB/s) : 854.05 --| |-- NODE 0 Mem Write(MB/s) : 171.44 --| |-- NODE 0 PMM Read (MB/s): 712.08 --| |-- NODE 0 PMM Write(MB/s): 712.08 --| |-- NODE 0.0 NM read hit rate : 1.00 --| |-- NODE 0.1 NM read hit rate : 1.00 --| |-- NODE 0.2 NM read hit rate : 0.00 --| |-- NODE 0.3 NM read hit rate : 0.00 --| |-- NODE 0 Memory (MB/s): 2449.64 --| |---------------------------------------| |---------------------------------------||---------------------------------------| |-- System DRAM Read Throughput(MB/s): 854.05 --| |-- System DRAM Write Throughput(MB/s): 171.44 --| |-- System PMM Read Throughput(MB/s): 712.08 --| |-- System PMM Write Throughput(MB/s): 712.08 --| |-- System Read Throughput(MB/s): 1566.12 --| |-- System Write Throughput(MB/s): 883.52 --| |-- System Memory Throughput(MB/s): 2449.64 --| |---------------------------------------||---------------------------------------|

    參考鏈接

  • Direct Write to PMem how to disable DDIO
  • Correct, Fast Remote PersistenceDDIO是在CPU層面enable的。
  • 基于RDMA和NVM的大數(shù)據(jù)系統(tǒng)一致性協(xié)議研究
  • pmem/valgrind
  • PMDK based Persistent Memory Programming
  • Running FIO with pmem engines
  • Documentation for ndctl and daxctl
  • AEPWatch
  • CHAPTER 5. USING NVDIMM PERSISTENT MEMORY STORAGE
  • I/O Alignment Considerations里面有一些常用的命令
  • peresistent memory programming the remote access perspective
  • pmem_flush
  • Create Memory Allocation Goal - IPMCTL User Guide
  • 磁盤I:O 性能指標(biāo) 以及 如何通過 fio 對(duì)nvme ssd,optane ssd, pmem 性能摸底
  • 2MB FSDAX 使用2Mpagesize的PM FSDAX namespace
  • 總結(jié)

    以上是生活随笔為你收集整理的Persistent Memory编程简介的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。