JVM是否能够检测并行化的机会?

pyt*_*nic 13 java parallel-processing multithreading jvm

Java Hotspot可以很好地优化顺序代码.但我猜测随着多核计算机的出现,运行时的信息可以用于检测在运行时并行化代码的机会,例如检测软件流水线是否可能在循环和类似的事情中.

关于这个话题有没有做过任何有趣的工作?或者是研究失败还是一些很难解决的停顿问题?

Joo*_*kka 14

我认为Java内存模型的当前保证使得很难在编译器或VM级别上做很多(如果有的话)自动并行化.Java语言没有语义来保证任何数据结构甚至是有效的不可变的,或者任何特定的语句都是纯粹的并且没有副作用,因此编译器必须自动计算这些以便并行化.在编译器中可以推断出一些基本的机会,但是一般情况将留给运行时,因为动态加载和绑定可能会引入在编译时不存在的新突变.

请考虑以下代码:

for (int i = 0; i < array.length; i++) {
    array[i] = expensiveComputation(array[i]);
}
Run Code Online (Sandbox Code Playgroud)

并行化expensiveComputation是很简单的,如果是纯函数,其输出仅取决于它的参数,如果我们可以保证array在循环期间不会改变(实际上我们正在改变它,设置array[i]=...,但在这种特殊情况下)expensiveComputation(array[i])总是先调用,所以这里没关系 - 假设它array是本地的,而不是从其他地方引用的.

此外,如果我们改变这样的循环:

for (int i = 0; i < array.length; i++) {
    array[i] = expensiveComputation(array, i);
    // expensiveComputation has the whole array at its disposal!
    // It could read or write values anywhere in it!
}
Run Code Online (Sandbox Code Playgroud)

然后并行化不再是微不足道的,即使它expensiveComputation是纯粹的并且不会改变它的参数,因为并行线程将改变array其他人正在读取它的内容!并行化程序必须弄清楚阵列的哪些部分expensiveComputation在各种条件下引用,并相应地进行同步.

也许它不会是完全不可能检测到所有的突变和副作用,可能是怎么回事,并在并行时把这些考虑进去,但它是非常有在努力练习,可以肯定,或许不可行.这就是为什么并行化,并确定一切仍然正常,是程序员在Java中头痛的原因.

函数式语言(例如JVM上的Clojure)是本主题的热门答案.纯粹的无副作用函数与持久("有效不可变")数据结构一起可能允许隐式或几乎隐式并行化.让我们将数组的每个元素加倍:

(map #(* 2 %) [1 2 3 4 5])
(pmap #(* 2 %) [1 2 3 4 5])  ; The same thing, done in parallel.
Run Code Online (Sandbox Code Playgroud)

这是透明的,因为有两件事:

  1. 该函数#(* 2 %)是纯粹的:它接受一个值并给出一个值,就是这样.它不会改变任何东西,它的输出只取决于它的参数.
  2. 向量[1 2 3 4 5]是不可变的:无论是谁在看它,或者什么时候,它都是一样的.

用Java制作纯函数是可能的,但2)不变性是这里的阿基里斯之踵.Java中没有不可变数组.要谨慎,Java中没有任何东西是不可变的,因为甚至final可以使用反射来改变字段.因此,不能保证计算的输出(或输入!)不会因并行化而改变 - >因此自动并行化通常是不可行的.

由于不变性,愚蠢的"倍增元素"示例扩展到任意复杂的处理:

(defn expensivefunction [v x]
  (/ (reduce * v) x))


(let [v [1 2 3 4 5]]
  (map (partial expensivefunction v) v)) ; pmap would work equally well here!
Run Code Online (Sandbox Code Playgroud)