代码之家  ›  专栏  ›  技术社区  ›  Simon

用于加速Java文件通道中随机读取的代码提示?

  •  3
  • Simon  · 技术社区  · 15 年前

    我有一个大的(3GB)双精度二进制文件,在我为集群数据编写的迭代算法中,随机访问(或多或少)双精度文件。每次迭代从文件中读取大约50万个数据,并写入大约10万个新值。

    我创建了这样的文件通道…

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

    然后我用一个双字节大小的私有字节来读取它

    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配置中,我是否可以做些什么来加速读操作?我意识到我可以改变硬件,但在我做这件事之前,我想确保我已经挤出了问题的最后一滴软件汁。

    提前谢谢

    5 回复  |  直到 15 年前
        1
  •  4
  •   Michael Borgwardt    15 年前

    只要您的文件存储在常规硬盘上,您就可以通过以一种能够提供访问位置的方式组织数据来获得最大可能的加速,也就是说,使一行中尽可能多的get/set调用访问文件的相同小区域。

    这比你能做的任何其他事情都重要,因为在高清上访问随机点是现代PC做的最慢的事情——它比任何其他东西都要长10000倍。

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

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

        2
  •  4
  •   Gregory Pakosz    15 年前

    而不是读成 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块。这样做有助于将不同的感兴趣的像素保存在一个连续的内存区域中(并且希望保存在缓存中)。

    您可以将这个想法转换为您的算法(如果适用):您将文件的一部分映射一次,这样不同的调用 获取值(行、列) 设置值(行、列) 对刚刚映射的部分进行处理。

        3
  •  1
  •   djna    15 年前

    如果我们能减少读取的次数,那么事情就会发展得更快。

    3GB不是 巨大的 对于64位的JVM,因此相当多的文件可以放在内存中。

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

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

        4
  •  1
  •   ThinkJet    15 年前
    1. 逐字节访问总是产生较差的性能(不仅仅在Java中)。尝试读/写更大的块(例如行或列)。

    2. 如何切换到数据库引擎来处理这些数量的数据?它将为您处理所有优化。

    可能是 This article 帮助你…

        5
  •  1
  •   Robert Christie    15 年前

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

    这个 HDF 文件格式可能比较合适。它有一个 Java API 但不是纯Java。它是根据Apache风格的许可证授权的。