2010-04-26 61 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; 
    } 
相关问题