RandomAccessFile writeInt(int i)vs write(byte [] b) - 性能

时间:2018-02-05 20:15:10

标签: java file-writing randomaccessfile

我今天遇到了关于RandomAccessFile的一件有趣的事情。

我注意到使用RandomAccessFile的{​​{1}}方法比使用writeInt(int i)的{​​{1}}慢得多,我首先将int值转换为byte [4数组。

我正在使用此代码进行转换

RandomAccessFile

差异非常显着,有利于write(byte[] b)

使用JDK 8在我的笔记本电脑上写入100万private static byte[] intToByte(int i) { byte[] result = new byte[4]; result[0] = (byte) (i >> 24); result[1] = (byte) (i >> 16); result[2] = (byte) (i >> 8); result[3] = (byte) (i); return result; }

  • 通过write(byte[] b)方法 ~9秒
  • 途经int ~2,3秒

我在另一个环境中有类似的结果,我使用的是JDK 7和完全不同的机器。

writeInt(int i)方法委托给本地writeInt(int i)方法,write(byte[] b)委托委托给本地write0(int b)

当我进行性能分析时,我注意到大部分执行时间是在write(byte[] b)方法中使用时使用的。

有谁知道为什么我看到这么大的差异?似乎writeBytes效率低下。

2 个答案:

答案 0 :(得分:2)

RandomAccessFile实际上有两个本地方法来写入字节:

//writes an array
private native void writeBytes(byte b[], int off, int len) throws IOException;

//writes one byte
public native void write(int b) throws IOException;

方法 writeInt(int)用本机的 write(int)方法分别写入每个字节,而 write(byte [])使用本机 writeBytes(byte [],int,int)方法。

writeInt 方法执行4种方法调用来写入传递的整数值的每个字节,另一种方法仅使用一个调用来写入数组。方法调用实际上是Java中昂贵的操作:对于每次调用,JVM都会为操作数堆栈和局部变量数组分配额外的内存。

答案 1 :(得分:1)

不会详细介绍我所做的更改,但您的测试有点缺陷。我冒昧地更新它们并进行了一些测试:

@BenchmarkMode(value = { Mode.AverageTime })
@OutputTimeUnit(TimeUnit.MILLISECONDS)
@Warmup(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS)
@Measurement(iterations = 2, time = 2, timeUnit = TimeUnit.SECONDS)
public class RandomAccessWriteFileTest {

    public static void main(String[] args) throws Exception {
        Options opt = new OptionsBuilder().include(RandomAccessWriteFileTest.class.getSimpleName())
                .jvmArgs("-ea")
                .shouldFailOnError(true)
                .build();
        new Runner(opt).run();
    }

    @Benchmark()
    @Fork(1)
    public long benchamrkWriteDirectInt(BenchmarkPlainIntSetup setupTest) {
        try {
            setupTest.raf.writeInt(6969);
            return setupTest.raf.length();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    @Benchmark()
    @Fork(1)
    public long benchamrkWriteConvertedInt(BenchmarkConvertedIntSetup setupTest) {
        try {
            setupTest.raf.write(intToBytes(6969));
            return setupTest.raf.length();
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

    private static byte[] intToBytes(int i) {
        byte[] result = new byte[4];

        result[0] = (byte) (i >> 24);
        result[1] = (byte) (i >> 16);
        result[2] = (byte) (i >> 8);
        result[3] = (byte) i;

        return result;
    }

    @State(Scope.Thread)
    static public class BenchmarkConvertedIntSetup {

        public RandomAccessFile raf;

        public File f;

        @Setup(Level.Iteration)
        public void setUp() {
            try {
                f = new File("jmhDirectIntBenchamrk.ser" + ThreadLocalRandom.current().nextInt());
                raf = new RandomAccessFile(f, "rw");
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        }

        @TearDown(Level.Iteration)
        public void tearDown() {
            f.delete();
        }
    }

    @State(Scope.Thread)
    static public class BenchmarkPlainIntSetup {

        public RandomAccessFile raf;

        public File f;

        @Setup(Level.Iteration)
        public void setUp() {
            try {
                f = new File("jmhDirectIntBenchamrk.ser" + ThreadLocalRandom.current().nextInt());
                raf = new RandomAccessFile(f, "rw");
            } catch (FileNotFoundException e) {
                throw new RuntimeException(e);
            }
        }

        @TearDown(Level.Iteration)
        public void tearDown() {
            f.delete();
        }
    }
}

绝对结果有差异(这些是每次操作的ms)

 benchamrkWriteConvertedInt  0.008 
 benchamrkWriteDirectInt     0.026

不知道为什么(可能会在以后的某个时间挖掘大会,但我可以确认你的发现。好问题!)

这是用最新的java-8和java-9 btw

运行的