微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么 Hotspot JIT 不为长计数器执行循环展开?

如何解决为什么 Hotspot JIT 不为长计数器执行循环展开?

我刚刚阅读了 Java 杂志文章 Loop Unrolling。作者在那里演示了带有 for 计数器的简单 int 循环是使用循环展开优化编译的:

private long intStride1()
{
    long sum = 0;
    for (int i = 0; i < MAX; i += 1)
    {
        sum += data[i];
    }
    return sum;
}

然而,他们随后通过将计数器类型切换为 long 来表明一切都发生了变化:

private long longStride1()
{
    long sum = 0;
    for (long l = 0; l < MAX; L++)
    {
        sum += data[(int) l];
    }
    return sum;
}

这会通过以下方式更改输出程序集:

  1. 介绍安全点
  2. 未执行展开

这会显着降低吞吐量性能

为什么 64 位 HotSpot VM 不为 long 计数器执行循环展开?为什么第二种情况需要安全点,而第一种情况不需要?

解决方法

自 JDK 16 起,HotSpot JVM 支持循环展开和对具有 64 位计数器的循环进行其他优化。

JDK-8223051 的说明回答了您的两个问题:

许多核心循环转换适用于计数循环,即那些 带有计算出的行程计数。转换包括展开、 迭代范围拆分(数组 RCE)和条带挖掘(JDK-8186027)。 优化器执行许多复杂的模式匹配来检测和 变换计数循环。

这些模式匹配和转换中的大部分或全部适用于 具有 32 位控制变量和算术的循环。这是有道理的 只要批量操作只适用于 Java 数组,因为那些 数组只能跨越 31 位索引范围。更新的 API 用于更大的 大容量数据块将引入 64 位索引,例如巴拿马的 本机数组和(可能)范围扩展的字节缓冲区。在下面 引擎盖,Unsafe API 通常使用 64 位地址和地址 算术。处理此类数据结构的循环自然使用 64 位值,作为直接 Java 长整型,或作为包装游标 具有递增长组件的结构(巴拿马指针)。

需要有一个故事来转换这种长时间运行的循环。 这个 RFE 是对那个故事的请求。

一个复杂的因素是有时计数循环没有 安全点,假设最大可能的迭代 (跨越 32 位动态范围)不会导致 JVM 的安全点 由于卡在这样的无响应线程而导致故障的机制 一个计数循环。该假设在 64 位情况下无效。 幸运的是,我们有一个(相对较新的)优化可以解决 这个问题,通过将一个很长的运行循环剥离到一个 具有适当有界行程的循环序列(外循环) 计数。

,

那是因为跟踪可能的整数溢出。将 int 转换为 long 并检查 min/max int 时很容易捕获整数溢出,但在大多数平台上没有比 long 更大的类型。 但由于支持 JDK 16 长循环。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。