2010-04-26 66 views
4

我有約。 30000個文件(每個1MB),我想把它放到一個本地方法中,它只需要一個字節數組和它的大小作爲參數。如何將文件(從Java讀取)最有效地傳遞給本地方法?

我看了一些例子和基準(如http://nadeausoftware.com/articles/2008/02/java_tip_how_read_files_quickly),但他們都做了一些其他奇特的事情。

基本上我不在乎文件的內容,我不想訪問該文件或字節數組中的某些內容,或者對它做任何其他操作。我只想將一個文件放入一個儘可能快地接受字節數組的本地方法。

目前我使用的是RandomAccessFile,但是速度非常慢(10MB/s)。

有沒有像

byte[] readTheWholeFile(File file){ ... } 

任何東西,我可以投入

native void fancyCMethod(readTheWholeFile(myFile), myFile.length()) 

你有什麼建議?

+2

有沒有原因你不能在本機代碼本身做I/O?由於存儲器模型不同,將數據從Java傳遞到本機相當昂貴。 – 2010-04-26 16:28:44

+0

不,可悲的是,這是不可能的。我希望有一種方式,JVM認爲放入本地方法的字節數組不會在Java代碼中再次使用,只是將內存範圍提供給本機代碼,而不是開始複製它。 – soc 2010-04-26 16:37:59

+0

有沒有辦法獲得Java中的內存範圍 – 2010-04-26 19:31:15

回答

1

使用規則陣列可能是低效的,因爲它傳遞給本機代碼當VM可以複製的陣列,且餘時,也可以使用中間存儲器/ O。

要獲得最快的IO,請使用ByteBuffer.allocateDirect分配一個字節緩衝區。底層數組是「特殊的」,因爲它不是常規JVM堆的一部分。本機代碼和I/O可以直接訪問陣列。

要讀取的數據到緩衝器的使用,

ByteBuffer byteBuffer = ByteBuffer.allocateDirect(randomAccessFile.length()); 
RandomAccessFile.getChannel().read(byteBuffer, 0); 

要得到背襯陣列傳遞給JNI使用

byte[] byteArray = byteBuffer.array(); 

然後,可以通過這個陣列和文件長度JNI。

直接緩衝區的創建非常繁重,因爲所有文件都是1MB(或其附近),您應該可以在多個文件上重複使用相同的緩衝區。

希望這會有所幫助!

+0

感謝您的回答mdma! 我只是想知道,我怎麼能確定該數組()將工作? Javadoc說:「在調用此方法之前調用hasArray方法以確保此緩衝區具有可訪問的後備數組。」 而allocateDirect()告訴我「它是否有一個支持數組未指定。」 我想知道這是否會奏效? – soc 2010-04-27 07:45:25

+0

這是一些與平臺相關的功能,它依賴於虛擬機。您可以捕獲array()拋出的Excption,並使用ByteBuffer.get(byte [])獲取數組作爲後備。如果你真的直接訪問所有的虛擬機,你可以編寫一個小的JNI存根方法,它接受直接的ByteBuffer實例,並調用GetDirectByteBufferAddress,然後轉發給你的原始JNI方法。 如果ByteBuffer不得不將數據複製到一個新數組中,它將會很快 - 這些都是優化的方法,並且比單獨將一個文件讀入一個字節[]更快。 – mdma 2010-04-27 13:47:30

+0

另一點可能會幫助你的表現 - 使用多線程。即使您的應用程序將被I/O綁定,I/O將阻止等待數據(例如非連續文件)。使用多個線程同時讀取不同文件會使您的應用程序加速,尤其是使用異步I/O。 ForkJoin框架(JSR 166)對於這類工作非常有用,並且非常易於使用: 將文件操作重構爲任務。爲每個要處理的文件創建一個任務,並將其全部放入任務隊列中。任務隊列然後以您指定的並行性級別運行這些任務。 – mdma 2010-04-27 13:53:14

1

我不完全確定這是你問的,但它聽起來像你想有效地將​​文件的內容作爲字節數組傳遞給本地方法。

如果是這樣的話,我建議你使用BufferedInputStream讀取Java中的文件內容,並將它們存儲在經ByteBuffer#allocateDirect分配ByteBuffer,這樣可以傳遞給JNI側和整個訪問。現在,在本機方法中,您可以撥打GetDirectByteBufferAddress直接訪問緩衝區。

0

這裏是readFileFully的樣品,你可以實現

public static byte[] readFileFully(String aFileName) throws IOException 
    { 
     byte[] retData = null; 

     File inputFile = new File(aFileName); 
     if (inputFile == null || !inputFile.exists() || !inputFile.canRead()) 
     { 
     throw new IOException("INVALID FILE : " + aFileName); 
     } 

     // Read in the file data 
     BufferedInputStream iStream = null; 
     try 
     { 
     iStream = new BufferedInputStream(new FileInputStream(inputFile)); 
     int size = (int)inputFile.length(); 
     retData = new byte[size]; 
     int bytes_read = 0; 

     // read stuff in here 
     while (bytes_read < size) 
     { 
      bytes_read += iStream.read(retData,bytes_read,size - bytes_read); 
     } 
     } 
     finally 
     { 
     if (iStream != null) 
     { 
      try 
      { 
       iStream.close(); 
      } 
      catch(IOException e) 
      { 
      } 
     } 
     inputFile = null; 
     } 
     return retData; 
    } 
相關問題