java - jvm如何优化循环代码?

标签 java string algorithm optimization jvm

有一种方法可以从文本中搜索子串(使用暴力算法,请忽略空指针)

public static int forceSearch(String text, String pattern) {
    int patternLength = pattern.length();
    int textLength = text.length();

    for (int i = 0, n = textLength - patternLength; i <= n; i++) {
        int j = 0;
        for (; j < patternLength && text.charAt(i + j) == pattern.charAt(j); j++) {
            ;
        }
        if (j == patternLength) {
            return i;
        }
    }
    return -1;
}

奇怪!使用相同的算法,但下面的代码更快!!!

public static int forceSearch(String text, String pattern) {
    int patternLength = pattern.length();
    int textLength = text.length();

    char first = pattern.charAt(0);
    for (int i = 0, n = textLength - patternLength; i <= n; i++) {
        if (text.charAt(i) != first) {
            while (++i <= n && text.charAt(i) != first)
                ;
        }

        int j = 0;
        for (; j < patternLength && text.charAt(i + j) == pattern.charAt(j); j++) {
            ;
        }
        if (j == patternLength) {
            return i;
        }
    }
    return -1;
}

我发现如果我用 jvm 运行第二个代码明显比第一个快。但是,当我用 c 编写并运行时,这两个函数花费的时间几乎相同。所以我认为原因是jvm优化了循环代码

if (text.charAt(i) != first) {
    while (++i <= max && text.charAt(i) != first)
        ;
}

我说的对吗?如果我是对的,我们应该如何使用jvm优化策略来 优化我们的代码?

希望有人帮忙,谢谢:)

最佳答案

如果您真的想深入了解这个问题,您可能需要指示 JVM 打印程序集。根据我的经验,对循环进行微小的调整可能会导致令人惊讶的性能差异。但这不一定是由于循环本身的优化。

有很多因素会影响代码的 JIT 编译方式。 例如,调整方法的大小会影响您的内联树,这可能意味着更好或更差的性能,具体取决于您的调用堆栈的外观。如果一个方法被内联到调用堆栈的更深处,它可能会阻止嵌套的调用站点被内联到同一个框架中。如果那些嵌套的调用站点特别“热门”,那么增加的调用开销可能会很大。我并不是说那是这里的原因;我只是指出,有许多阈值可以控制 JIT 如何安排您的代码,并且性能差异的原因并不总是很明显。

将 JMH 用于基准测试的一个好处是,您可以通过显式设置内联边界来减少此类更改的影响。但是您可以使用 -XX:CompileCommand 手动实现相同的效果。

当然,还有缓存友好性等其他因素需要更直观的分析。鉴于您的基准测试可能 没有特别深的调用树,我倾向于倾向于将缓存行为作为更可能的解释。我猜你的第二个版本性能更好,因为你的比较(pattern 的第一 block )通常在你的 L1 缓存中,而你的第一个版本会导致更多的缓存流失。如果您的输入很长(听起来很长),那么这可能是一种解释。如果不是,原因可能更微妙,例如,您的第一个版本可能是“欺骗”CPU 使用更积极的缓存预取,但实际上损害性能(至少对于输入你正在做基准测试)。无论如何,如果要解释缓存行为,那么我想知道为什么您在 C 版本中看不到类似的差异。您在编译 C 版本时使用了哪些优化标志?

消除死代码也可能是一个因素。我必须看看您的输入是什么,但您的手动优化版本可能会导致某些指令 block 在检测解释阶段永远不会被命中,从而导致 JIT 将它们排除在最终组装之外。

我重申:如果您想查明真相,您需要强制 JIT 转储每个版本的程序集(并与 C 版本进行比较)。

关于java - jvm如何优化循环代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/50185660/

相关文章:

java - android - 如何将 java swing 转换为 android?

java - 如何使用 egit/eclipse 配置工作区,以便我的更改转到 fork 上的分支

algorithm - 使用旋转卡尺测量凸多边形的直径

java - 我如何使用 Hibernate/JPA 在插入/更新/删除之前告诉数据库用户是谁?

java - 为什么添加到框架的第一个面板会消失?

python - 格式正确的乘法表

ios - 检查字符串中是否有违禁单词

java - 如何从列表中选择一个随机字符串

algorithm - 同步两个有序列表

arrays - 最大利润股市算法