プログラム悪戦苦闘日記

はてなダイアリーからの移行(遺物)

Java New I/O part5(最終回)

 New I/Oの話題はMappingByteBufferやSelectorの話もあるが、ここで一旦打ち切り。最終回は、本当にNew I/Oは今までのI/Oより速いか、測ってみた。プログラムの内容は、前回の練習問題でやったファイルの中身をすべてビット反転して、ファイルに出力するというものだ。ファイルのサイズは100MB(=10,240KB)。同じ処理を10回ループして、その処理時間を合計している。時間計測にはSystem.nanoTime()を使った。測定プログラムは次の通り。

import java.io.*;
import java.nio.*;
import java.nio.channels.*;
import java.text.*;

public class Main {
    public static void main(String[] args) 
    throws Exception {
        if( args.length != 2 )
            return;
            
        final int MAX = 10;
        
        tern1(args); tern2(args);
            
        final long start1 = System.nanoTime();
        for(int i=1; i<=MAX; ++i)
            tern1(args);
        final long end1 = System.nanoTime();
        final long dt1 = (end1 - start1) / 1000L;
        
        final long start2 = System.nanoTime();
        for(int i=1; i<=MAX; ++i)
            tern2(args);
        final long end2 = System.nanoTime();
        final long dt2 = (end2 - start2) / 1000L;
        
        final DecimalFormat fmt = new DecimalFormat("#,##0");
        System.out.println("処理時間1:" + fmt.format(dt1) + "micro sec");
        System.out.println("処理時間2:" + fmt.format(dt2) + "micro sec");
    }
    
    public static void tern1(String[] args) 
    throws IOException {
        FileInputStream  in    = new FileInputStream(args[0]);
        FileOutputStream out   = new FileOutputStream(args[1]);
        FileChannel      inch  = in.getChannel();
        FileChannel      outch = out.getChannel();
        
        ByteBuffer buf = ByteBuffer.allocate(1024*1024);
        
        while( inch.read(buf) != -1 ) {
            buf.flip();
        
            while(buf.position() < buf.limit()) {
                byte b = buf.get();
                   buf.position(buf.position() - 1);
                   buf.put((byte) (b ^ 0xFF));
            }
        
            buf.flip();
            outch.write(buf);
            buf.clear();
        }
        
        outch.force(true);
        out.close();
        in.close();
    }
    
    public static void tern2(String[] args) 
    throws IOException {
        BufferedInputStream  in  = new BufferedInputStream(new FileInputStream(args[0]));
        BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(args[1]));
        int c;
        
        while( (c = in.read()) != -1 )
            out.write((byte) (c ^ 0xFF));
            
        out.flush();
        out.close();
        in.close();
    }
}

さて、結果は、

java.nioの場合:12.72 sec / 回
java.ioの場合:  10.43 sec / 回

…なんかjava.ioの方が速いんですけど。java.nioでByteBufferを作るとき、一気に100MB allocateしたかったのだが、OutOfMemoryErrorが出てしまったので、しかたなく1MBずつ100回ループすることにした。
 もしかしたらBufferを作る時間がかかっているのかも知れない。ちなみにC++で作ったものは↓。処理時間は 7.87 sec / 回 だった。

#include <cstdio>
#include <iostream>
#include <windows.h>

using namespace std;

void tern()
{
    FILE* in = fopen("hoge.dat", "rb");
    FILE* out = fopen("piyo.dat", "wbc");

    fseek(in, 0, SEEK_END);
    long size = ftell(in);
    fseek(in, 0, SEEK_SET);

    unsigned char* buf = new unsigned char[size];
    fread(buf, 1, size, in);
    for(int i=0; i<size; ++i)
        buf[i] ^= 0xFF;
    fwrite(buf, 1, size, out);
    delete[] buf;

    fflush(out);
    fclose(out);
    fclose(in);
}

int main()
{
    tern();

    LARGE_INTEGER start, end, freq;
    ::QueryPerformanceFrequency(&freq);
    ::QueryPerformanceCounter(&start);
	
    for(int i=1; i<=10; ++i)
        tern();
	
    ::QueryPerformanceCounter(&end);
    double dt = static_cast<double>(end.QuadPart - start.QuadPart) / freq.QuadPart;
    cout << dt << endl;
}