Linux

使用預讀優化讀取 I/O,同時避免將數據儲存在頁面記憶體中

  • October 18, 2016

我需要能夠從文件中順序讀取數據,而不是將正在讀取的數據儲存在頁面記憶體中,因為預計不會再次讀取文件內容,而且因為盒子上有記憶體壓力(想要將寶貴的記憶體用於有用的磁碟 I/O 記憶體)。

我的問題是如何優化這些讀取。因為我知道正在讀取的數據是按順序放置在磁碟上的(減去碎片),所以我希望能夠提前讀取(通過增加 /sys/block/sda/queue/read_ahead_kb)但不確定這是否將帶來任何好處,因為我必須通過使用 posix_fadvise (使用 POSIX_FADV_DONTNEED 標誌)來防止正在讀取的數據儲存在頁面記憶體中。

是否會因為提示從頁面記憶體中刪除數據而簡單地丟棄預讀數據?

使用直接 IO

直接 I/O 是文件系統的一項功能,文件讀取和寫入直接從應用程序到儲存設備,繞過作業系統讀取和寫入記憶體。直接 I/O 僅由管理自己的記憶體的應用程序(例如數據庫)使用。

應用程序通過打開帶有 O_DIRECT標誌的文件來呼叫直接 I/O。

例如:

int fd = open( filename, O_RDONLY | O_DIRECT );

Linux 上的 Direct IO 很奇怪,並且有一些限制。應用程序 IO 緩衝區必須是頁面對齊的,並且某些文件系統要求每個 IO 請求是頁面大小的精確倍數。最後一個限制可能使讀取/寫入文件的最後一部分變得困難。

可以使用和fdopen設置一個大的頁面對齊緩衝區來完成在應用程序中處理預讀的易於編碼的方法:posix_memalign``setvbuf

// should really get page size using sysconf()
// but beware of systems with multiple page sizes
#define ALIGNMENT ( 4UL * 1024UL )
#define BUFSIZE ( 1024UL * 1024UL )
char *buffer;
...

int fd = open( filename, O_RDONLY | O_DIRECT );
FILE *file = fdopen( fd, "rb" );

int rc = posix_memalign( &buffer, ALIGNMENT, BUFSIZE );
rc = setvbuf( file, buffer, _IOFBF, BUFSIZE );

您還可以使用mmap()來獲取匿名記憶體以用於緩衝區。這具有自然頁面對齊的優點:

...
char *buffer = mmap( NULL, BUFSIZE, PROT_READ | PROT_WRITE,
   MAP_ANONYMOUS | MAP_PRIVATE, -1, 0 );
rc = setvbuf( file, buffer, _IOFBF, BUFSIZE );

然後只需使用fread()/或您想從流中讀取的fgets()任何類型的讀取函式。FILE *``file

您確實需要使用工具進行檢查,例如strace實際的read系統呼叫是使用頁面對齊和頁面大小的緩衝區完成的 -FILE *基於 - 的流處理的一些 C 庫實現不使用setvbuf僅用於 IO 緩衝的緩衝區,所以對齊和大小可以關閉。我不認為 Linux/glibc 會這樣做,但如果你不檢查並且大小和/或對齊是關閉的,你的 IO 呼叫將會失敗。

再一次 - Linux 直接 IO 可能很古怪。只有部分文件系統支持直接 IO,其中一些文件系統比其他文件系統更特殊。 如果您決定使用它,請徹底測試它。

每當需要填充流的緩衝區時,發布的程式碼將執行 1 MB 的預讀。您還可以使用執行緒實現更複雜的預讀——一個執行緒填充一個緩衝區,其他執行緒從一個完整的緩衝區讀取。這將避免在預讀完成時處理“卡頓”,但代價是大量相對複雜的多執行緒程式碼。

引用自:https://unix.stackexchange.com/questions/317126