12

我正在对用 Java 编写的应用程序进行一些基准测试。实验很重要,结果不受页面缓存的影响(我用的是linux)

因此,避免页面缓存的最佳方法是在打开文件时使用 O_DIRECT。因此,我更改了 jre 源代码中的相应代码。

我的方法非常适用于通过(例如写作)的所有事情FileOutputStream,但它不适用于FileInputStream(例如阅读)。

将 O_DIRECT 添加到 的 open-call 时FileInputStream,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 以确保内存对齐(这是 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);
4

3 回答 3

17

旧帖子,但我最近写了一个名为Jaydio的小型库,希望能解决这个确切的问题。也许你会发现它很有用。

于 2014-03-13T21:36:18.943 回答
10
  "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  [*]."

[*] 换句话说,它是一种甲骨文。

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

检查注释部分man 2 open

O_DIRECT

O_DIRECT 标志可能会对 用户空间缓冲区的长度和地址以及I/O的文件偏移量施加对齐限制。在 Linux 中,对齐限制因文件系统和内核版本而异....

在 Linux 2.4 下,传输大小,以及用户缓冲区和文件偏移的对齐方式都必须是文件系统逻辑块大小的倍数。在 Linux 2.6 下,对齐到 512 字节边界就足够了。……

总之,O_DIRECT 是一个潜在的强大工具,应谨慎使用。建议应用程序将 O_DIRECT 的使用视为默认禁用的性能选项。

我认为,JRE(类加载器)中有一些 FileInputStream 的用法,其读取的偏移量或大小未与 512 字节对齐。(对于高级格式,最小对齐可能更大,甚至 4096 字节或一个 4K 页。)

未对齐偏移的内核行为是灰色区域,这里有一些信息:RFC: Clarifying Direct I/O Semantics, Theodore Ts'o, tytso@mit, LWN, 2009

其他有趣的讨论在这里:Linux: Accessing Files With O_DIRECT (kerneltrap, 2007)

嗯,当 DIRECT 发生故障时,似乎应该回退到缓冲 I/O。所有使用 DIRECT 的 IO 操作都是同步的。可能是一些 DMA 影响?还是O_DIRECT和的组合mmap

更新:

感谢 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 字节,没有对齐。

于 2013-03-05T16:17:09.357 回答
5

您可以利用Java Native Access (JNA)在 Java 下使用 O_DIRECT 。此处提供了启用 O_DIRECT 的 InputStream 和 OutputStream 的实现。

于 2014-04-07T14:15:15.093 回答