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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > linux >内容正文

linux

byte数组转file不写入磁盘_Linux 环境写文件如何稳定跑满磁盘 I/O 带宽?

發布時間:2024/10/8 linux 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 byte数组转file不写入磁盘_Linux 环境写文件如何稳定跑满磁盘 I/O 带宽? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
  • 準備

    • 要求

    • 機器配置

    • 測試磁盤 IO 性能

  • 實驗一: Buffer IO 寫入

  • 實驗二: 4K 單次 Direct IO 寫入

  • 實驗三: mmap 寫入

  • 實驗四: 改進的 mmap 寫入

  • 結論

準備

要求

在?限制內存?的情況下,假定我們每次寫入 4k 的數據,如何保證?kill -9?不丟數據的情況下,仍然穩定的跑滿磁盤的 IO?因為需要保證?kill -9?不丟數據,所以?fwrite()?就不在我們的考慮范圍之內了. 又因為限制內存,所以直觀的想法是直接 Direct IO, 但 Direct IO 能否跑滿磁盤 IO 呢?

機器配置

CPU: 64 核 Intel(R) Xeon(R) CPU E5-2682 v4 @ 2.50GHz

磁盤?: Intel Optane SSD

測試磁盤 IO 性能

官方稱讀 / 寫帶寬是?2400/2000 MB/s, 我們利用?fio?來進行實測:

順序讀性能:

sudo fio --filename=test -iodepth=64 -ioengine=libaio --direct=1 --rw=read --bs=2m --size=2g --numjobs=4 --runtime=10 --group_reporting --name=test-read

結果:

READ: bw=2566MiB/s (2691MB/s), 2566MiB/s-2566MiB/s (2691MB/s-2691MB/s), io=8192MiB (8590MB), run=3192-3192msec

順序寫性能:

sudo fio --filename=test -iodepth=64 -ioengine=libaio -direct=1 -rw=write -bs=1m -size=2g -numjobs=4 -runtime=20 -group_reporting -name=test-write

結果:

WRITE: bw=2181MiB/s (2287MB/s), 2181MiB/s-2181MiB/s (2287MB/s-2287MB/s), io=8192MiB (8590MB), run=3756-3756msec

實測讀寫帶寬:?2566/2181 MB/s

實驗一: Buffer IO 寫入

因為是限制內存,所以 Buffer IO 不在我們的考慮范圍內,但是我們先來測試一下 Buffer IO 的具體性能到底如何? 我們使用最簡單的方法,因為我們的 CPU 核數是 64,所以直接 64 線程單次?4K?字節 Buffer IO 寫入, 即通過操作系統的 Page Cache 的策略來緩存,刷盤:

代碼片段?:?完整代碼

static char data[4096] attribute((aligned(4096))) = {'a'};

void writer(int index) {
std::string fname = "data" + std::to_string(index);
int data_fd = ::open(fname.c_str(), O_RDWR | O_CREAT | O_APPEND, 0645);
for (int32_t i = 0; i < 1000000; i++) {
::write(data_fd, data, 4096);
}
close(data_fd);
}

int main() {
std::vectorstd::thread threads;
for(int i = 0; i < 64; i++) {
std::thread worker(writer, i);
threads.push_back(std::move(worker));
}
for (int i = 0; i < 64; i++) {
threads[i].join();
}
return 0;
}

我們通過?O_APPEND?單次 4k 追加寫入,之后通過?vmstat?來保留?120s?的寫入帶寬:

vmstat 1 120 > buffer_io

經過最后的測試數據整理,我們發現 Buffer IO 的性能基本能穩定跑滿帶寬, 其中只有一次 I/O 抖動:

實驗二: 4K 單次 Direct IO 寫入

Buffer IO 利用 Page Cache 幫助我們緩存了大量的數據,其實必然提高了寫入帶寬,但假如在限制內存的情況下,Buffer IO 就不是正確的解決方案了,這次我們繞過 Page Cache, 直接 Direct IO 單次?4K?寫入:

代碼片段?:?完整代碼

唯一需要修改的地方就是在?open()?中加入?O_DIRECT?標志:

int data_fd = ::open(fname.c_str(), O_RDWR | O_CREAT | O_APPEND | O_DIRECT, 0645);

通過?vmstat?獲取寫入帶寬數據, 整理如下:

通過數據我們發現,單次 4k 的 Direct IO 寫入無法跑滿磁盤的 I/O 帶寬,僅僅只有?800MB/S

實驗三: mmap 寫入

通過前面這兩個實驗我們發現,Buffer IO 是可以跑滿磁盤 I/O 的,那我們可以嘗試模擬 Buffer IO 的寫入方式,使用較少的內存來達到 Buffer IO 的寫入效果.

我們使用?mmap?來實現 Buffer IO 寫入,通過限定的 Buffer Block 來模擬 Page Cache 的聚合效果, 實驗中我們使用?memcpy?來完成數據拷貝,Buffer Block 我們設定為?4K * 4, 與 Direct IO 的不同,我們這次限定即?16KB?的單次寫入:

代碼片段:?完整代碼

main()?函數不變,修改線程的?writer()?函數:

static char data[4096] attribute((aligned(4096))) = {'a'};
static int32_t map_size = 4096 * 4;

void MapRegion(int fd, uint64_t file_offset, char** base) {
void* ptr = mmap(nullptr, map_size, PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
file_offset);
if (unlikely(ptr == MAP_FAILED)) {
*base = nullptr;
return;
}
base = reinterpret_cast<char>(ptr);
}

void UnMapRegion(char* base) {
munmap(base, map_size);
}

void writer(int index) {
std::string fname = "data" + std::to_string(index);
char* base = nullptr;
char* cursor = nullptr;
uint64_t mmap_offset = 0, file_offset = 0;
int data_fd = ::open(fname.c_str(), O_RDWR | O_CREAT, 0645);
posix_fallocate(data_fd, 0, (4096UL * 1000000));
MapRegion(data_fd, 0, &base);
if (unlikely(base == nullptr)) {
return;
}
cursor = base;
file_offset += map_size;
for (int32_t i = 0; i < 1000000; i++) {
if (unlikely(mmap_offset >= map_size)) {
UnMapRegion(base);
MapRegion(data_fd, file_offset, &base);
if (unlikely(base == nullptr)) {
return;
}
cursor = base;
file_offset += map_size;
mmap_offset = 0;
}
memcpy(cursor, data, 4096);
cursor += 4096;
mmap_offset += 4096;
}
UnMapRegion(base);
close(data_fd);
}

我們通過?vmstat?來獲取寫入帶寬數據,我們發現?mmap?的?16K?寫入可以跑滿磁盤帶寬,但 I/O 抖動較大,無法類似于 Buffer IO 穩定的寫入.

我們通過?perf?生成火焰圖分析:

通過?pref?生成分析瓶頸時發現,寫入?writer()?時觸發了大量的?Page Fault, 即缺頁中斷,而?mmap()?本身的調用也有一定的消耗 (關于?mmap()?的源碼分析,我們在后面的文章會詳細分析 ),我們實驗三的思路是: 首先?fallocate?一個大文件,然后?mmap()?內存映射?16k?的 Block,?memcpy()?寫滿之后,游標右移重新?mmap(),以此循環.

實驗四: 改進的 mmap 寫入

為了避免?mmap()?的開銷,我們使用臨時文件在寫入之前?mmap()?映射,之后循環利用這?16K?的 Block, 避免?mmap()?的巨大開銷:

代碼片段:?完整代碼

void MapRegion(int fd, uint64_t file_offset, char** base) {
void* ptr = mmap(nullptr, map_size, PROT_READ | PROT_WRITE,
MAP_SHARED,
fd,
file_offset);
if (unlikely(ptr == MAP_FAILED)) {
*base = nullptr;
return;
}
*base = reinterpret_cast<char*>(ptr);
}

void UnMapRegion(char* base) {
munmap(base, map_size);
}

void writer(int index) {
std::string fname = "data" + std::to_string(index);
std::string batch = "batch" + std::to_string(index);

char* base = nullptr;
char* cursor = nullptr;
uint64_t mmap_offset = 0, file_offset = 0;

int data_fd = ::open(fname.c_str(), O_RDWR | O_CREAT | O_DIRECT, 0645);
int batch_fd = ::open(batch.c_str(), O_RDWR | O_CREAT | O_DIRECT, 0645);
posix_fallocate(data_fd, 0, (4096UL * 1000000));
posix_fallocate(batch_fd, 0, map_size);

MapRegion(batch_fd, 0, &base);
if (unlikely(base == nullptr)) {
return;
}
cursor = base;
file_offset += map_size;

for (int32_t i = 0; i < 1000000; i++) {
if (unlikely(mmap_offset >= map_size)) {
pwrite64(data_fd, base, map_size, file_offset);
cursor = base;

file_offset += map_size;
mmap_offset = 0;
}
memcpy(cursor, data, 4096);
cursor += 4096;
mmap_offset += 4096;
}
UnMapRegion(base);

close(data_fd);
close(batch_fd);
}

使用?vmstat?來獲取寫入速度的數據, 整理如下:

這次避免了?mmap()?的開銷,寫入速度可以穩定保持在?2180 MB/S?左右,且沒有 I/O 抖動.

內存使用也僅僅只有?18000KB, 大約?18M:

結論

下面是四種方式的寫入速度對比:

在限制內存,且需要?kill -9?不丟數據的情況下,我們可以使用?mmap()?來模擬 Buffer IO,但為了避免頻繁?mmap()?的開銷,我們需要臨時文件來做我們的內存映射. 這種方法可以保證我們的寫入速度穩定且?kill -9?不至于丟失數據.

轉載自:http://www.leviathan.vip

-?END?-

「技術分享」某種程度上,是讓作者和讀者,不那么孤獨的東西。歡迎關注我的微信公眾號:「Kirito的技術分享」

總結

以上是生活随笔為你收集整理的byte数组转file不写入磁盘_Linux 环境写文件如何稳定跑满磁盘 I/O 带宽?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。