在Java中将浮点数组写入文件

5
我正在读取一个NetCDF文件,我想将每个数组作为浮点数组读入,然后将浮点数组写入新文件。如果我读取浮点数组并迭代每个元素(使用DataOutputStream),我可以使其工作,但这非常慢,我的NetCDF文件超过1GB。
我尝试使用ObjectOutputStream,但这会写入额外的信息字节。
因此,总结一下。 1. 打开NetCDF文件 2. 从NetCDF文件中读取浮点数组x 3. 一次性将浮点数组x写入原始数据文件 4. 使用x+1重复步骤2

3
我猜你试图使用BufferedOutputStream来提高写入性能,对吗? - Simone Gianni
5个回答

3
好的,您有1 GB的读取和1 GB的写入空间。根据您的硬盘,您可能会获得大约100 MB/s的读取速度和60 MB/s的写入速度。这意味着需要大约27秒来读取和写入。
您的驱动器速度是多少,比这慢多少?
如果您想在没有任何处理的情况下测试磁盘速度,请记录复制一个文件所需的时间(即它不在磁盘缓存中)。这将使您了解从文件中读取并写入大部分数据的最小延迟(即没有处理或Java参与)。
为了让任何想知道如何进行循环无损数据复制的人受益,即它不仅调用为您循环的方法。
FloatBuffer src = // readable memory mapped file.
FloatByffer dest = // writeable memory mapped file.
src.position(start);
src.limit(end);
dest.put(src);

如果您有混合类型的数据,可以使用ByteBuffer,它在概念上每次复制一个字节,但实际上可以使用长整型或更宽的类型一次复制8个或更多字节。即无论CPU能做什么。
对于小块,这将使用循环,但对于大块,它可以使用操作系统中的页面映射技巧。无论如何,Java中没有定义它是如何完成的,但它可能是最快的复制数据的方法。
大多数这些技巧只有在将已经在内存中的文件复制到缓存文件时才会有所不同。一旦从磁盘读取文件或文件太大而无法缓存,您的物理磁盘的IO带宽就是真正重要的事情。
这是因为CPU可以将数据复制到主内存的速度为6 GB/s,但将数据复制到硬盘的速度只有60-100 MB/s。如果CPU/内存中的复制速度比它本来可以达到的速度慢2倍、10倍或50倍,它仍然会等待磁盘。注意:如果没有缓冲,这完全是可能的,甚至更糟,但只要有任何简单的缓冲,CPU就会比磁盘快。

我的电脑是全新的Mac Pro,速度非常快。是Java的开销比较慢。 - user869525
2
这是您已经测量并且可以看到差异的内容,还是因为您从未出错而“知道”的内容。;)换句话说,请给我一些数字。我有两个高性能SSD驱动器,并且我可以在Java中测量它们的性能,这不是瓶颈。 - Peter Lawrey

2

我遇到了相同的问题,现在把我的解决方案记录在这里以供将来参考。

如果你需要迭代一个包含浮点数的数组,并且对于每个浮点数都调用DataOutputStream.writeFloat方法,那么速度会非常慢。相反,你应该自己将浮点数转换成字节数组,然后一次性写入该数组:

慢的写法:

DataOutputStream out = ...;
for (int i=0; i<floatarray.length; ++i)
    out.writeFloat(floatarray[i]);

更快速
DataOutputStream out = ...;
byte buf[] = new byte[4*floatarray.length];
for (int i=0; i<floatarray.length; ++i)
{
    int val = Float.floatToRawIntBits(probs[i]);
    buf[4 * i] = (byte) (val >> 24);
    buf[4 * i + 1] = (byte) (val >> 16) ;
    buf[4 * i + 2] = (byte) (val >> 8);
    buf[4 * i + 3] = (byte) (val);
}

out.write(buf);

如果数组非常大(>100k),将其分成块以避免使用缓冲数组时出现堆溢出。


1

1) 写入时,请使用BufferedOutputStream,您将获得100倍的加速。

2) 读取时,请至少每次读取10K,可能100K更好。

3) 发布您的代码。


0

如果您正在使用Unidata NetCDF库,那么您的问题可能不是写作问题,而是NetCDF库缓存机制。

     NetcdfFile file = NetcdfFile.open(filename);
     Variable variable = openFile.findVariable(variable name);
     for (...) {
          read data
          variable.invalidateCache();
      }

0
侧重解决方案:
如果这是一次性的生成(或者你愿意在Ant脚本中自动化),并且你可以访问某种Unix环境,那么你可以使用NCDUMP而不是在Java中执行。类似于:
ncdump -v your_variable your_file.nc | [awk] > float_array.txt

如果需要,您可以使用 -p 选项控制浮点数的精度。我刚刚在一个3GB的NetCDF文件上运行了它,效果很好。尽管我非常喜欢Java,但这可能是实现您想要的功能最快的方法。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接