Есть ли у JVM возможность обнаруживать возможности для распараллеливания?

Java Hotspot может очень хорошо оптимизировать последовательный код. Но я догадывался, что с появлением многоядерных компьютеров может ли информация во время выполнения быть полезной для обнаружения возможностей распараллеливания кода во время выполнения, например, для обнаружения возможности конвейерной обработки программного обеспечения в цикле и тому подобных вещах.

Была ли когда-нибудь интересная работа по этой теме? Или это провал исследования или какая-то проблема, которую очень трудно решить?

13 голосов | спросил pythonic 7 J0000006Europe/Moscow 2012, 13:31:22

1 ответ


0

Я думаю, что текущие гарантии модели памяти Java делают довольно трудным много работы, если есть, автоматическое распараллеливание на уровне компилятора или виртуальной машины. Язык Java не имеет семантики, которая гарантировала бы, что любая структура данных является даже эффективно неизменной, или что любое конкретное утверждение является чистым и не имеет побочных эффектов, поэтому компилятор должен будет выяснить это автоматически для распараллеливания. Некоторые элементарные возможности можно было бы определить в компиляторе, но общий случай был бы оставлен на время выполнения, поскольку динамическая загрузка и связывание могли бы привести к новым мутациям, которых не было во время компиляции.

Рассмотрим следующий код:

for (int i = 0; i < array.length; i++) {
    array[i] = expensiveComputation(array[i]);
}

Было бы тривиально распараллелить, если 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!
}

тогда распараллеливание больше не является тривиальным, даже если expensiveComputation чисто и не меняет своего аргумента, потому что параллельные потоки будет изменять содержимое array, пока другие читают его! Параллелизатор должен выяснить какие части массива expensiveComputation в различных условиях и синхронизировать соответственно.

Возможно, было бы невозможно обнаружить все мутации и побочные эффекты, которые могут происходить, и учесть их при распараллеливании, но это было бы очень Тяжело, наверняка, наверное, неосуществимо на практике. Вот почему распараллеливание и выяснение того, что все по-прежнему работает правильно, являются головной болью программиста в Java.

Функциональные языки (например, Clojure в JVM) являются горячим ответом на эту тему. Чистые функции без побочных эффектов вместе с постоянными («эффективно неизменяемыми») структурами данных потенциально позволяют неявное или почти неявное распараллеливание. Давайте удвоим каждый элемент массива:

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

Это прозрачно из-за 2 вещей:

  1. Функция #(* 2 %) является чистой: она принимает значение и выдает значение, и все. Он ничего не меняет, и его вывод зависит только от аргумента.
  2. Вектор [1 2 3 4 5] является неизменным: независимо от того, кто на него смотрит и когда, он одинаков.

В Java можно создавать чистые функции, но 2) неизменность - вот ахиллесова пята. В Java нет неизменяемых массивов. Чтобы быть педантируемым, nothing является неизменным в 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!
ответил Joonas Pulakka 7 J0000006Europe/Moscow 2012, 14:08:00

Похожие вопросы

Популярные теги

security × 330linux × 316macos × 2827 × 268performance × 244command-line × 241sql-server × 235joomla-3.x × 222java × 189c++ × 186windows × 180cisco × 168bash × 158c# × 142gmail × 139arduino-uno × 139javascript × 134ssh × 133seo × 132mysql × 132