JAVA IO效能比較

壹頁書發表於2013-11-09
JAVA常用IO方式包括流,通道和MappedByteBuffer。
試驗通過複製一個大檔案(eclipse-jee-juno-SR2-win32.rar 237M),並選擇不同的緩衝區大小,記錄複製的時間。
影響IO的因素有很多,同樣的方式複製時間可能差異很大。所以需要排除一些最大和最小的資料,保證樣本的正確性。

  1. import java.io.BufferedInputStream;
  2. import java.io.BufferedOutputStream;
  3. import java.io.File;
  4. import java.io.FileInputStream;
  5. import java.io.FileOutputStream;
  6. import java.io.IOException;
  7. import java.io.RandomAccessFile;
  8. import java.lang.reflect.InvocationHandler;
  9. import java.lang.reflect.Method;
  10. import java.lang.reflect.Proxy;
  11. import java.nio.ByteBuffer;
  12. import java.nio.MappedByteBuffer;
  13. import java.nio.channels.FileChannel;
  14. import java.nio.channels.FileChannel.MapMode;
  15. import java.util.Date;

  16. public class TestIO {
  17.     // 快取大小
  18.     private static enum BufferSize {
  19.         _1M, _10M, _50M;

  20.         public static int getBufferSize(BufferSize bufferSize) {
  21.             if ("_1M".equals(bufferSize.toString())) {
  22.                 return 1024 * 1024;
  23.             } else if ("_10M".equals(bufferSize.toString())) {
  24.                 return 10 * 1024 * 1024;
  25.             } else if ("_50M".equals(bufferSize.toString())) {
  26.                 return 50 * 1024 * 1024;
  27.             }
  28.             return -1;
  29.         }
  30.     }

  31.     // 原始檔
  32.     private static File source = new File("C:/Users/lihuilin/Desktop/eclipse-jee-juno-SR2-win32.rar");

  33.     // 複製檔案介面
  34.     private static interface Copy {
  35.         void action() throws Exception;

  36.         BufferSize getBufferSize();
  37.     }

  38.     // 獲取目標檔案
  39.     private static File getTarget(Copy copy, BufferSize bufferSize) {
  40.         String target = "C:/tmp/" + copy.getClass().getSimpleName() + bufferSize.toString() + "_" + new Date().getTime() + ".rar";
  41.         return new File(target);
  42.     }

  43.     // 傳統IO流複製方式
  44.     private static class Stream implements Copy {
  45.         BufferSize bufferSize;

  46.         public Stream(BufferSize bufferSize) {
  47.             this.bufferSize = bufferSize;
  48.         }

  49.         @Override
  50.         public void action() throws IOException {
  51.             BufferedInputStream in = new BufferedInputStream(new FileInputStream(source), BufferSize.getBufferSize(bufferSize));
  52.             BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(getTarget(this, bufferSize)));
  53.             byte[] buffer = new byte[BufferSize.getBufferSize(bufferSize)];
  54.             int length;
  55.             while ((length = in.read(buffer)) != -1) {
  56.                 out.write(buffer, 0, length);
  57.             }
  58.             out.flush();
  59.             in.close();
  60.             out.close();
  61.         }

  62.         @Override
  63.         public BufferSize getBufferSize() {
  64.             return bufferSize;
  65.         }
  66.     }

  67.     // NIO通道方式
  68.     private static class Channel implements Copy {

  69.         private BufferSize bufferSize;

  70.         public Channel(BufferSize bufferSize) {
  71.             this.bufferSize = bufferSize;
  72.         }

  73.         @Override
  74.         public void action() throws Exception {
  75.             FileChannel in = new FileInputStream(source).getChannel();
  76.             FileChannel out = new FileOutputStream(getTarget(this, bufferSize)).getChannel();
  77.             ByteBuffer buffer = ByteBuffer.allocate(BufferSize.getBufferSize(bufferSize));
  78.             while (in.read(buffer) != -1) {
  79.                 buffer.flip();
  80.                 out.write(buffer);
  81.                 buffer.compact();
  82.             }
  83.             in.close();
  84.             out.close();
  85.         }

  86.         @Override
  87.         public BufferSize getBufferSize() {
  88.             return bufferSize;
  89.         }
  90.     }

  91.     // MappedByteBuffer方式
  92.     private static class Mapped implements Copy {
  93.         private BufferSize bufferSize;

  94.         public Mapped(BufferSize bufferSize) {
  95.             this.bufferSize = bufferSize;
  96.         }

  97.         @Override
  98.         public void action() throws Exception {
  99.             FileChannel in = new RandomAccessFile(source, "r").getChannel();
  100.             FileChannel out = new RandomAccessFile(getTarget(this, bufferSize), "rw").getChannel();
  101.             long pos = 0;
  102.             long step = BufferSize.getBufferSize(bufferSize);
  103.             MappedByteBuffer inBuffer, outBuffer = null;
  104.             while (pos < in.size() && (in.size() - pos) > step) {
  105.                 inBuffer = in.map(MapMode.READ_ONLY, pos, BufferSize.getBufferSize(bufferSize));
  106.                 outBuffer = out.map(MapMode.READ_WRITE, pos, BufferSize.getBufferSize(bufferSize));
  107.                 outBuffer.put(inBuffer);
  108.                 pos = pos + step;
  109.                 inBuffer.clear();
  110.                 outBuffer.clear();
  111.             }

  112.             inBuffer = in.map(MapMode.READ_ONLY, pos, in.size() - pos);
  113.             outBuffer = out.map(MapMode.READ_WRITE, pos, in.size() - pos);

  114.             outBuffer.put(inBuffer);
  115.             inBuffer.clear();
  116.             outBuffer.clear();
  117.             in.close();
  118.             out.close();
  119.         }

  120.         @Override
  121.         public BufferSize getBufferSize() {
  122.             return bufferSize;
  123.         }

  124.     }

  125.     // 動態代理,用於記錄複製時間並寫入日誌
  126.     private static class Watch implements InvocationHandler {
  127.         private Copy copy;

  128.         public Watch(Copy copy) {
  129.             this.copy = copy;
  130.         }

  131.         public Copy getInstance() {
  132.             return (Copy) Proxy.newProxyInstance(copy.getClass().getClassLoader(), copy.getClass().getInterfaces(), this);
  133.         }

  134.         @Override
  135.         public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  136.             Object result;
  137.             long start = System.currentTimeMillis();
  138.             result = method.invoke(copy, args);
  139.             long end = System.currentTimeMillis();

  140.             String log = copy.getClass().getSimpleName() + ":" + copy.getBufferSize().toString() + ":" + (end - start) + "\n";
  141.             System.out.print(log);
  142.             FileOutputStream out = new FileOutputStream("C:/tmp/Log.txt", true);
  143.             out.write(log.getBytes());
  144.             out.flush();
  145.             out.close();
  146.             return result;
  147.         }
  148.     }

  149.     public static void main(String[] args) throws Exception {
  150.         for (int i = 0; i < 15; i++) {
  151.             new Watch(new Mapped(BufferSize._50M)).getInstance().action();
  152.             new Watch(new Mapped(BufferSize._10M)).getInstance().action();
  153.             new Watch(new Mapped(BufferSize._1M)).getInstance().action();

  154.             new Watch(new Channel(BufferSize._50M)).getInstance().action();
  155.             new Watch(new Channel(BufferSize._10M)).getInstance().action();
  156.             new Watch(new Channel(BufferSize._1M)).getInstance().action();

  157.             new Watch(new Stream(BufferSize._50M)).getInstance().action();
  158.             new Watch(new Stream(BufferSize._10M)).getInstance().action();
  159.             new Watch(new Stream(BufferSize._1M)).getInstance().action();
  160.         }
  161.     }
  162. }
將樣本資料Log.txt掛載為Oracle的外部表,以便分析

  1. create table log
  2. (
  3.         copytype varchar2(10),
  4.         buffersize varchar2(10),
  5.         copytime varchar2(10)
  6. )
  7. organization external
  8. (
  9.         type oracle_loader
  10.         default directory tmp
  11.         access parameters
  12.         (
  13.                 records delimited by "\n"
  14.                 FIELDS TERMINATED BY ":" LDRTRIM
  15.                 REJECT ROWS WITH ALL NULL FIELDS
  16.                 (
  17.                         copytype CHAR(20) TERMINATED BY ":",
  18.                         buffersize CHAR(20) TERMINATED BY ":",
  19.                         copytime CHAR(20) TERMINATED BY ":"
  20.                 )
  21.         )
  22.         location ('Log.txt')
  23. );
查詢樣本資料,發現同樣的方式,差異非常大。

SQL> select * from log where copytype='Channel' and buffersize='_1M';
COPYTYPE   BUFFERSIZE COPYTIME
---------- ---------- ----------
Channel    _1M        6896
Channel    _1M        9859
Channel    _1M        5711
Channel    _1M        6514
Channel    _1M        5400
Channel    _1M        5494
Channel    _1M        7052
Channel    _1M        5807
Channel    _1M        10904
Channel    _1M        13839
Channel    _1M        17035
Channel    _1M        6255
Channel    _1M        5085

已選擇13行。

整理樣本資料,將每種方式前五個資料和最後五個資料清除,並建立檢視。

  1. create or replace view v1 as
  2. select copytype,buffersize,copytime from
  3. (
  4.     select copytype,buffersize,copytime,
  5.     row_number() over(partition by copytype,buffersize order by to_number(copytime)) c1,
  6.     row_number() over(partition by copytype,buffersize order by to_number(copytime) desc) c2
  7.     from log
  8. ) log where c1 not in (1,2,3,4,5) and c2 not in(1,2,3,4,5);
此時資料已經相差不大。
SQL> select * from v1;
COPYTYPE   BUFFERSIZE COPYTIME
---------- ---------- ----------
Channel    _10M       807
Channel    _10M       844
Channel    _10M       870
Channel    _1M        6255
Channel    _1M        6514
Channel    _1M        6896
Channel    _50M       12748
Channel    _50M       12865
Channel    _50M       12901
Mapped     _10M       11170
Mapped     _10M       16355
Mapped     _10M       17158
Mapped     _1M        1774
Mapped     _1M        1963
Mapped     _1M        4213
Mapped     _50M       472
Mapped     _50M       601
Stream     _10M       1047
Stream     _10M       1182
Stream     _10M       1436
Stream     _1M        7283
Stream     _1M        7347
Stream     _50M       28280
Stream     _50M       30288
Stream     _50M       31012

已選擇25行。

使用行轉列,檢視平均時間。

  1. select copytype,
  2. round(avg(decode(buffersize,'_1M',copytime)),2) one,
  3. round(avg(decode(buffersize,'_10M',copytime)),2) ten,
  4. round(avg(decode(buffersize,'_50M',copytime)),2) fifty
  5. from v1 group by copytype;
COPYTYPE          ONE        TEN      FIFTY
---------- ---------- ---------- ----------
Channel          6555     840.33      12838
Mapped           2650   14894.33      536.5
Stream           7315    1221.67      29860



來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/29254281/viewspace-776173/,如需轉載,請註明出處,否則將追究法律責任。

相關文章