【问题标题】:Java program is extremely slow while the task is pretty simpleJava程序非常慢,而任务非常简单
【发布时间】:2016-03-26 15:09:38
【问题描述】:

我做了一个分类器,你首先用一个包含 A 类文本的文件夹训练分类器,然后用一个包含 B 类文本的文件夹训练分类器。之后,你给分类器一个测试文本,分类器应该猜对A 或 B 类(或 C 或 D ......如果你有更多类别)。

但是,这个程序在某种程度上非常慢,虽然它应该只需要几秒钟,但需要 10 多分钟。我认为故障出在阅读器的某个地方,因为我在步骤之间设置了计时器,这样我就可以找到它。我发现是这两种方法可能导致问题:

public String readText(String path) {
    BufferedReader br;
    String result = "";
    try {

        br = new BufferedReader(new FileReader(path));


        StringBuilder sb = new StringBuilder();
        String line = br.readLine();

        while (line != null) {
            sb.append(line);
            sb.append("\n");
            line = br.readLine();
        }
        result = sb.toString();
        br.close();
    } catch (IOException e) {
        e.printStackTrace();

    }

    return result;
}

这是读取一个路径的文本并给出结果的方法。 (上)

    public void handleTrainDirectory(String folderPath, Category category) {
    File folder = new File(folderPath);
    File[] listOfFiles = folder.listFiles();
    for (File file : listOfFiles){
        if (file.isFile()) {
           handleTrainText(file.getPath(), category);
        }
    }

}

这个东西循环遍历我提供的整个文件夹,并完成每个文本所需的所有事情。 (上)

   public void handleTrainText(String path, Category category) {
    String[] text = handleText(path);
    makeVocabulary(text);
    List<Integer> wordFrequencies = countWordFrequencies(text);
    Text newText = new Text(path, category, wordFrequencies);
    trainTexts.add(newText);
}

这就是 handleTrainText 对每个文本所做的事情。 (上)

为了让这篇文章不会太长,我不会在这里给出所有方法,因为我认为失败是在这些方法中的某个地方。如果您想查看我立即提供的其他方法之一,但现在我尽量使这篇文章尽可能清晰。

仅供参考:我正在为一个目录执行此操作,其中只有 300 个小文本文件,单线程,应该只需要几秒钟。

PS:对不起我糟糕的英语,不是我的母语。

编辑:因为你们不太清楚这里发生了什么是由 handleTrainText 调用的其他方法:

public String[] handleText(String path) {
    String text = readText(path);
    String normalizedText = normalizeText(text);
    return tokenizeText(normalizedText);
}

public String normalizeText(String text) {
    String fstNormalized = Normalizer
            .normalize(text, Normalizer.Form.NFD)
            .replaceAll("[^\\p{ASCII}]", "")
            .toLowerCase()
            .replace("\n", "")
            .replaceAll("[0-9]", "")
            .replaceAll("[!?;:,.%]", "");


    return fstNormalized;
}
public String[] tokenizeText(String normalizedText) {
    return normalizedText.split(" ");
}
public List<String> makeVocabulary(String[] tokens) {
    for (int i = 0; i < tokens.length; i++)
        if (!vocabulary.contains(tokens[i]))
            vocabulary.add(tokens[i]);
    return vocabulary;
}

【问题讨论】:

  • 我的建议是将流直接提供给解析逻辑,无需将所有这些都保存在内存中。加载多个文件后,问题可能是频繁的 GC。
  • 所以如果我理解正确的话,程序的所有功能都用一个大方法完成吗?让 Java 更快?
  • 我没有看完你的代码,但是从不需要一个大方法,因为你总是可以将一个方法拆分成多个 final 方法,这些方法链接起来-互相打电话。
  • Wat 是在 Google 上搜索的“GCs”,但没有逻辑结果 :-)。对不起各位,我是初学者。
  • @TotalCare 我不确定 handleText 的作用。我假设它调用 readText。您在 StringBuilders 中保留文本记忆,而不是直接使用它们来构建您的词汇表。根据您的算法,countWordFrequencies 和 makeVocabulary 需要一段时间。请打印这些在交互之间完成所需的时间。

标签: java performance file-io classification bufferedreader


【解决方案1】:

部分答案:您的makeVocabulary 方法是O(n^2);替换为

public Set<String> makeVocabulary(String[] tokens) {
    return new HashSet<String>(Arrays.asList(tokens));
}

当然,我真的建议您不要一次阅读全部内容,而只是进行一些流处理。请记住,在大多数架构上,缓存未命中大约相当于 16-64 次其他操作...

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-03-26
    • 2019-10-19
    • 2012-05-28
    • 1970-01-01
    • 1970-01-01
    • 2012-10-14
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多