大规模文件共现分析

时间:2014-01-13 11:21:27

标签: java multithreading performance processing-efficiency

我有大约1000个文件。每个文件包含约20,000个文档。我还有一个大约1,000,000个单词的列表。

我想计算每个单词与其他单词出现的时间。因此,有一个大小为1M X 1M的稀疏矩阵。

为了加快计算速度,我通过执行以下操作分别处理每个文件:

1-我机器中的每个核心都在处理单个文件并输出以下格式的文件

WordId1 WordId2 Frequency 

2-完成每个文件后,我将1000文件合并为一个文件。

这是我目前的方法,但这需要很长时间才能完成,我认为应该有更有效的方法来做到这一点,所以欢迎你的意见。

3 个答案:

答案 0 :(得分:2)

我猜你可以通过仔细处理细节来获得合理的表现。有问题的部分看起来是记忆。有足够的内存,你可以避免写出和合并。

处理单个文档时,如果相应的单词存在,则可以在设置每个位时将其转换为BitSet

您的关系是对称的,因此我希望您只将(a, b, count)存储为a < b

你需要Multiset<Pair<String, String>>这样的东西进行计数,但是有更多的内存保存结构。您的文字已编号,因此每个文字都可以用int表示,一对可以用long表示。也许像LongIntHashMap这样的东西也可以。您需要并发,因此您可以使用atomics作为条目,也可以将地图划分为N个部分(通过某些散列,N大于核心数)并同步。在AtomicIntegerArray之上构建一些东西应该很容易。

你没有说你的结果是否有可能适合记忆,但如果是这样的话,可能会导致巨大的加速。

要求的解释

字符串编号为0到100万,符合int。两个这样的数字一起放在long中,可以用作TLongIntHashMap的关键字。对于每个文档,您标识所有相关的字符串对,获取相应的long并增加TLongIntHashMap中的值。

这里,只需要在锁定下完成增量。由于这种锁定会阻碍并发性,我建议使用多个映射,每个映射都有自己的锁。可以对递增进行分组,以便可以使用单个锁执行多个操作。

更好的解决方案可能是每个单词使用一个TIntIntHashMap。想象一下,您将文档中找到的所有单词(表示为int s)放入集合中。然后你可以像这样循环

for (int w1 : words) {
    getLock(w1).lock();
    TIntIntHashMap map = getMap(w1);
    for (int w2 : words) {
        if (isLess(w1, w2) map.increment(w2);
    }
    getLock(w1).unlock();
}

此处,isLess是一种任意的反对称反自反关系,用于避免同时存储(a, b)(b, a)。虽然只是w1 < w2会这样做,但它会导致相当不平衡的值(getMap(0)可能很大而getMap(1000000)会为空。使用((w1 - w2) ^ ((w1 + w2) << 31)) < 0应该这样做。

答案 1 :(得分:2)

我做了一些像这样的统计,我把工作分成了两步

步骤1:多线程计数:计算每对的分区ID并直接输出相关的分区文件(partition_id =(md5 of pair)/ partition_count,分区进程是关键点),(我试过hash_map统计数据(当大小大于thread_hold时,将map_data输出到文件,这节省了大量的磁盘空间,并且我把输出文件放在不同的磁盘上,这使得进程加速了很多)

步骤2:多线程合并:合并计数输出步骤1使用map(此过程在内存中完成,如果内存不足,请选择较大的partition_count)

注意:mapreduce是一个简单的工作,step1是map短语,step2是减少短语,关键过程是对应于hadoop减少过程之前的分区部分的分区过程

答案 2 :(得分:1)

你在这里达到复杂性的基本规律。您正在尝试为大量单词处理大量文档,并从中生成大量数据集。

它总是很慢。

有些事情可能加快速度:

  1. 忘掉一百万字的清单。相反,只要接受文本中找到的任何单词,您就可以随后过滤它们。如果您确实需要对列表进行过滤,请确保列表采用适当的格式(例如HashSet),以便您快速检查。

  2. 这种事情更可能是IO绑定而不是CPU绑定,所以尝试在快速SSD驱动器上运行它 - 或者如果文件足够小,则设置一个RAM磁盘并从中运行它。做一些监控以确定瓶颈的位置。

  3. 对每组文件的处理就像你已经确定的非常平行一样,所以你可以看看它不只是在多个核心而是在多台机器上传播它。

  4. 要尝试的东西(数据库的开销实际上可能会让它变慢): 而不是在最后进行合并,您可以编译结果以在内存中一起处理一个文档。完成处理后,将一个批量插入数据库。然后,数据库将允许您使用sum()等动态查询结果,以查找每个单词组合的总计。这实际上为您提供了比平面文件更灵活/更有用的结果,并避免了单独的合并步骤。