我有一个大的(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配置来加速读取?我意识到我可以改变硬件,但在我这样做之前,我想确保我已经从问题中挤出了最后一滴软件。
提前致谢
答案 0 :(得分:4)
我会使用文件映射,而不是阅读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)
的不同调用对此部分进行处理映射。
答案 1 :(得分:4)
只要您的文件存储在常规硬盘上,您就可以通过以一种给出访问位置的方式组织数据来获得最大可能的加速,即导致尽可能多的获取/设置调用访问该文件的同一个小区域。
这比你能做的任何其他事情都重要,因为访问高清上的随机点是迄今为止现代PC所做的最慢的事情 - 它比其他任何事情都要长约10,000倍。
因此,如果一次只能处理数据集的一部分(小到足以舒适地适应内存中的高速缓存),然后将结果组合起来,那么就这样做。
或者,通过将文件存储在SSD或(更好地)存储在RAM中来避免此问题。即使将其存放在简单的拇指驱动器上也是一个很大的改进。
答案 2 :(得分:1)
大概如果我们可以减少阅读次数,那么事情会更快。
对于64位JVM,3Gb不是 large ,因此相当多的文件适合内存。
假设您将文件视为缓存的“页面”。读取值时,请阅读其周围的页面并将其保留在内存中。然后,当您执行更多操作时,请先检查缓存。
或者,如果你有能力,在处理开始时将整个内容读入内存。
答案 3 :(得分:1)
逐字节访问总是会产生较差的性能(不仅仅是在Java中)。尝试读/写更大的块(例如行或列)。
如何切换到数据库引擎来处理这样的数据?它将为您处理所有优化。
可能会This article帮助你......
答案 4 :(得分:1)