ivaneeo's blog

          自由的力量,自由的生活。

            BlogJava :: 首頁 :: 聯系 :: 聚合  :: 管理
            669 Posts :: 0 Stories :: 64 Comments :: 0 Trackbacks

          我從java1.3開始學習java,后來主要用1.4,再后來1.51.6中的很多新特性,都停留在“知道”的狀態,比如nio,雖然據說可以提升性能,但并沒有真正深入使用和測試過,工作操作文件的情況不多,所以關注也不多,即便用到,也還是習慣性的用java.io。今天看到的這篇文章,雖然測試手段非常簡單,所得結論也難免有些片面 ,但依然說明,在順序訪問的時候,NIO的性能相對java.io有很大的提升。

          也許應該update一下自己的知識了,否則就要OUT,或者早已經OUT了。 

          下次操作文件或者寫socket要用NIO了。

          ---

          轉自:http://links.techwebnewsletters.com/ctt?kn=28&m=34038811&r=MzI1Mjc3MDAzOAS2&b=0&j=NTc5NjM4MTAS1&mt=1&rt=0

          以下為翻譯的內容:

          最近我在工作中用到了java i/o相關功能。因為對java.io的了解更多(畢竟面世較早),所以一開始我使用的是java.io包下的類,后來為了測試一下是不是能夠通過NIO提高文件操作性能,于是轉向了java.nio。我得到的結論讓我感到有些震驚,下面是對比測試的一些細節:

             1、在java.io的測試代碼中,我使用RandomAccessFile直接向文件寫數據,并搜索到特定的位置執行記錄的插入、讀取和刪除。

             2、在java.nio的初步測試代碼中,使用FileChannel對象。NIO之所以比java.io更加高效,是因為NIO面向的是data chunks,而java.io基本上是面向byte的。

             3、為了進一步挖掘NIO的能力,我又改用MappedByteBuffer執行測試,這個類是構建在操作系統的虛擬內存機制上的。根據java文檔所說,這個類在性能方面是最好的。

          為了進行測試,我寫了一個模擬員工數據庫的小程序,員工數據的結構如下:

              view plaincopy to clipboardprint?

          class Employee {   

                  String last; // the key   

                  String first;   

                  int id;   

                  int zip;   

                  boolean employed;   

                  String comments;   

              }  

          class Employee {

                  String last; // the key

                  String first;

                  int id;

                  int zip;

                  boolean employed;

                  String comments;

              }

          員工數據寫入文件,并將last name作為索引key,日后可以通過這個key從文件中加載該員工對應的數據。無論使用IONIO還是MappedByteBuffers,首先都需要打開一個RandomAccessFile。以下代碼在用戶的home目錄下創建一個名為employee.ejb的文件,設置為可讀可寫,并初始化對應的ChannelMappedByteBuffer

              view plaincopy to clipboardprint?

          String userHome = System.getProperty("user.home");   

              StringBuffer pathname = new StringBuffer(userHome);   

              pathname.append(File.separator);   

              pathname.append("employees.ejb");   

              java.io.RandomAccessFile journal =   

                  new RandomAccessFile(pathname.toString(), "rw");   

              

              //下面這一句是為了NIO   

              java.nio.channels.FileChannel channel = journal.getChannel();   

                

              //下面這兩句是為了使用MappedByteBuffer   

              journal.setLength(PAGE_SIZE);   

              MappedByteBuffer mbb =   

                  channel.map(FileChannel.MapMode.READ_WRITE, 0, journal.length() );  

          String userHome = System.getProperty("user.home");

              StringBuffer pathname = new StringBuffer(userHome);

              pathname.append(File.separator);

              pathname.append("employees.ejb");

              java.io.RandomAccessFile journal =

                  new RandomAccessFile(pathname.toString(), "rw");

           

              //下面這一句是為了NIO

              java.nio.channels.FileChannel channel = journal.getChannel();

             

              //下面這兩句是為了使用MappedByteBuffer

              journal.setLength(PAGE_SIZE);

              MappedByteBuffer mbb =

                  channel.map(FileChannel.MapMode.READ_WRITE, 0, journal.length() );

           

          使用channel.map進行映射后,當該文件被追加了新的數據時,之前的MappedByteBuffer是看不到這些數據的。因為我們想測試讀和寫,所以當文件中追加寫入新的記錄后,需要重新做映射才能使得MappedByteBuffer讀取新數據。為了提高效率,降低重新映射的次數,每次空間不夠的時候,我們將文件擴張特定的大小(比如說1K)以防止每次追加新記錄都要重新映射。

           

          下面是寫入員工記錄的對比測試:

          使用java.io的代碼:

              view plaincopy to clipboardprint?

          public boolean addRecord_IO(Employee emp) {   

                  try {   

                      byte[] last = emp.last.getBytes();   

                      byte[] first = emp.first.getBytes();   

                      byte[] comments = emp.comments.getBytes();   

                        

                      // Just hard-code the sizes for perfomance   

                      int size = 0;   

                      size += emp.last.length();   

                      size += 4; // strlen - Integer   

                      size += emp.first.length();   

                      size += 4; // strlen - Integer   

                      size += 4; // emp.id - Integer   

                      size += 4; // emp.zip - Integer   

                      size += 1; // emp.employed - byte   

                      size += emp.comments.length();   

                      size += 4; // strlen - Integer   

                      long offset = getStorageLocation(size);   

                      //   

                      // Store the record by key and save the offset   

                      //   

                      if ( offset == -1 ) {   

                          // We need to add to the end of the journal. Seek there   

                          // now only if we're not already there   

                          long currentPos = journal.getFilePointer();   

                          long jounralLen = journal.length();   

                          if ( jounralLen != currentPos )   

                              journal.seek(jounralLen);   

                                

                          offset = jounralLen;   

                      }else {   

                          // Seek to the returned insertion point   

                          journal.seek(offset);   

                      }   

                      // Fist write the header   

                      journal.writeByte(1);   

                      journal.writeInt(size);   

                      // Next write the data   

                      journal.writeInt(last.length);   

                      journal.write(last);   

                      journal.writeInt(first.length);   

                      journal.write(first);   

                      journal.writeInt(emp.id);   

                      journal.writeInt(emp.zip);   

                      if ( emp.employed )   

                          journal.writeByte(1);   

                      else  

                          journal.writeByte(0);   

                      journal.writeInt(comments.length);   

                      journal.write(comments);   

                      // Next, see if we need to append an empty record if we inserted   

                      // this new record at an empty location   

                      if ( newEmptyRecordSize != -1 ) {   

                          // Simply write a header   

                          journal.writeByte(0); //inactive record   

                          journal.writeLong(newEmptyRecordSize);   

                      }   

                      employeeIdx.put(emp.last, offset);   

                      return true;   

                  }   

                  catch ( Exception e ) {   

                      e.printStackTrace();   

                  }   

                  return false;   

              }  

          public boolean addRecord_IO(Employee emp) {

                  try {

                      byte[] last = emp.last.getBytes();

                      byte[] first = emp.first.getBytes();

                      byte[] comments = emp.comments.getBytes();

                     

                      // Just hard-code the sizes for perfomance

                      int size = 0;

                      size += emp.last.length();

                      size += 4; // strlen - Integer

                      size += emp.first.length();

                      size += 4; // strlen - Integer

                      size += 4; // emp.id - Integer

                      size += 4; // emp.zip - Integer

                      size += 1; // emp.employed - byte

                      size += emp.comments.length();

                      size += 4; // strlen - Integer

                      long offset = getStorageLocation(size);

                      //

                      // Store the record by key and save the offset

                      //

                      if ( offset == -1 ) {

                          // We need to add to the end of the journal. Seek there

                          // now only if we're not already there

                          long currentPos = journal.getFilePointer();

                          long jounralLen = journal.length();

                          if ( jounralLen != currentPos )

                              journal.seek(jounralLen);

                             

                          offset = jounralLen;

                      }else {

                          // Seek to the returned insertion point

                          journal.seek(offset);

                      }

                      // Fist write the header

                      journal.writeByte(1);

                      journal.writeInt(size);

                      // Next write the data

                      journal.writeInt(last.length);

                      journal.write(last);

                      journal.writeInt(first.length);

                      journal.write(first);

                      journal.writeInt(emp.id);

                      journal.writeInt(emp.zip);

                      if ( emp.employed )

                          journal.writeByte(1);

                      else

                          journal.writeByte(0);

                      journal.writeInt(comments.length);

                      journal.write(comments);

                      // Next, see if we need to append an empty record if we inserted

                      // this new record at an empty location

                      if ( newEmptyRecordSize != -1 ) {

                          // Simply write a header

                          journal.writeByte(0); //inactive record

                          journal.writeLong(newEmptyRecordSize);

                      }

                      employeeIdx.put(emp.last, offset);

                      return true;

                  }

                  catch ( Exception e ) {

                      e.printStackTrace();

                  }

                  return false;

              } 

           

          使用java.nio的代碼:

              view plaincopy to clipboardprint?

          public boolean addRecord_NIO(Employee emp) {   

                  try {   

                      data.clear();   

                      byte[] last = emp.last.getBytes();   

                      byte[] first = emp.first.getBytes();   

                      byte[] comments = emp.comments.getBytes();   

                      data.putInt(last.length);   

                      data.put(last);   

                      data.putInt(first.length);   

                      data.put(first);   

                      data.putInt(emp.id);   

                      data.putInt(emp.zip);   

                      byte employed = 0;   

                      if ( emp.employed )   

                          employed = 1;   

                      data.put(employed);   

                      data.putInt(comments.length);   

                      data.put(comments);   

                      data.flip();   

                      int dataLen = data.limit();   

                      header.clear();   

                      header.put((byte)1); // 1=active record   

                      header.putInt(dataLen);   

                      header.flip();   

                      long headerLen = header.limit();   

                      int length = (int)(headerLen + dataLen);   

                      long offset = getStorageLocation((int)dataLen);   

                      //   

                      // Store the record by key and save the offset   

                      //   

                      if ( offset == -1 ) {   

                          // We need to add to the end of the journal. Seek there   

                          // now only if we're not already there   

                          long currentPos = channel.position();   

                          long jounralLen = channel.size();   

                          if ( jounralLen != currentPos )   

                              channel.position(jounralLen);   

                          offset = jounralLen;   

                      }   

                      else {   

                          // Seek to the returned insertion point   

                          channel.position(offset);   

                      }   

                      // Fist write the header   

                      long written = channel.write(srcs);   

                      // Next, see if we need to append an empty record if we inserted   

                      // this new record at an empty location   

                      if ( newEmptyRecordSize != -1 ) {   

                          // Simply write a header   

                          data.clear();   

                          data.put((byte)0);   

                          data.putInt(newEmptyRecordSize);   

                          data.flip();   

                          channel.write(data);   

                      }   

                      employeeIdx.put(emp.last, offset);   

                      return true;   

                  }   

                  catch ( Exception e ) {   

                      e.printStackTrace();   

                  }   

                  return false;   

              }  

          public boolean addRecord_NIO(Employee emp) {

                  try {

                      data.clear();

                      byte[] last = emp.last.getBytes();

                      byte[] first = emp.first.getBytes();

                      byte[] comments = emp.comments.getBytes();

                      data.putInt(last.length);

                      data.put(last);

                      data.putInt(first.length);

                      data.put(first);

                      data.putInt(emp.id);

                      data.putInt(emp.zip);

                      byte employed = 0;

                      if ( emp.employed )

                          employed = 1;

                      data.put(employed);

                      data.putInt(comments.length);

                      data.put(comments);

                      data.flip();

                      int dataLen = data.limit();

                      header.clear();

                      header.put((byte)1); // 1=active record

                      header.putInt(dataLen);

                      header.flip();

                      long headerLen = header.limit();

                      int length = (int)(headerLen + dataLen);

                      long offset = getStorageLocation((int)dataLen);

                      //

                      // Store the record by key and save the offset

                      //

                      if ( offset == -1 ) {

                          // We need to add to the end of the journal. Seek there

                          // now only if we're not already there

                          long currentPos = channel.position();

                          long jounralLen = channel.size();

                          if ( jounralLen != currentPos )

                              channel.position(jounralLen);

                          offset = jounralLen;

                      }

                      else {

                          // Seek to the returned insertion point

                          channel.position(offset);

                      }

                      // Fist write the header

                      long written = channel.write(srcs);

                      // Next, see if we need to append an empty record if we inserted

                      // this new record at an empty location

                      if ( newEmptyRecordSize != -1 ) {

                          // Simply write a header

                          data.clear();

                          data.put((byte)0);

                          data.putInt(newEmptyRecordSize);

                          data.flip();

                          channel.write(data);

                      }

                      employeeIdx.put(emp.last, offset);

                      return true;

                  }

                  catch ( Exception e ) {

                      e.printStackTrace();

                  }

                  return false;

              } 

           

          使用MappedByteBuffer的代碼如下:

           

             view plaincopy to clipboardprint?

          public boolean addRecord_MBB(Employee emp) {   

                  try {   

                      byte[] last = emp.last.getBytes();   

                      byte[] first = emp.first.getBytes();   

                      byte[] comments = emp.comments.getBytes();   

                      int datalen = last.length + first.length + comments.length + 12 + 9;   

                      int headerlen = 5;   

                      int length = headerlen + datalen;   

                      //   

                      // Store the record by key and save the offset   

                      //   

                      long offset = getStorageLocation(datalen);   

                      if ( offset == -1 ) {   

                          // We need to add to the end of the journal. Seek there   

                          // now only if we're not already there   

                          long currentPos = mbb.position();   

                          long journalLen = channel.size();   

                          if ( (currentPos+length) >= journalLen ) {   

                              //log("GROWING FILE BY ANOTHER PAGE");   

                              mbb.force();   

                              journal.setLength(journalLen + PAGE_SIZE);   

                              channel = journal.getChannel();   

                              journalLen = channel.size();   

                              mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, journalLen);   

                              currentPos = mbb.position();   

                          }   

                          if ( currentEnd != currentPos )   

                              mbb.position(currentEnd);   

                          offset = currentEnd;//journalLen;   

                      }   

                      else {   

                          // Seek to the returned insertion point   

                          mbb.position((int)offset);   

                      }   

                      // write header   

                      mbb.put((byte)1); // 1=active record   

                      mbb.putInt(datalen);   

                      // write data   

                      mbb.putInt(last.length);   

                      mbb.put(last);   

                      mbb.putInt(first.length);   

                      mbb.put(first);   

                      mbb.putInt(emp.id);   

                      mbb.putInt(emp.zip);   

                      byte employed = 0;   

                      if ( emp.employed )   

                          employed = 1;   

                      mbb.put(employed);   

                      mbb.putInt(comments.length);   

                      mbb.put(comments);   

                      currentEnd += length;   

                      // Next, see if we need to append an empty record if we inserted   

                      // this new record at an empty location   

                      if ( newEmptyRecordSize != -1 ) {   

                          // Simply write a header   

                          mbb.put((byte)0);   

                          mbb.putInt(newEmptyRecordSize);   

                          currentEnd += 5;   

                      }   

                      employeeIdx.put(emp.last, offset);   

                      return true;   

                  }   

                  catch ( Exception e ) {   

                      e.printStackTrace();   

                  }   

                  return false;   

              }  

          public boolean addRecord_MBB(Employee emp) {

                  try {

                      byte[] last = emp.last.getBytes();

                      byte[] first = emp.first.getBytes();

                      byte[] comments = emp.comments.getBytes();

                      int datalen = last.length + first.length + comments.length + 12 + 9;

                      int headerlen = 5;

                      int length = headerlen + datalen;

                      //

                      // Store the record by key and save the offset

                      //

                      long offset = getStorageLocation(datalen);

                      if ( offset == -1 ) {

                          // We need to add to the end of the journal. Seek there

                          // now only if we're not already there

                          long currentPos = mbb.position();

                          long journalLen = channel.size();

                          if ( (currentPos+length) >= journalLen ) {

                              //log("GROWING FILE BY ANOTHER PAGE");

                              mbb.force();

                              journal.setLength(journalLen + PAGE_SIZE);

                              channel = journal.getChannel();

                              journalLen = channel.size();

                              mbb = channel.map(FileChannel.MapMode.READ_WRITE, 0, journalLen);

                              currentPos = mbb.position();

                          }

                          if ( currentEnd != currentPos )

                              mbb.position(currentEnd);

                          offset = currentEnd;//journalLen;

                      }

                      else {

                          // Seek to the returned insertion point

                          mbb.position((int)offset);

                      }

                      // write header

                      mbb.put((byte)1); // 1=active record

                      mbb.putInt(datalen);

                      // write data

                      mbb.putInt(last.length);

                      mbb.put(last);

                      mbb.putInt(first.length);

                      mbb.put(first);

                      mbb.putInt(emp.id);

                      mbb.putInt(emp.zip);

                      byte employed = 0;

                      if ( emp.employed )

                          employed = 1;

                      mbb.put(employed);

                      mbb.putInt(comments.length);

                      mbb.put(comments);

                      currentEnd += length;

                      // Next, see if we need to append an empty record if we inserted

                      // this new record at an empty location

                      if ( newEmptyRecordSize != -1 ) {

                          // Simply write a header

                          mbb.put((byte)0);

                          mbb.putInt(newEmptyRecordSize);

                          currentEnd += 5;

                      }

                      employeeIdx.put(emp.last, offset);

                      return true;

                  }

                  catch ( Exception e ) {

                      e.printStackTrace();

                  }

                  return false;

              } 

           

          接下來,調用每種方法插入100,000條記錄, 耗時對比如下:

              * With java.io: ~10,000 milliseconds

              * With java.nio: ~2,000 milliseconds

              * With MappedByteBuffer: ~970 milliseconds

           

          使用NIO的性能改善效果非常明顯,使用MappedByteBuffer的性能,更是讓人吃驚。

          使用三種方式讀取數據的性能對比如下:

              * With java.io: ~6,900 milliseconds

              * With java.nio: ~1,400 milliseconds

              * With MappedByteBuffer: ~355 milliseconds

          和寫入的時候情況差不多,NIO有很明顯的性能提升,而MappedByteBuffer則有驚人的高效率。從java.io遷移到nio并使用MappedByteBuffer,通常可以獲得10倍以上的性能提升。

           

          源文檔 <http://blog.csdn.net/sean1203/archive/2010/01/06/5142464.aspx

          posted on 2010-10-14 19:29 ivaneeo 閱讀(1012) 評論(0)  編輯  收藏 所屬分類: java魔力
          主站蜘蛛池模板: 临清市| 衡阳县| 呼玛县| 富平县| 双柏县| 桦南县| 武宣县| 宣威市| 丘北县| 平塘县| 雷州市| 临沭县| 全椒县| SHOW| 石家庄市| 二连浩特市| 固安县| 札达县| 重庆市| 合川市| 潍坊市| 本溪市| 紫云| 西林县| 武夷山市| 曲周县| 望城县| 阿荣旗| 吉首市| 汉沽区| 广丰县| 平泉县| 江口县| 黔西| 晋城| 肥西县| 蒙城县| 获嘉县| 湖北省| 宣恩县| 郸城县|