6

我正在尝试从 java 程序创建 300M 文件,我从旧文件 API 切换到新的 java 7 nio 包,但新包的运行速度比旧包还要慢。

我发现 CPU 使用率比使用旧文件 API 时要少,但我正在运行这个简单的代码,我得到 0.5Mbytes/sec 的文件传输速率,并且来自 java 的写入正在读取一个磁盘并写入另一个(写入是访问磁盘的唯一进程)。

Files.write(FileSystems.getDefault().getPath(filePath), fiveToTenKBytes, StandardOpenOption.CREATE);

有没有希望在这里获得合理的吞吐量?


更新:

我正在从大文件中解压缩 3 亿个 5-10k 字节的图像文件。我有 3 个磁盘,1 个本地磁盘和 2 个 SAN 连接(大文件的典型吞吐率约为 20MB/秒)。

我还尝试了这段代码,它将速度提高到几乎不低于 2MB/秒的吞吐量(解压这些文件需要 9 天)。

ByteBuffer byteBuffer = ByteBuffer.wrap(imageBinary, 0, (BytesWritable)value).getLength());
FileOutputStream fos = new FileOutputStream( imageFile );
fos.getChannel().write(byteBuffer);
fos.close();

我从本地磁盘读取并写入 SAN 附加磁盘。我正在读取 Hadoop SequenceFile 格式,hadoop 通常能够使用基本相同的代码以 20MB/秒的速度读取这些文件。

除了超级慢之外,唯一看起来不合适的是,我看到读取 IO 比写入 IO 多大约 2:1,尽管序列文件是 gzip 压缩的(尽管图像实际上得到了 1:1 的比例),所以压缩文件应该是大约。1:1 与输出。


第二次更新

看着iostat我看到一些奇数,我们在这里查看 xvdf,我有一个 java 进程读取xvdb和写入,xvdf并且没有其他进程处于活动状态xvdf

iostat -d 30
Device:            tps    kB_read/s    kB_wrtn/s    kB_read    kB_wrtn
xvdap1            1.37         5.60         4.13        168        124
xvdb             14.80       620.00         0.00      18600          0
xvdap3            0.00         0.00         0.00          0          0
xvdf            668.50      2638.40       282.27      79152       8468
xvdg           1052.70      3751.87      2315.47     112556      69464

读取xvdf是写入的 10 倍,这令人难以置信。

fstab
/dev/xvdf       /mnt/ebs1       auto    defaults,noatime,nodiratime     0       0
/dev/xvdg       /mnt/ebs2       auto    defaults,noatime,nodiratime     0       0
4

2 回答 2

2

如果我正确理解了您的代码,那么您正在将 300M 文件拆分/写入小块(“ fiveToTenKBytes”)。

考虑使用Stream 方法

如果您正在写入磁盘,请考虑使用 BufferedOutputStream 包装 OutputStream。

例如:

try (BufferedOutputStream bos = new BufferedOutputStream(Files.newOutputStream(Paths.getPath(filePathString), StandardOpenOption.CREATE))){

 ...

}
于 2013-03-15T14:00:23.037 回答
1

我认为您的缓慢来自创建新文件,而不是实际传输。我相信在 Linux 中创建文件是一个同步操作:系统调用在创建文件和更新目录之前不会返回。这表明您可以做几件事:

  • 将多个写入线程与单个读取线程一起使用。读取器线程会将源文件中的数据读取到 abyte[]中,然后创建一个Runnable从该数组写入输出文件的 a。使用有很多线程的线程——可能是 100 或更多——因为他们将花费大部分时间等待creat完成。根据您拥有的内存量设置此池的入站队列的容量:如果您的文件大小为 10k,那么 1,000 的队列容量似乎是合理的(没有充分的理由让读者远远领先于作者,因此您甚至可以使用两倍线程数的容量)。
  • 而不是 NIO,使用基本BufferedInputStream的 s 和BufferedOutputStreams. 您的问题是系统调用,而不是内存速度(NIO 类旨在防止堆内存和堆外内存之间的复制)。

我将假设您已经知道不要尝试将所有文​​件存储到单个目录中。甚至在一个目录中存储数百个文件。

作为另一种选择,您是否考虑过使用 S3 进行存储?我猜它的存储桶键比实际目录效率高得多,并且有一个文件系统可以让您像访问文件一样访问存储桶(我自己没有尝试过)。

于 2013-03-16T18:54:39.877 回答