<< 2006/12/17 | Home | 2006/12/19 >>
PR: 転職    葬式    マンスリーマンション 神戸    北海道    環境    FX    不動産担保融資    桐ヶ谷斎場    海外旅行    専門学校   

続ファイルコピー

そういえばwrite back cacheの影響があるなと思い、コピー先をUSB 1.1のメモリに変えてみた。約6.3MBのファイルコピー。

Stream

1024512256128643216
7.798.028.529.5311.5516.0325.15

NIO
1024512256128643216
7.787.998.529.5311.5516.1325.32

やっぱり大差無いな。もっとも今回の例だとUSBへの書き込みが圧倒的なbottle neckだから差が出なくて当然か。しかし随分とバッファサイズが影響するもんだ。USBへの書き込みの開始終了に、何かオーバーヘッドがあるんだろうか。

読み込みと書き込みが遅いデバイスならば、読み込みと書き込みを別スレッドに分けてオーバーラップさせてやったら、速くなるんじゃないだろうか。というわけでマルチスレッド版コピーを試してみた。

import java.io.*;
import java.util.concurrent.*;

public class MultiThreadedCopyByStream {
    static class Packet {
        final int size;
        final byte[] buf;
        Packet(int size, byte[] buf) {
            this.size = size;
            this.buf = buf;
        }
    }

    static BlockingQueue queue = new LinkedBlockingQueue(8); 

    static class Donor extends Thread {
        final String copyFrom;
        final int bufSize;

        Donor(String copyFrom, int bufSize) {
            this.copyFrom = copyFrom;
            this.bufSize = bufSize;
        }

        @Override public void run() {
            InputStream in = null;
            try {
                in = new FileInputStream(copyFrom);
                while (true) {
                    byte[] buf = new byte[bufSize];
                    System.console().printf("%,d, Reading data%n", System.currentTimeMillis() - startTime);
                    int readLength = in.read(buf);
                    System.console().printf("%,d, Reading data done%n", System.currentTimeMillis() - startTime);
                    if (readLength == -1) {
                        try {
                            queue.put(new Packet(-1, null));
                        }
                        catch (InterruptedException ex) {
                            throw new RuntimeException(ex);
                        }
                        break;
                    }
                    try {
                        queue.put(new Packet(readLength, buf));
                    }
                    catch (InterruptedException ex) {
                        throw new RuntimeException(ex);
                    }
                }
            }
            catch (IOException ex) {
                ex.printStackTrace();
            }
            finally {
                if (in != null) {
                    try {in.close();}
                    catch (IOException ex) {ex.printStackTrace();}
                }
            }
        }
    }

    static class Acceptor extends Thread {
        final String copyTo;

        Acceptor(String copyTo) {
            this.copyTo = copyTo;
        }

        @Override public void run() {
            startTime = System.currentTimeMillis();

            OutputStream out = null;
            try {
                out = new FileOutputStream(copyTo);
                while (true) {
                    Packet packet = null;
                    try {
                        packet = queue.take();
                    }
                    catch (InterruptedException ex) {
                        throw new RuntimeException(ex);
                    }
                    if (packet.size == -1) break;
                    System.console().printf("%,d, Writing data%n", System.currentTimeMillis() - startTime);
                    out.write(packet.buf, 0, packet.size);
                    System.console().printf("%,d, Writing data done%n", System.currentTimeMillis() - startTime);
                }
            }
            catch (IOException ex) {
                ex.printStackTrace();
            }
            finally {
                if (out != null) {
                    try {out.close();}
                    catch (IOException ex) {ex.printStackTrace();}
                }

                System.console().printf("Elapsed %,d%n",System.currentTimeMillis() - startTime);
            }
        }
    }

    public static void main(String[] args) {
        String copyFrom = args[0];
        String copyTo = args[1];
        int bufferSize = 32 * 1024;
        if (args.length > 2) {
            bufferSize = Integer.parseInt(args[2]) * 1024;
        }
        if (bufferSize <= 0)
            throw new IllegalArgumentException("Invalid buffer size(=" + bufferSize + ").");

        new Donor(copyFrom, bufferSize).start();
        new Acceptor(copyTo).start();
    }

    static volatile long startTime;
}

concurrentパッケージを使うと簡単だ。読み込みがローカルドライブだと差がでないだろうから、無線LAN越しのリモートファイルにしてみた。バッファサイズは1MB。

マルチスレッドシングルスレッド(Stream)シングルスレッド(NIO)
8.249.149.16
微妙。確かに効果はあるんだけど、大した違いじゃない。処理のタイムスタンプを見てみると、

32, Reading data
516, Reading data done
516, Writing data
516, Reading data
1,407, Reading data done
1,407, Reading data
1,407, Reading data done
1,422, Reading data
1,735, Writing data done
1,735, Writing data
1,875, Reading data done
1,875, Reading data
2,344, Reading data done
2,360, Reading data
2,938, Reading data done
2,938, Reading data
2,938, Reading data done
2,938, Writing data done
2,938, Reading data
2,938, Writing data
2,954, Reading data done
4,172, Writing data done
4,172, Writing data
5,407, Writing data done
5,407, Writing data
6,625, Writing data done
6,625, Writing data
7,860, Writing data done
7,860, Writing data
8,219, Writing data done

読み込みが、ほぼ一瞬で終わっている。どうもOSに違いレイヤで、データの先読みをしているっぽい。面白いのがcmdのcopyによる結果が8.17secで高速なところ。copyも内部はマルチスレッド化しているんだろうか?

このサイトの掲載内容は私自身の見解であり、必ずしもIBMの立場、戦略、意見を代表するものではありません。
日本アイ・ビー・エム 花井 志生 Since 1997.6.8