0

我想扫描大量文本并计算词频(n-gram 频率实际上适用于那些熟悉 NLP/IR 的人)。我为此使用了 Java HashMap。所以会发生什么是我逐行处理文本。对于每一行,我提取单词,对于每个单词,我更新哈希图中的相应频率。

问题是这个过程变得越来越慢。例如,它首先处理大约 100k 行/秒 - 性能立即开始下降。在大约 2800 万行之后,性能已经下降到 16k 行/秒——当然还在不断下降。

首先想到的是,这是由于 hashmap 中的条目过多造成的,这导致每次 put 和 get 每次都变慢。所以我尝试的是在任何时候只在哈希图中保留最频繁的条目(比如 100k)。这是通过使用将频率映射到单词的第二个映射来完成的(如这里:Automatically sorted by values map in Java

一般来说,这执行得更快。(虽然开始时为 56,000 行/秒,但当达到 2800 万行时,性能仅下降到 36.5k 行/秒)。然而,这也一直在以更慢的速度下降——但事实仍然是,它一直在下降。

当哈希图的大小保持不变时,您是否有任何可能的解释为什么会发生这种情况?您认为这与垃圾收集器有关吗?意思是,我不断向/从哈希映射中放置和删除对象的事实会碎片化内存或其他东西?还是可能是散列函数问题?由于我使用的是字符串,因此散列函数是 Java 对字符串的默认散列函数。

这是执行上述任务的代码部分:

http://pastebin.com/P8S6Sj86

注意:我是一名 Java 新手,因此您的答案中的任何详细说明都非常受欢迎

4

2 回答 2

3

我建议使用 Java VisualVM 进行一些分析。这是 Java 自带的 - 转到命令行并键入 jvisualvm 来运行它。这可以很容易地查看内存流失是否是您的问题,或者特定类型的对象是否被创建了数十万次。

如果你把你的代码分解成几个方法,你也能分辨出哪些方法运行时间太长。

我确实注意到您在内部循环中不必要地创建了许多对象。这当然无助于提高性能,尽管它可能不是罪魁祸首。

例如:

float avg = new Float(sumItems) / new Float (freqMap.size());

应该只是

float avg = (float)sumItems / freqMap.size();

您的另一段代码也可能很麻烦:

System.out.println(numItems + " items counted");

根据您的操作系统或 IDE,将 100,000 行写入控制台需要大量时间。相反,只需为每 1000 个项目编写一个进度更新。

于 2011-09-19T18:35:16.583 回答
1

建议:

尝试为您存储在哈希图中的对象实现自定义 hashCode 方法。以下是一些链接:

Java HashMap 性能优化/替代

http://www.ibm.com/developerworks/java/library/j-jtp05273/index.html

http://www.javamex.com/tutorials/collections/hash_function_guidelines.shtml

在 HashMap 中使用 String 键的坏主意?

于 2011-09-19T18:36:38.660 回答