映射大量长片的最快方法

时间:2015-06-08 20:44:05

标签: java performance

我正在编写一个将数字(长)转换为一小组结果对象的java应用程序。这种映射过程对于应用程序的性能非常关键,因为它经常需要。

public static Object computeResult(long input) {
    Object result;
    // ... calculate
    return result;
}

大约有150,000,000个不同的关键对象,以及大约3,000个不同的值。 从输入数(长)到输出(不可变对象)的转换可以通过我的算法以每秒4,000,000次转换的速度计算。 (使用4个线程)

我想缓存150M不同可能输入的映射,以使转换更快,但我发现创建这样的缓存有些困难:

public class Cache {
    private static long[] sortedInputs; // 150M length
    private static Object[] results; // 150M length

    public static Object lookupCachedResult(long input) {
        int index = Arrays.binarySearch(sortedInputs, input);
        return results[index];
    }
}

我试图创建两个长度为150M的数组。第一个数组包含所有可能的输入long,并按数字排序。第二个数组保存对与第一个数组输入对应的索引处的3000个不同的,预先计算的结果对象之一的引用。

要获得缓存结果,我会对第一个数组上的输入数字进行二进制搜索。然后在同一索引的第二个数组中查找缓存的结果。

遗憾的是,这种缓存方法并不比计算结果快。甚至不到一半,每秒只有大约1.5M的查找次数。 (也使用4个线程)

有人会想到在这种情况下更快地缓存结果吗?

我怀疑有一个数据库引擎能够每秒回答超过4,000,000个查询,比如一个普通的工作站。

2 个答案:

答案 0 :(得分:1)

Hashing是这里的方法,但是我会避免使用HashMap,因为它只适用于对象,即每次插入long时都必须构建一个Long,这会减慢它的速度。由于JIT,这个性能问题可能并不重要,但我建议至少尝试以下方法并根据HashMap变体测量性能:

将你的长片保存在一个长度为n的长阵列中> 3000并通过一个非常简单的哈希函数手动进行散列(因此效率很高) index = key % n。由于您事先知道了3000个可能的值,因此您可以凭经验找到数组长度n,这样这个简单的哈希函数不会导致冲突。所以你绕过了重复等等并且具有真正的O(1) - 性能。

其次,我建议你看看像

这样的Java数值库

两者都有本地Lapack和BLAS实现的支持,这些实现通常由非常聪明的人进行高度优化。也许你可以用矩阵/向量代数来表示你的算法,这样它就可以一次计算整个长数组(或者大块数)。

答案 1 :(得分:1)

  

大约有150,000,000个不同的关键对象,以及大约3,000个不同的值。

使用少量值,您应该确保它们被重复使用(除非它们是非常小的对象)。为此,Interner是完美的(虽然你可以自己运行)。

  

我尝试了hashmap和treemap,两次尝试都以outOfMemoryError结束。

这两者都有巨大的内存开销。使用TreeMap并没有多大意义,因为它使用了您已经尝试过的二进制搜索。

至少有三种可用于长对象地图的实现,google用于“原始集合”。这应该使用比两个数组稍多的内存。哈希通常是O(1)(让我们忽略最糟糕的情况,因为它没有理由发生,是吗?)和更好的内存局部性,它会将你的二进制搜索击败(*)20倍。您的二进制搜索需要log2(150e6),即大约27个步骤,并且平均可能需要两个哈希。这取决于您打包哈希表的紧密程度;这通常是创建时给出的参数。

如果您运行自己的(您很可能不应该),我建议使用大小为1 << 28的数组,即268435456条目,以便您可以使用按位运算进行索引。

(*)这样的预测很难,但我确信这值得尝试。