2

我试图了解 JDK 何时会自动矢量化。我有以下一组问题(尽管谷歌搜索、阅读、实验等)。给定一个简单的循环如下:

for(int i=0; size = size(); i < size; i++) {
   a[i] = b[i] * c[i];
   method1();
   // someObject.method2();
   // someHashMap.put(b[i], c[i]);
}
  1. 为什么需要内联方法调用“method1”(出现在循环中)以进行自动矢量化?(我不明白为什么必须这样做......)
  2. 也许这是一个“愚蠢”的问题,但如果“someObject.method2()”未被注释怎么办。(让我们假设method2 是巨大的方法,即很多行)。这也会阻止自动矢量化吗?如果 method2 是一个很小的方法(例如,只有 1 或 2 行等?)
  3. 如果未注释“someHashMap”行怎么办?我们有一个可以在所有 SIMD 中共享的对象/变量这一事实是否也会导致自动矢量化失败?(我看不到它是如何工作的,除非 jdk 在访问“someHashMap”的公共对象/var 时以某种方式自动插入“同步”关键字
  4. 在我看来,“流”接口将解决上面问题#3 中隐含的问题,因为流中的“收集器”逻辑会自动处理合并单个哈希图,因此我们不需要任何“同步”词。(而且总的来说,流式 API 似乎是一个完美的 API,可以让 jdk 自动使用自动矢量化,只要在创建流式代码时没有“外部变量”(即没有副作用)...... jdk /jit 编译器会在使用标准流接口编写代码时自动执行自动向量化?如果没有,这样做是否有意义(可能在未来的 jdk 版本中,或者可能来自其他供应商的 jdk?)
  5. 如果循环体包含很多 if 语句等(很多分支,让我们进一步说每个分支都进行大量计算),这是否意味着 a)自动向量化可能是一个坏主意(就像它对于 GPU 一样) 和 b) jit 编译器足够聪明,可以确定自动向量化是一个坏主意,因此它不会自动向量化?
  6. 我目前正在使用 Oracle jdk8,但是如果使用 jdk9 或 jdk10 等,上面的答案会改变吗?
4

1 回答 1

2

为了回答您的问题 (1),原则上,如果 Java 编译器分析并确定它没有任何会影响自动矢量化的副作用,那么它可以在存在非内联method1()调用的情况下进行优化。特别是,编译器可以证明该方法是“const”的(没有副作用,也没有从全局内存中读取),这通常可以在调用站点进行许多优化而无需内联。它也可能证明更受限制的属性,例如不读取或写入某种类型的数组,这也足以在这种情况下允许自动向量化继续进行。method1()

然而,在实践中,我不知道今天有任何 Java 编译器可以进行这种优化。如果相信这个答案,在 Hotspot 中:“[非内联] 方法调用对于 JIT 编译器通常是不透明的。” 大多数 Java 编译器都以一种或另一种方式基于 Hotspot,所以我不希望有一个复杂的 Java 编译器可以在 Hotspot 做不到的情况下做到这一点。

这个答案还涵盖了为什么这种过程间分析 (IPA) 可能既困难又不是特别有用的一些原因。特别是,可以证明非平凡事物的方法通常足够小,以至于它们无论如何都会内联。我不确定我是否完全同意:也有人会争辩说 Java 内联的积极性部分是因为它不执行 IPA,所以强大的 IPA 可能会开启减少内联的能力,从而减少运行时代码占用和 JIT 时间。

您在 (2) 或 (3) 中询问的其他方法变体不会改变任何东西:编译器仍然需要 IPA 确实允许它进行矢量化,并且据我所知 Java 编译器没有它。

(4) 和 (5) 似乎应该作为完全独立的问题提出。

关于 (6) 我认为它没有改变,但对于 OpenJDK 热点邮件列表来说这是一个很好的问题:我认为你会得到一个很好的答案。

最后,值得注意的是,即使在没有 IPA 并且对 不了解的情况下method1(),编译器也可以优化 上的数学ab并且c如果它可以证明它们都没有逃脱。不过,总的来说,这似乎毫无用处:这意味着所有这些变量都将在此函数中分配(或某些函数内联到此函数中),而我想在大多数现实情况下,至少传入三个变量中的一个由来电者。

于 2018-09-03T23:30:53.550 回答