簡體   English   中英

強制 JVM 在沒有頁面緩存的情況下執行所有 IO(例如 O_DIRECT)

[英]Force JVM to do all IO without page cache (e.g. O_DIRECT)

我正在對用 Java 編寫的應用程序進行一些基准測試。 實驗很重要,結果不受頁面緩存的影響(我用的是linux)

因此,避免頁面緩存的最佳方法是在打開文件時使用 O_DIRECT。 因此,我更改了 jre 源代碼中的相應代碼。

我的方法非常適用於通過FileOutputStream的所有內容(例如寫入),但不適用於FileInputStream (例如讀取)。

將 O_DIRECT 添加到FileInputStream的 open-call 時,JVM 無法加載任何類:

Error: Could not find or load main class perf.TestDirectIO

這個錯誤不是類路徑問題,因為我可以通過使用“未破解的”JVM 來修復它。

所以打開文件似乎有問題。

對於有關如何解決此問題的任何建議,我感到非常高興。

如果有人想做類似的事情,我已經在我的博客中記錄了整個 hack


作為參考,這些是我對 JVM 代碼所做的更改:

jdk/src/share/native/java/io/FileInputStream.c

 @@ -58,7 +60,8 @@
 JNIEXPORT void JNICALL
 Java_java_io_FileInputStream_open(JNIEnv *env, jobject this, jstring path) {
-    fileOpen(env, this, path, fis_fd, O_RDONLY);
+    fileOpen(env, this, path, fis_fd, O_RDONLY | O_DIRECT); // this is the change that causes all the problems
 }

此更改有效: jdk/src/solaris/native/java/io/FileOutputStream_md.c

@@ -55,8 +55,10 @@
 JNIEXPORT void JNICALL
 Java_java_io_FileOutputStream_open(JNIEnv *env, jobject this,
                                    jstring path, jboolean append) {
     fileOpen(env, this, path, fos_fd,
-             O_WRONLY | O_CREAT | (append ? O_APPEND : O_TRUNC));
+             O_WRONLY | O_DIRECT | O_CREAT | (append ? O_APPEND : O_TRUNC));
 }

我還更改了熱點 jre 以確保 memory 對齊(這是 O_DIRECT 的要求) hotspot/src/share/vm/runtime/os.cpp

+# include <mm_malloc.h>
...
-  u_char* ptr = (u_char*)::malloc(size + space_before + space_after);
+  u_char* ptr = (u_char*)::_mm_malloc(size + space_before + space_after,512);

老帖子,但我最近寫了一個名為Jaydio的小型庫,希望能解決這個問題。 也許你會發現它很有用。

  "The thing that has always disturbed me about O_DIRECT is that the whole interface is just stupid, and was probably designed by a deranged monkey on some serious mind-controlling substances [*]." 

[*]換句話說,它是一個Oracleism。

- 來自Transmeta的Linus Torvalds,2002年5月11日

檢查man 2 open NOTES部分:

O_DIRECT

O_DIRECT標志可能會對用戶空間緩沖區的長度和地址以及I / O的文件偏移量施加對齊限制 在Linux中,對齊限制因文件系統和內核版本而異....

在Linux 2.4下,傳輸大小,用戶緩沖區和文件偏移的對齊必須都是文件系統邏輯塊大小的倍數。 在Linux 2.6下,對齊到512字節邊界就足夠了。 ....

總之,O_DIRECT是一個潛在的強大工具,應謹慎使用。 建議應用程序將O_DIRECT用作性能選項,默認情況下禁用該選項。

我認為,在JRE(類加載器)中有一些FileInputStream用法,其讀取的偏移量或大小未對齊到512字節。 (對於高級格式 ,最小對齊可能更大,甚至4096字節,或一個4K頁面。)

內核對於未對齊偏移的行為是灰色區域,一些信息在這里: RFC:澄清直接I / O語義,Theodore Ts'o,tytso @ mit,LWN,2009

其他有趣的討論在這里: Linux:使用O_DIRECT訪問文件 (kerneltrap,2007)

嗯,當DIRECT出現故障時,似乎應該回退到緩沖的I / O. 使用DIRECT的所有IO操作都是同步的。 可能是一些DMA效應? 或者O_DIRECTmmap組合?

更新:

感謝strace輸出。 這是錯誤( grep O_DIRECT ,然后檢查文件描述符操作):

28290 open("...pact/perf/TestDirectIO.class", O_RDONLY|O_DIRECT) = 11
28290 fstat(11, {st_mode=S_IFREG|0644, st_size=2340, ...}) = 0
28290 fcntl(11, F_GETFD)                = 0
28290 fcntl(11, F_SETFD, FD_CLOEXEC)    = 0
...skip
28290 stat("...pact/perf/TestDirectIO.class", {st_mode=S_IFREG|0644, st_size=2340, ...}) = 0
...skip
28290 read(11, "\312\376\272\276\0\0\0003\0\215\n\0-\0D\t\0E\0F\7\0G\n\0\3\0D\10\0H\n"..., 1024) = 1024
28290 read(11, 0x7f1d76d23a00, 1316)    = -1 EINVAL (Invalid argument)

未對齊的讀取大小會導致EINVAL錯誤。 您的類文件長度為2340字節,它是1024 + 1316字節,未對齊。

您可以在Java下使用O_DIRECT,利用Java Native Access(JNA) 此處提供啟用O_DIRECT的InputStream和OutputStream的實現。

只是為了更新,因為如果你谷歌O_DIRECT JVM ,這篇文章是第一個出現的:

使用O_DIRECT進行 Direct I/O 的能力在 2017 年被合並到 JDK 中:

創建 FileChannel 時,您可以傳遞ExtendedOpenOption.DIRECT

import com.sun.nio.file.ExtendedOpenOption;

import java.nio.channels.FileChannel;
import java.nio.file.StandardOpenOption;

FileChannel fc = FileChannel.open(f.toPath(), StandardOpenOption.WRITE, 
                                  ExtendedOpenOption.DIRECT);

在 2022 年,還可以考慮將新的 Foreign Memory API 與MemorySegment用於后備緩沖區。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM