【发布时间】:2012-02-16 05:18:52
【问题描述】:
我正在为一个课程做一个项目,该课程专注于在内存中存储一个大部分为 0 值的巨大矩阵,并对其执行一些矩阵数学运算。我的第一个想法是使用HashMap来存储矩阵元素,并且只存储非零元素,以避免使用大量内存。
我想为HashMap 创建一个键,它表示元素的行号和列号,当我访问地图中的那个条目时,我可以重新提取这两个值。我不知道 Java 和 C#——在 C# 中我会用 Row 和 Column 成员创建一个 struct,但在 Java 中我很快意识到没有用户值类型。随着最后期限的临近,我做了一个安全的赌注,并把Key 做得很长。我使用一些非常简单的位移将行数据(32 位 int)存储在前 32 位中,将列数据存储在后 32 位中。 [编辑:我还想指出,我的 HashMap 是用一个特定的初始大小初始化的,它完全代表我存储在其中的值的数量,这个数量永远不会超过。]
[旁注:我希望能够再次提取行/列数据的原因是为了大大提高矩阵乘法的效率,从O(n^2)到O(n),以及一个更小的n来引导]
实现此结构后我注意到的是,从仅给出非零元素的文本文件中读取 23426 x 23426 矩阵需要 7 秒,但计算特征值只需要 2 秒我们必须给予!在对方法进行选择性注释后,我得出结论,这 7 秒时间跨度的大部分时间都用于将我的值存储在 HashMap 中。
public void Set(double value, int row, int column) {
//assemble the long key, placing row and column in adjacent sets of bits
long key = (long)row << SIZE_BIT_MAX; //(SIZE_BIT_MAX is 32)
key += column;
elements.put(key, value);
}
这是设置值的代码。如果我改用这种方法:
public void Set(double value, int row, int column) {
//create a distinct but smaller key (around 32 bits max)
long key = (long)(row * matrixSize) + column;
elements.put(key, value);
}
读取仅需 2 秒。这两个版本的密钥对于每个元素都是不同的,都是长类型,并且创建它们的实际代码的复杂性最小。是 elements.put(key, value) 造成了 7 秒和 2 秒之间的差异。
我的问题是,为什么?我在这些关键版本之间看到的区别在于,第一个版本的位始终设置为 1,并且更频繁,而第二个版本的所有最高 32 位都设置为 0。我是在追逐红鲱鱼,还是这个相当显着的差异在性能上是 HashMap.put 方法内部的结果?
【问题讨论】:
-
没有SSCCE,很难告诉你原因。我的猜测是您没有指定地图的初始大小。然后它开始时非常小,并且必须经常调整自己的大小。调整大小,尤其是大地图的调整成本非常高。
-
初始大小已指定且永不超过。我将编辑我的帖子以反映这一点。
-
可能会有小改进,但创建具有适当数量的初始元素的 HashMap 以避免在达到新容量时不断重新散列。例如 new HashMap
(20000); -
另一个问题是,教授评估的是什么?只是你能不能做到?是否可以实现高效的稀疏矩阵算法?如果是后者,HashMap 可能会给你一个“C”。
-
查看我的评论和编辑;我在项目中很快就意识到了这一点。
标签: java performance hashmap bit sparse-matrix