Выражение `forall`: параллельный или последовательный расчет?
Мое понимание forall
Заявления о том, что они выполняются параллельно, а for
выписки выполняются последовательно. Действительно, следующий код, кажется, подтверждает это ожидание (т.е. случайная последовательность только для forall
из-за потоков)
for i in 1..5 do writeln( i * 10 );
10
20
30
40
50
forall i in 1..5 do writeln( i * 10 );
10
50
20
30
40
С другой стороны, если я использую forall
(или эквивалент [...]) в правой части как выражение
var A = ( forall i in 1..5 do i * 10 );
var B = [ i in 1..5 ] i * 10;
var X = ( forall a in A do a );
var Y = [ a in A ] a;
var P = ( for i in 1..5 do i * 10 ); // for comparison
writeln( "A = ", A );
writeln( "B = ", B );
writeln( "X = ", X );
writeln( "Y = ", Y );
writeln( "P = ", P );
все результаты становятся одинаковыми (т.е. упорядочены от 10 до 50):
A = 10 20 30 40 50
B = 10 20 30 40 50
X = 10 20 30 40 50
Y = 10 20 30 40 50
P = 10 20 30 40 50
Значит ли это, что forall
выражения в правой части присваивания всегда выполняются последовательно? Если это так, является ли соответствующий [...] также эквивалентным for
выражения в этом контексте?
1 ответ
Хороший вопрос. Вы правы, что с помощью forall
выражения (явные или []
-"в скобках"), таким образом, дает детерминированные результаты, но forall
выражение по-прежнему приводит к параллельному выполнению.
Выражения, подобные вашему, эффективно приводят к итерации с молнией, которая определяется так, что соответствующие итерации будут совпадать. Например, в часовне, кажущиеся операции "целого массива", такие как:
var A, B, C: [1..10] real;
A = B + C;
эквивалентны (повышены) в исполнении молнии forall
цикл:
var A, B, C: [1..10] real;
forall (a, b, c) in zip(A, B, C) do
a = b + c;
Оба эти выражения:
(а) указать параллельное выполнение,
(б) обеспечить, чтобы соответствующие элементы A
, B
, а также C
массивы используются в каждом экземпляре тела цикла (иначе язык не был бы очень полезен).
Принимая один из ваших примеров,
...B = [ i in 1..5 ] i * 10...
эквивалентно:
forall (b, v) in zip(B, [i in 1..5] i * 10) do
b = v;
или же:
forall (b, i) in zip(B, 1..5) do
b = i * 10;
и аналогично для других вариантов, которые вы предоставили.
Это достигается в Chapel с использованием концепции, известной как итераторы лидера-последователя. Первоначально они были описаны в статье, опубликованной на PGAS 2011, под названием " Пользовательские параллельные итераторы на молнии в часовне", слайды которых здесь. Они также описаны в учебнике по часовням на параллельных итераторах.