3

我有一个大型(3Gb)二进制文件,我在为聚类数据编写的迭代算法期间随机访问(或多或少)。每次迭代都会从文件中读取大约 50 万次,并写入大约 10 万次新值。

我像这样创建 FileChannel ...

f = new File(_filename);
_ioFile = new RandomAccessFile(f, "rw");
_ioFile.setLength(_extent * BLOCK_SIZE);
_ioChannel = _ioFile.getChannel();

然后我使用一个双倍大小的私有 ByteBuffer 来读取它

private ByteBuffer _double_bb = ByteBuffer.allocate(8);

我的阅读代码看起来像这样

public double GetValue(long lRow, long lCol) 
{
    long idx = TriangularMatrix.CalcIndex(lRow, lCol);
    long position = idx * BLOCK_SIZE;
    double d = 0;
    try 
    {
        _double_bb.position(0);
        _ioChannel.read(_double_bb, position);
        d = _double_bb.getDouble(0);
    } 

    ...snip...

    return d;
}

我这样写...

public void SetValue(long lRow, long lCol, double d) 
{
    long idx = TriangularMatrix.CalcIndex(lRow, lCol);
    long offset = idx * BLOCK_SIZE;
    try 
    {
        _double_bb.putDouble(0, d);
        _double_bb.position(0);
        _ioChannel.write(_double_bb, offset);
    } 

    ...snip...

}

我的代码迭代所花费的时间大致随着读取次数线性增加。我已经对周围的代码进行了一些优化,以尽量减少读取次数,但我认为这是必要的核心集,而不会从根本上改变算法的工作方式,我现在想避免这种情况。

所以我的问题是,读/写代码或 JVM 配置中是否有任何东西可以加快读取速度?我意识到我可以改变硬件,但在我这样做之前,我想确保我已经从问题中挤出了每一滴软件汁液。

提前致谢

4

5 回答 5

4

我不会读入 a ByteBuffer,而是使用文件映射,请参阅:FileChannel.map().

此外,您并没有真正解释您GetValue(row, col)SetValue(row, col)访问存储的方式。或多或少是随机row的?col我想到的想法如下:有时,对于图像处理,当您必须访问像row + 1, row - 1, col - 1,之类的像素以求col + 1平均值时;诀窍是将数据组织成 8 x 8 或 16 x 16 块。这样做有助于将不同的感兴趣像素保持在连续的内存区域(并希望在缓存中)。

您可以将此想法转换为您的算法(如果适用):您将文件的一部分映射一次,以便对刚刚映射的这部分进行不同的调用GetValue(row, col)和处理。SetValue(row, col)

于 2009-12-22T09:00:47.463 回答
4

只要您的文件存储在常规硬盘上,您就可以通过以提供访问局部性的方式组织数据来获得最大可能的加速,即导致尽可能多的 get/set 调用连续访问相同的小文件的区域。

这比你能做的任何事情都更重要,因为访问 HD 上的随机点是迄今为止现代 PC 所做的最慢的事情——它需要的时间比其他任何事情都要长约 10,000 倍。

因此,如果可以一次只处理数据集的一部分(小到可以舒适地放入内存 HD 缓存中)然后合并结果,那么就这样做。

或者,通过将文件存储在 SSD 或(更好)在 RAM 中来避免此问题。即使将它存储在一个简单的拇指驱动器上也可能是一个很大的改进。

于 2009-12-22T09:48:25.090 回答
1

据推测,如果我们可以减少读取次数,那么事情会进展得更快。

3Gb 对于 64 位 JVM 来说并不,因此相当多的文件可以放在内存中。

假设您将文件视为缓存的“页面”。当您读取一个值时,请读取它周围的页面并将其保存在内存中。然后,当您进行更多读取时,首先检查缓存。

或者,如果您有能力,在处理开始时将整个内容读入内存。

于 2009-12-22T08:53:38.797 回答
1
  1. 逐字节访问总是会产生较差的性能(不仅在 Java 中)。尝试读/写更大的块(例如行或列)。

  2. 切换到数据库引擎来处理如此大量的数据怎么样?它将为您处理所有优化。

可能是这篇文章可以帮助你...

于 2009-12-22T09:03:06.210 回答
1

您可能需要考虑使用专为管理大量数据和随机读取而设计的库,而不是使用原始文件访问例程。

HDF文件格式可能非常适合。它有一个Java API,但不是纯 Java。它是在 Apache Style 许可下获得许可的。

于 2009-12-22T09:20:39.990 回答