色哟哟视频在线观看-色哟哟视频在线-色哟哟欧美15最新在线-色哟哟免费在线观看-国产l精品国产亚洲区在线观看-国产l精品国产亚洲区久久

0
  • 聊天消息
  • 系統消息
  • 評論與回復
登錄后你可以
  • 下載海量資料
  • 學習在線課程
  • 觀看技術視頻
  • 寫文章/發帖/加入社區
會員中心
創作中心

完善資料讓更多小伙伴認識你,還能領取20積分哦,立即完善>

3天內不再提示

io_uring內核各個組件的性能

科技綠洲 ? 來源:Linux開發架構之路 ? 作者:Linux開發架構之路 ? 2023-11-10 11:46 ? 次閱讀

先看看性能

io_uring 需要內核版本在5.1 及以上才支持,liburing的編譯安裝 很簡單,直接clone 官方的代碼,sudo make && sudo make install 就好了,本文是在內核5.12版本 上測試的。

在描述io_uring 的性能之前,我們先直接看一組實測數據:

這組數據是在3D XPoint 介質的硬盤 :optane-5800上測試的,optane5800 能夠提供(randread100% 150w/s , randwrite 100% 150w/s)的性能。

圖片

進行測試的fio 腳本如下:

# aio
[global]
ioengine=libaio
direct=0
randrepeat=1
threads=8
runtime=15
time_based
size=1G
directory=../test-data
group_reporting
[read256B-rand]
bs=4096
rw=randread
numjobs=1
iodepth=128

# io_uring
[global]
ioengine=io_uring
sqthread_poll=1 #開啟io_uring sq_poll模式
direct=1
randrepeat=1
threads=8
runtime=15
time_based
size=1G
directory=../test-data
group_reporting
[read256B-rand]
bs=4096
rw=randread
numjobs=1
iodepth=128

通過上面的測試,我們能夠得到如下幾個結論:

  1. 這種高隊列深度的測試下,可以看到io_uring 在開啟sq_poll之后的性能 相比于aio 的高隊列深度的處理能力好接近一倍;
  2. 在較低隊列深度 以及不開啟 sq_poll 模式的情況下,io_uring 整體沒有太大的優勢,或者說一樣的性能。
  3. 在buffer I/O (direct=0) 下,io_uring 也不會有太大的優勢,因為都得通過 os-cache 來操作。

需要注意的是,如果aio和io_uring 在高并發下(jobs 的數目不斷增加),都是可以達到當前磁盤的性能瓶頸的。

AIO 的基本實現

那有這樣的測試現象,我們可能會有一些疑問,就這性能?我們在nvme上做軟件,希望發揮的是整個磁盤的性能,而不是比拼誰的隊列深度大,誰的優勢更大。。。我用aio 做batch 也能達到磁盤的性能瓶頸,為什么要選擇 對于數據庫/存儲 領域來說 好像“如日中天”的io_uring呢。

我們先來看看aio 的大體實現,沒有涉及到源代碼。aio 主要提供了三個系統調用:

  • io_setup 初始化一些內核態的數據結構
  • io_submit 用于用戶態提交io 請求
  • io_getevents 用于io 請求處理完成之后的io 收割

圖片

大體的IO調度過程如下:

  1. io_setup 完成一些內核數據結構的初始化(包括內核中的 aio 調度隊列,aio_ring_info 的ring-buffer緩沖區)
  2. 用戶態構造一批io請求,通過io_submit 拷貝請求到內核態io 隊列(文件系統之上,上圖沒有體現出來)之后返回到用戶態。
  3. 內核態繼續通過內核i/o 棧處理io請求,處理完成之后 通過 aio_complete 函數將處理完成的請求放入到 aio_ring_info,每一個io請求是一個io_event。
  4. 用戶態通過 io_getevents 系統調用 從 aio_ring_info(ring-buffer) 的head 拿處理完成的io_event,如果head==tail,則表示這個ring-buffer是空的。拿到之后,將拿到的io_event 一批從內核態拷貝到用戶態。

如果單純看 誰能將磁盤性能完整發揮出來,那毋庸置疑,大家都可以;那為什么做存儲的對io_uring 的出現如此熱衷呢?我們就得結合實際的應用場景來看看兩者之間的差異了:

  1. 使用AIO的話,請求調度都需要直接由通用塊層來調度處理,所以需要O_DIRECT標記。這就意味著,使用AIO的應用都無法享受os cache,這對與存儲應用來說并不友好,cache都得自己來維護,而且顯然沒有os page-cache性能以及穩定性有優勢。而使用io_uring 則沒有這樣的限制,當然,io_uring在 buffer I/O下顯然沒有太大的優勢。
  2. 延時上的開銷。AIO 提交用戶請求的時候 通過io_submit調用,收割用戶請求的時候通過io_getevents,正常應用的時候每一個請求都意味著至少兩次系統調用(I/O提交和I/O收割),而對于io_uring來說,I/O 提交和I/O收割都可以 offload 給內核。這樣相比于AIO 來說,io_uring能夠極大得減少 系統調用引入的上下文切換。
  3. io_uring 能夠支持針對submit queue的polling,啟動一個內核線程進行polling,加速請求的提交和收割;對于aio來說,這里就沒有這樣的機制。

總的來說,io_uring 能夠保證上層應用 對系統資源(cache)正常使用的同時 ,降低應用 下發的請求延時和CPU的開銷,在單實例高隊深下,能夠顯著優于同等隊深下的AIO性能。

io_ring 使用

io_uring 基本接口

io_uring的用戶態API 提供了三個系統調用,io_uring_setup,io_uring_enter,io_uring_register。

  • int io_uring_setup(u32 entries, struct io_uring_params *p); 這個接口 用于創建 擁有 entries 個請求的 提交隊列(SQ) 和 完成隊列(CQ),并且返回給用戶一個fd。這個fd可以用做在同一個uring實例上 用戶空間和內核空間共享sq和cq 隊列,這樣能夠避免在請求完成時不需要從完成隊列拷貝數據到用戶態了。io_uring_params 主要是根據用戶的配置來設置uring 實例的創建行為。包括 單不限于開啟 IORING_SETUP_IOPOLL 和 IORING_SETUP_SQPOLL 兩種 poll 模式。后面會細說。
  • int io_uring_register(unsigned int fd, unsigned int opcode, void *arg, unsigned int nr_args);這個接口主要用于注冊用戶態和內核態共享的緩沖區,即將 setup 返回的fd中的數據結構 映射到共享內存,從而進一步減少用戶I/O 提交到uring 隊列中的開銷。
  • int io_uring_enter(unsigned int fd, unsigned int to_submit, unsigned int min_complete, unsigned int flags, sigset_t *sig);這個接口既能夠提交 新的I/O請求 ,又能夠支持I/O收割。

liburing 的使用

可以從上面的幾個系統調用能夠簡單看到 用戶在自主使用這三個系統調用來調度 I/O請求時 還是比較麻煩的,像io_uring_setup 之后的fd,我們用戶層想要使用創建好的sq/cq ,則需要自主進行mmap,并且維護用戶態的sq/cq 數據結構,并在后續的 enter 中自主進行用戶態的sq 的填充。這個過程相對來說還是比較麻煩的。更不要說用三個系統調用中數十個的flags的靈活配置,如果全部結合起來,對于剛接觸io_uring的用戶來說還是需要較大的學習成本。

比如,我想啟動io_uring,并初始化好用戶態的sq/cq 數據結構,就需要寫下面這一些代碼:

int app_setup_uring(struct submitter *s) {
struct app_io_sq_ring *sring = &s->sq_ring;
struct app_io_cq_ring *cring = &s->cq_ring;
struct io_uring_params p;
void *sq_ptr, *cq_ptr;

/*
* We need to pass in the io_uring_params structure to the io_uring_setup()
* call zeroed out. We could set any flags if we need to, but for this
* example, we don't.
* */
memset(&p, 0, sizeof(p));
s->ring_fd = io_uring_setup(QUEUE_DEPTH, &p);
if (s->ring_fd < 0) {
perror("io_uring_setup");
return 1;
}

/*
* io_uring communication happens via 2 shared kernel-user space ring buffers,
* which can be jointly mapped with a single mmap() call in recent kernels.
* While the completion queue is directly manipulated, the submission queue
* has an indirection array in between. We map that in as well.
* */

int sring_sz = p.sq_off.array + p.sq_entries * sizeof(unsigned);
int cring_sz = p.cq_off.cqes + p.cq_entries * sizeof(struct io_uring_cqe);

/* In kernel version 5.4 and above, it is possible to map the submission and
* completion buffers with a single mmap() call. Rather than check for kernel
* versions, the recommended way is to just check the features field of the
* io_uring_params structure, which is a bit mask. If the
* IORING_FEAT_SINGLE_MMAP is set, then we can do away with the second mmap()
* call to map the completion ring.
* */
if (p.features & IORING_FEAT_SINGLE_MMAP) {
if (cring_sz > sring_sz) {
sring_sz = cring_sz;
}
cring_sz = sring_sz;
}

/* Map in the submission and completion queue ring buffers.
* Older kernels only map in the submission queue, though.
* */
sq_ptr = mmap(0, sring_sz, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_POPULATE,
s->ring_fd, IORING_OFF_SQ_RING);
if (sq_ptr == MAP_FAILED) {
perror("mmap");
return 1;
}

if (p.features & IORING_FEAT_SINGLE_MMAP) {
cq_ptr = sq_ptr;
} else {
/* Map in the completion queue ring buffer in older kernels separately */
// 放置內存被page fault
cq_ptr = mmap(0, cring_sz, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_POPULATE,
s->ring_fd, IORING_OFF_CQ_RING);
if (cq_ptr == MAP_FAILED) {
perror("mmap");
return 1;
}
}
/* Save useful fields in a global app_io_sq_ring struct for later
* easy reference */
sring->head = sq_ptr + p.sq_off.head;
sring->tail = sq_ptr + p.sq_off.tail;
sring->ring_mask = sq_ptr + p.sq_off.ring_mask;
sring->ring_entries = sq_ptr + p.sq_off.ring_entries;
sring->flags = sq_ptr + p.sq_off.flags;
sring->array = sq_ptr + p.sq_off.array;

/* Map in the submission queue entries array */
s->sqes = mmap(0, p.sq_entries * sizeof(struct io_uring_sqe),
PROT_READ | PROT_WRITE, MAP_SHARED | MAP_POPULATE,
s->ring_fd, IORING_OFF_SQES);
if (s->sqes == MAP_FAILED) {
perror("mmap");
return 1;
}

/* Save useful fields in a global app_io_cq_ring struct for later
* easy reference */
cring->head = cq_ptr + p.cq_off.head;
cring->tail = cq_ptr + p.cq_off.tail;
cring->ring_mask = cq_ptr + p.cq_off.ring_mask;
cring->ring_entries = cq_ptr + p.cq_off.ring_entries;
cring->cqes = cq_ptr + p.cq_off.cqes;

return 0;
}

所以Jens Axboe將三個系統調用做了一個封裝,形成了liburing,在這里面我想要初始化一個uring實例,并完成用戶態的數據結構的映射,只需要調用下面io_uring_queue_init這個接口:

struct io_uring ring;
struct io_uring_params p = { };
int ret;
ret = io_uring_queue_init(IORING_MAX_ENTRIES, &ring, IORING_SETUP_IOPOLL);

關于liburing的使用,可以看下面這個100行的小案例:

大體的功能就是利用io_uring 去讀一個用戶輸入的文件,每次讀請求的大小是4K,讀完整個文件結束。

#include
#include
#include
#include
#include
#include
#include
#include "liburing.h"

#define QD 4

int main(int argc, char *argv[])
{
struct io_uring ring;
int i, fd, ret, pending, done;
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
struct iovec *iovecs;
struct stat sb;
ssize_t fsize;
off_t offset;
void *buf;

if (argc < 2) {
printf("%s: filen", argv[0]);
return 1;
}
// 初始化io_uring,并拿到初始化的結果,0是成功的,小于0 是失敗的
ret = io_uring_queue_init(QD, &ring, 0);
if (ret < 0) {
fprintf(stderr, "queue_init: %sn", strerror(-ret));
return 1;
}

// 打開用戶輸入的文件
fd = open(argv[1], O_RDONLY | O_DIRECT);
if (fd < 0) {
perror("open");
return 1;
}

// 將文件屬性放在sb中,主要是獲取文件的大小
if (fstat(fd, &sb) < 0) {
perror("fstat");
return 1;
}

// 拆分成 設置的 io_uring支持的最大隊列深度 個請求,4個
fsize = 0;
iovecs = calloc(QD, sizeof(struct iovec));
for (i = 0; i < QD; i++) {
if (posix_memalign(&buf, 4096, 4096))
return 1;
iovecs[i].iov_base = buf;
iovecs[i].iov_len = 4096;
fsize += 4096;
}

// 構造請求,并存放在 seq中
offset = 0;
i = 0;
do {
sqe = io_uring_get_sqe(&ring);
if (!sqe)
break;
io_uring_prep_readv(sqe, fd, &iovecs[i], 1, offset);
offset += iovecs[i].iov_len;
i++;
if (offset > sb.st_size)
break;
} while (1);

// 提交請求sqe 中的請求到內核
ret = io_uring_submit(&ring);
if (ret < 0) {
fprintf(stderr, "io_uring_submit: %sn", strerror(-ret));
return 1;
} else if (ret != i) {
fprintf(stderr, "io_uring_submit submitted less %dn", ret);
return 1;
}

done = 0;
pending = ret;
fsize = 0;
// 等待內核處理完所有的請求,并由用戶態拿到cqe,表示請求處理完成
for (i = 0; i < pending; i++) {
ret = io_uring_wait_cqe(&ring, &cqe);
if (ret < 0) {
fprintf(stderr, "io_uring_wait_cqe: %sn", strerror(-ret));
return 1;
}

done++;
ret = 0;
if (cqe->res != 4096 && cqe->res + fsize != sb.st_size) {
fprintf(stderr, "ret=%d, wanted 4096n", cqe->res);
ret = 1;
}
fsize += cqe->res;
io_uring_cqe_seen(&ring, cqe);
if (ret)
break;
}

// 最后輸出 提交的請求的個數(4k),完成請求的個數,總共處理的請求大小
printf("Submitted=%d, completed=%d, bytes=%lun", pending, done,
(unsigned long) fsize);
close(fd);
io_uring_queue_exit(&ring);
return 0;
}

編譯: gcc -O2 -D_GNU_SOURCE -o io_uring-test io_uring-test.c -luring

運行: ./io_uring-test test-file.txt

io_uring 非poll 模式下 的實現

接下來記錄一下io_uring的實現,來填之前說到的一些小坑,當然…這里描述的內容也是站在前人的肩膀 以及 自己經過一些測試驗證總體來看的。

io_uring 能夠支持其他多種I/O相關的請求:

  • 文件I/O:read, write, remove, update, link,unlink, fadivse, allocate, rename, fsync等
  • 網絡I/O:send, recv, socket, connet, accept等
  • 進程間通信:pipe

還是以 上面案例中 io_uring 處理read 請求為例, 通過io_uring_prep_readv來填充之前已經創建好的sqe。

static inline void io_uring_prep_readv(struct io_uring_sqe *sqe, int fd,
const struct iovec *iovecs,
unsigned nr_vecs, __u64 offset)
{
// 調度讀請求,將構造好的iovecs 中的內容填充到sqe中。
io_uring_prep_rw(IORING_OP_READV, sqe, fd, iovecs, nr_vecs, offset);
}

static inline void io_uring_prep_rw(int op, struct io_uring_sqe *sqe, int fd,
const void *addr, unsigned len,
__u64 offset)
{
sqe->opcode = (__u8) op;
...
sqe->fd = fd;
sqe->off = offset;
sqe->addr = (unsigned long) addr;
sqe->len = len;
...
sqe->__pad2[0] = sqe->__pad2[1] = 0;
}

那我們需要先回到最開始的io_uring_setup以及 后續的mmap setup返回的結果 之后 用戶態和內核態共享的數據結構內容。

圖片

數據結構 在內存中的分布 如上圖:

1.io_uring_setup 之后,會將內核中創建好的一塊內存區域 用 fd標識 以及各個數據結構在這個內存區域中的偏移量存放在io_uring_params中, 通過mmap 來將這部分內存區域的數據結構映射到用用戶空間。

其中io_uring_params 中的 關鍵數據結構如下:

struct io_uring_params {
__u32 sq_entries; // sq 隊列的個數
__u32 cq_entries; // cq 隊列的個數
__u32 flags; // setup設置的一些標識,比如是否開啟內核的io_poll 或者 sq_poll等
__u32 sq_thread_cpu; // 設置sq_poll 模式下 輪詢的cpu 編號
__u32 sq_thread_idle;
__u32 features;
__u32 wq_fd;
__u32 resv[3];
struct io_sqring_offsets sq_off; // sq的偏移量
struct io_cqring_offsets cq_off; // cq的偏移量
};

2.Mmap 之后的內存形態就是上圖中的數據結構形態,mmap的過程就是填充用戶態可訪問的sq/cq。

  • SQ ,submission queue,保存用戶空間提交的請求的地址,實際的用戶請求會存放在io_uring_sqe的sqes中。
struct io_uring_sq {
unsigned *khead;
unsigned *ktail;
...
struct io_uring_sqe *sqes; // 較為復雜的數據結構,保存請求的實際內容
unsigned sqe_head;
unsigned sqe_tail;
...
};

用戶空間的sq更新會追加到SQ 的隊尾部,內核空間消費 SQ 時則會消費隊頭。

  • CQ, complete queue,保存內核空間完成請求的地址,實際的完成請求的數據會存放在io_uring_cqe的cqes中。
struct io_uring_cq {
unsigned *khead;
unsigned *ktail;
...
struct io_uring_cqe *cqes;
...
};

內核完成IO 收割之后會將請求填充到cqes 中,并更新cq 的隊尾,用戶空間則會從cq的隊頭消費 處理完成的請求。

3.在前面的read 案例代碼中,調用的liburing 的函數 io_uring_get_sqe 就是在用戶空間更新sq的隊尾部。

struct io_uring_sqe *io_uring_get_sqe(struct io_uring *ring)
{
struct io_uring_sq *sq = &ring->sq;
unsigned int head = io_uring_smp_load_acquire(sq->khead);
unsigned int next = sq->sqe_tail + 1;
struct io_uring_sqe *sqe = NULL;

// 當前sq的 tail 和 head之間的容量滿足sq的大小,則將當前請求的填充到sqe中
// 并更新sq 的隊尾,向上移動
if (next - head <= *sq->kring_entries) {
sqe = &sq->sqes[sq->sqe_tail & *sq->kring_mask];
sq->sqe_tail = next;
}
return sqe;
}

后續,內核處理完成之后,用戶空間從cq中獲取 處理完成的請求時則會調用io_uring_wait_cqe_nr進行收割。

io_uring 中的ring就是 上圖中的io 鏈路,從sq隊尾進入,最后請求從cq 隊頭出來,整個鏈路就是一個環形(ring)。而sq和cq在數據結構上被存放在了 io_uring 中。加了uring 中的u 猜測是指用戶態(userspace)可訪問的,目的是好的,不過讀起來的單詞諧音就讓一些人略微尷尬(urine。。。)

非poll 模式下的內核火焰圖調用棧如下:

圖片

io_uring poll模式下的實現

我們在最開始的性能測試過程中可以看到在開啟 poll 之后,io_uring的性能才能顯著提高。

我們從前面 io_uring 內存分布圖 中可以看到在內核調度兩個隊列請求的過程中 可以通過異步輪詢的方式進行調度的,也就是io_uring的 poll模式。

io_uring 在io_uring_setup的時候可以通過設置flag 來開啟poll模式,io-uring 支持兩種方式poll模式。

  • IORING_SETUP_IOPOLL,這種方式是由nvme 驅動支持的 io_poll。即用戶態通過io_uring_enter提交請求到內核的文件讀寫隊列中即可,nvme驅動會不斷得輪詢文件讀寫隊列進行io消費,同時用戶態在設置IORING_ENTER_GETEVENTS得flag之后,還需要不斷得調用io_uring_enter 通過io_iopoll_check 調用內核接口查看 nvme的io_poll 是否完成任務調度,從而進行填充 cqes。

如果使用nvme驅動,則需要單獨開啟io_poll 才能真正讓 IORING_SETUP_IOPOLL 配置生效。開啟的話,直接嘗試 root 用戶操作:echo 1 > /sys/block/nvme2n1/queue/io_poll,成功則表示開啟。如果出現bash: echo: write error: Invalid argument ,則表示當前nvme驅動還不支持,需要通過驅動層打開這個配置才行,可以嘗試執行如下步驟:如果執行之前,通過modinfo nvme 查看當前設備是否有nvme驅動失敗,則需要先編譯當前內核版本的nvme驅動才行,否則下面的操作沒有nvme驅動都是無法進行的。

  1. umount fs , 卸載磁盤上掛載的文件系統
  2. echo 1 > /sys/block/nvme0n1/device/device/remove , 將設備從當前服務器移除
  3. rmmod nvme
  4. modprobe nvme poll_queues=1, 重新加載nvme驅動,來支持io_poll的隊列深度為1
  5. echo 1 > /sys/bus/pci/rescan ,重新將磁盤加載回來
  • IORING_SETUP_SQPOLL,這種模式的poll則是我們fio測試下的 sqthread_poll開啟的配置。開啟之后io_uring會啟動一個內核線程,用來輪詢submit queue,從而達到不需要系統調用的參與就可以提交請求。用戶請求在用戶空間提交到SQ 之后,這個內核線程處于喚醒狀態時會不斷得輪詢SQ,也就可以立即捕獲到這次請求。(我們前面的案例中會先在用戶空間構造指定數量的SQ放到ring-buffer中,再由io_uring_enter一起提交到內核),這個時候有了sq_thread 的輪詢,只要用戶空間提交到SQ,內核就能夠捕獲到并進行處理。如果sq_thread 長時間捕獲不到請求,則會進入休眠狀態,需要通過調用io_uring_enter系統調用,并設置IORING_SQ_NEED_WAKEUP來喚醒sq_thread。

大體的調度方式如下圖:

圖片

這種sq_thread 內核對SQ的輪詢模式能夠極大得減少請求在submit queue中的排隊時間,同時減少了io_uring_enter系統調用的開銷。

圖片

開啟sq_thread之后的輪詢模式可以看到 用戶提交請求 對CPU消耗僅僅只占用了一小部分的cpu。

io_uring 在 rocksdb 中的應用

Rocksdb 針對io_uring的調用大體類似前面提到的使用liburing 接口實現的一個read 文件的案例,同樣是調用io_uring_prep_readv 來實現對文件的讀寫。

Io_uring 的特性決定了在I/O層 的批量讀才能體現它的優勢,所以rocksdb 將io_uring集成到了 MultiGet 中的 MultiRead 接口之中。

需要注意的是 rocksdb 設置的 io_uring的SQ 隊列深度大小是256,且setup的時候并沒有開啟sq_poll模式,而是默認開啟io_poll,即flag是0;如果想要開啟sq_poll模式,則需要變更這個接口的flags配置,比如將0設置為IORING_SETUP_SQPOLL,然后重新編譯源代碼即可。

inline struct io_uring* CreateIOUring() {
struct io_uring* new_io_uring = new struct io_uring;
int ret = io_uring_queue_init(kIoUringDepth, new_io_uring, 0);
if (ret) {
delete new_io_uring;
new_io_uring = nullptr;
}
return new_io_uring;
}

大家在使用db_bench測試io_uring的時候 如果不變更rocksdb這里的io_uring_queue_init接口的話,需要保證自己的磁盤支持io_poll模式,也就是通過上一節說的那種查看/修改 nvme 驅動配置來支持io_poll。

在io_poll模式下,對MultiGet的接口測試性能數據大概如下:

我的環境不支持io_poll,大體收益應該和fio的poll模式下的性能收益差不了太多

圖片圖片來自官方

db_bench的配置可以使用,直接用rocksdb的master, CMakeList.txt 默認會開啟io_uring:

生成數據:

./db_bench_uring
--benchmarks=fillrandom,stats
--num=3000000000
--threads=32
--db=./db
--wal_dir=./db
--duration=3600
-report_interval_seconds=1
--stats_interval_seconds=10
--key_size=16
--value_size=128
--max_write_buffer_number=16
-max_background_compactions=32
-max_background_flushes=7
-subcompactions=8
-compression_type=none

io_uring 測試MultiGet,不使用block_cache:

./db_bench_uring
--benchmarks=multireadrandom,stats
--num=3000000000
--threads=32
--db=./db
--wal_dir=./db
--duration=3600
-report_interval_seconds=1
--stats_interval_seconds=10
--key_size=16
--value_size=128
-compression_type=none
-cache_size=0
-use_existing_db=1
-batch_size=256 # 每次MultiGet的 請求總個數
-multiread_batched=true # 使用 MultiGet 的新的API,支持MultiRead,否則就是逐個Get
-multiread_stride=0 # 指定MultiGet 時生成的key之間的跨度,本來是連續的隨機key,現在可以讓上一個隨機key和下一個隨機key之間間隔指定的長度。

總結

總的來說,io_uring能夠在內核的各個組件都能夠正常運行的基礎上進一步提升了性能,提升的部分包括 減少系統調用的開銷,減少內核上下文的開銷,以及支持io_poll和sq_poll 這樣的高速輪詢處理機制。而且相比于libaio 僅能夠使用direct-io來調度,那這個限制本身就對存儲應用軟件不夠友好了。

可見的未來,存儲系統是內核的直接用戶,隨著未來硬件介質的超高速發展,互聯網應用對存儲系統的高性能需求就會反作用于內核,那內核的一些I/O鏈路的性能也需要不斷得跟進提升,然而每一項on-linux kernel的更改都因為內核精密復雜高要求 的 標準都會比普通的應用復雜很多,io_uring 能夠合入5系內核的upstream,那顯然證明了其未來的發展潛力 以及 內核社區 對其潛力的認可。

聲明:本文內容及配圖由入駐作者撰寫或者入駐合作網站授權轉載。文章觀點僅代表作者本人,不代表電子發燒友網立場。文章及其配圖僅供工程師學習之用,如有內容侵權或者其他違規問題,請聯系本站處理。 舉報投訴
  • 硬盤
    +關注

    關注

    3

    文章

    1308

    瀏覽量

    57283
  • 代碼
    +關注

    關注

    30

    文章

    4780

    瀏覽量

    68527
  • 編譯
    +關注

    關注

    0

    文章

    657

    瀏覽量

    32852
  • 組件
    +關注

    關注

    1

    文章

    512

    瀏覽量

    17813
收藏 人收藏

    評論

    相關推薦

    帶你深入探索okio組件的奧秘 提升IO效率

    三方組件庫上新了一批JS/eTS組件,其中就包括okio組件。okio是一個可應用于OpenAtom OpenHarmony(以下簡稱“OpenHarmony”)的高效IO庫,它依托于
    發表于 07-04 15:39 ?1060次閱讀

    如何將Runtime IO組件添加到現有項目?

    我最近在SPC5studio上下載了運行時IO組件的更新,但是,我似乎找不到從IDE gui導入向導將組件添加到現有項目或新項目的方法。以上來自于谷歌翻譯以下為原文I recently
    發表于 11-15 10:59

    可擴展的高性能RISC-V 內核IP

    性能SiFive U8系列內核IP的介紹開啟了現代SoC設計的新篇章。性能、面積效率和能效是芯片設計人員的關鍵指標,而SiFive U8系列在各個方面都表現搶眼。SiFive這種獨有
    發表于 08-13 15:14

    The Wirele Mea uring Sy tem of

    The Wirele Mea uring Sy tem of Temperature and Humidity and lt Application:介紹了利用無線收發元件構成的無線溫濕度測量系統的設計方法。采用這種測量方案,不必敷設電纜,節省了費用和時間。
    發表于 03-24 09:13 ?13次下載

    IO系統衡量性能的幾個指標

    本系列文章試圖從基本概念開始對磁盤存儲相關的各種概念進行綜合歸納,讓大家能夠對IO性能相關的基本概念,IO性能的監控和調整有個比較全面的了解
    發表于 03-28 12:01 ?2711次閱讀

    IO多路復用的幾種實現機制的分析

    服務器端編程經常需要構造高性能IO模型,常見的IO模型有四種:同步和異步的概念描述的是用戶線程與內核的交互方式:同步是指用戶線程發起IO
    發表于 03-07 11:40 ?5735次閱讀
    <b class='flag-5'>IO</b>多路復用的幾種實現機制的分析

    論述學習Linux內核各個階段

    第三階段(回歸第一階段):你已經工作了一段時間,寫了一些代碼,修復了一些bug,提交了一些patch,然后你重新回來迭代整體的知識框架,搞清楚各個子系統內在的聯系。這階段你如果有興趣可以讀《深入
    的頭像 發表于 08-20 17:23 ?5006次閱讀

    io_uring 優化 nginx,基于通用應用 nginx 的實戰

    引言 io_uring是Linux內核在v5.1引入的一套異步IO接口,隨著其迅速發展,現在的io_uring已經遠遠超過了純IO的范疇。從
    的頭像 發表于 10-10 16:19 ?3078次閱讀
    <b class='flag-5'>io_uring</b> 優化 nginx,基于通用應用 nginx 的實戰

    Linux 6.0生命周期結束介紹

    在去年 10 月初,Linux 6.0 正式發布,當時新版本帶來了非常多的新特性 / 功能,如 F2FS 低內存模式、在使用 XFS 和 io_uring 時的異步緩沖寫入、對 RISC-V 和 AArch64(ARM64)硬件架構的改進,以及 Btrfs 和 OverlayFS 文件系統的新功能和改進等。
    的頭像 發表于 01-16 11:43 ?846次閱讀

    現代異步存儲訪問API探索:libaio、io_uring和SPDK

    最近的高性能存儲設備暴露了現有軟件棧的低效,因而催生了對I/O棧的改進。Linux內核的最新API是io_uring。作者提供了第一個針對io_uring的深度研究,并且和libaio
    的頭像 發表于 06-27 10:54 ?1110次閱讀
    現代異步存儲訪問API探索:libaio、<b class='flag-5'>io_uring</b>和SPDK

    信號驅動IO與異步IO的區別

    一. 談信號驅動IO (對比異步IO來看) 信號驅動IO 對比 異步 IO進行理解 信號驅動IO: 內核
    的頭像 發表于 11-08 15:32 ?1055次閱讀
    信號驅動<b class='flag-5'>IO</b>與異步<b class='flag-5'>IO</b>的區別

    linux異步io框架iouring應用

    Linux內核5.1支持了新的異步IO框架iouring,由Block IO大神也即Fio作者Jens Axboe開發,意在提供一套公用的網絡和磁盤異步IO,不過
    的頭像 發表于 11-08 15:39 ?673次閱讀
    linux異步<b class='flag-5'>io</b>框架iouring應用

    異步IO框架iouring介紹

    前言 Linux內核5.1支持了新的異步IO框架iouring,由Block IO大神也即Fio作者Jens Axboe開發,意在提供一套公用的網絡和磁盤異步IO,不過
    的頭像 發表于 11-09 09:30 ?2382次閱讀
    異步<b class='flag-5'>IO</b>框架iouring介紹

    如何去提高EtherCAT IO性能呢?

    進行EtherCAT IO性能優化涉及多個方面,包括硬件選擇、網絡配置、軟件優化和應用程序設計。
    的頭像 發表于 03-07 09:28 ?483次閱讀

    組件測試儀如何提高太陽能組件性能

      JD-EL4太陽能組件測試儀在太陽能行業中扮演著至關重要的角色,不僅可以檢測組件的缺陷和問題,還可以幫助提高太陽能組件性能。以下是太陽能組件
    的頭像 發表于 05-21 17:13 ?688次閱讀
    主站蜘蛛池模板: 国产短视频精品区| 9LPORM原创自拍达人| 伊人久久影院大香线蕉| 影音先锋男人av橹橹色| 19不插片免费视频| nxgx69日本护士| 国产精品亚洲精品爽爽| 精品无人区麻豆乱码1区2| 快播苍井空| 日本xxxx19| 亚洲a免费| 中文亚洲大香伊蕉不卡一区| beeg日本老师按摩| 国产精品自产拍在线观看中文| 久草色在线| 青青草国产偷拍在线av| 羞羞麻豆国产精品1区2区3区| 影音先锋男人资源813.| 超碰在线视频公开| 国内精品久久久久久久试看| 美国色吧影院| 色综合五月激情综合色一区| 亚洲午夜精品A片久久WWW软件| 99re6久久热在线视频| 国产福利高清在线视频| 久久久久九九| 人人爽久久久噜噜噜丁香AV| 亚洲人成在线播放无码| av无码在线日本天堂| 花蝴蝶免费观看影视| 欧美日韩中文国产一区| 亚洲AV久久无码高潮喷水| 91久久夜色精品| 国产欧洲野花A级| 暖暖免费 高清 日本社区中文| 我半夜摸妺妺的奶C了她| 2021乱码精品公司| 国产精选视频在线观看| 欧美黑人经典片免费观看| 亚洲精品嫩草研究院久久| 啊…嗯啊好深男男小黄文|