如何在Java中存储100K X 100K矩阵?
我不能用正常的数组声明来做,因为它抛出java.lang.OutofMemoryError
。
答案 0 :(得分:14)
答案 1 :(得分:10)
如果矩阵中的绝大多数条目都为零(或者甚至是其他一些常数值),则稀疏矩阵将是合适的。否则,可能会重写您的算法,以便整个矩阵不会同时存在。例如,您可以一次生成和使用一行。
答案 2 :(得分:7)
100,000 x 100,000 = 10,000,000,000(100亿)条目。即使您存储单字节条目,仍然在10 GB附近 - 您的机器甚至还有那么多物理内存,更不用说有意愿将这么多内容分配给单个进程了吗?
你可能需要考虑某种方式,只在任何给定时间将矩阵的一部分保留在内存中,其余的则缓存在磁盘上。
答案 3 :(得分:7)
听起来你需要一个稀疏矩阵。其他人已经提出了可以满足您需求的良好第三方实施......
根据您的应用程序,只需使用Map作为矩阵数据的后备存储,就可以在没有第三方矩阵库的情况下离开。有点......
public class SparseMatrix<T> {
private T defaultValue;
private int m;
private int n;
private Map<Integer, T> data = new TreeMap<Integer, T>();
/// create a new matrix with m rows and n columns
public SparseMatrix(int m, int n, T defaultValue) {
this.m = m;
this.n = n;
this.defaultValue = defaultValue;
}
/// set value at [i,j] (row, col)
public void setValueAt(int i, int j, T value) {
if (i >= m || j >= n || i < 0 || j < 0)
throw new IllegalArgumentException(
"index (" + i + ", " +j +") out of bounds");
data.put(i * n + j, value);
}
/// retrieve value at [i,j] (row, col)
public T getValueAt(int i, int j) {
if (i >= m || j >= n || i < 0 || j < 0)
throw new IllegalArgumentException(
"index (" + i + ", " +j +") out of bounds");
T value = data.get(i * n + j);
return value != null ? value : defaultValue;
}
}
说明SparseMatrix使用的简单测试用例是:
public class SparseMatrixTest extends TestCase {
public void testMatrix() {
SparseMatrix<Float> matrix =
new SparseMatrix<Float>(100000, 100000, 0.0F);
matrix.setValueAt(1000, 1001, 42.0F);
assertTrue(matrix.getValueAt(1000,1001) == 42.0);
assertTrue(matrix.getValueAt(1001,1000) == 0.0);
}
}
这不是最有效的方法,因为矩阵中的每个非默认条目都存储为Object。根据您期望的实际值的数量,这种方法的简单性可能胜过集成第三方解决方案(并且可能再次根据您的情况处理其许可证)。
在上面的SparseMatrix实现中添加乘法之类的矩阵运算应该是直截了当的(并留给读者练习; - )
答案 4 :(得分:5)
根据您拥有的内存量,阵列的实际稀疏程度以及访问模式的不同,有许多可能的解决方案。
如果100K * 100K * 8的计算量小于机器上供JVM使用的物理内存量,那么简单的非稀疏阵列是可行的解决方案。
如果数组稀疏,(例如)75%或更多元素为零,则可以使用稀疏数组库来节省空间。已经提出了各种替代方案,但在所有情况下,如果能够为您节省足够的成本,您仍然需要解决问题。计算出有多少非零元素,乘以8(给你双精度)和(比如)4来计算稀疏数组的开销。如果这小于您可以为JVM提供的物理内存量,那么稀疏阵列是一种可行的解决方案。
如果稀疏和非稀疏数组(在内存中)不起作用,事情会变得更复杂,任何解决方案的可行性将取决于数组数据的访问模式。
一种方法是将数组表示为以MappedByteBuffer形式映射到内存的文件。假设您没有足够的物理内存来将整个文件存储在内存中,那么您将很难进入虚拟内存系统。因此,最好是您的算法只需要随时对阵列的连续部分进行操作。否则,你可能会死于交换。
第二种方法是第一种方法的变体。一次将数组/文件映射到一个部分,完成后,取消映射并移至下一部分。这仅适用于算法在节中处理数组的情况。
第三种方法是使用像BDB这样的轻量级数据库来表示数组。这将比任何内存中的解决方案慢,因为读取数组元素将转换为光盘访问。但如果你弄错了它就不会像内存映射方法那样杀死系统。 (如果你在Linux / Unix上执行此操作,系统的磁盘块缓存可能会加快速度,具体取决于算法的阵列访问模式)
第四种方法是使用分布式内存缓存。这会将光盘i / o替换为网络i / o,很难说这是好事还是坏事。
第五种方法是分析您的算法,看它是否适合作为分布式算法实现;例如数组的各个部分以及算法的相应部分在不同的机器上。
答案 5 :(得分:4)
您可以升级到此计算机:
http://www.azulsystems.com/products/compute_appliance.htm
864个处理器内核和768 GB内存,只需要在某个地方购买一个家庭住宅。
答案 6 :(得分:3)
好吧,我建议你增加jvm中的内存,但是你需要大量的内存,因为你正在谈论100亿个项目。它(很少)可能有大量内存或群集jvm,但这可能是错误的答案。
你得到outOfmemory,因为如果你声明int [1000],内存会被立即分配(另外双倍占用比int更多的空间 - 一个int表示也会节省你的空间)。也许你可以替换一个更有效的数组实现(如果你有许多空条目查找“稀疏矩阵”表示)。
您可以将片段存储在外部系统中,例如memcached或内存映射缓冲区。
这里有很多好的建议,也许如果你发布了一个更详细的问题描述,你试图解决的问题可能更具体。
答案 7 :(得分:2)
你应该尝试一个“外部”包来处理矩阵,但我从来没有这样做,可能像jama那样。
答案 8 :(得分:2)
除非您有100K x 100K x 8~80GB的内存,否则无法在内存中创建此矩阵。您可以在磁盘上创建此矩阵并使用内存映射访问它。但是,使用这种方法会非常慢。
你想做什么?您可能会发现以不同方式表示数据会更有效率。