Upload
mikhail-kurnosov
View
540
Download
1
Embed Size (px)
Citation preview
Лекция 12 (часть 2):
Язык параллельного
программирования IBM X10
Курносов Михаил Георгиевич
к.т.н. доцент Кафедры вычислительных систем
Сибирский государственный университет
телекоммуникаций и информатики
http://www.mkurnosov.net
Программные модели ВС
2
Вычислительная
система с общей
памятью
(SMP, NUMA, GPU)
Вычислительная
система с
распределенной
памятью
(Cluster, MPP)
Shared
memory model
OpenMP,
Intel Cilk Plus,
Intel TBB, CUDA,
OpenCL, OpenACC
Intel Cluster OpenMP
Distributed
memory model MPI, PVM MPI, PVM
Partitioned
global address
space model
IBM X10, Cray Chapel,
Unified Parallel C,
OpenSHMEM
IBM X10, Cray Chapel,
Unified Parallel C,
OpenSHMEM 2
Программные модели ВС
33
P
M
P
M
P
M
P
M
P P
Shared memory model Distributed memory model
P P P
PGAS
Process/thread/task
Memory (address space)
Message passing
Memory access
IBM X10
44
IBM X10 – это объектно-ориентированный язык
параллельного программирования, реализующий
модель вычислительной системы с разделённым
глобальным адресным пространством
(Partitioned Global Address Space – PGAS)
Синтаксис IBM X10 основан на Java
Разработка начата в исследовательском центре
IBM им. Т. Уотсона (Thomas J. Watson Research Center)
Программа IBM PERCS (Productive, Easy-to-use, Reliable
Computing System): Blue Watters, X10, POWER7, GPFS
http://x10-lang.org
IBM X10
55
IBM X10 – это объектно-ориентированный язык
параллельного программирования, реализующий
модель вычислительной системы с разделённым
глобальным адресным пространством
(Partitioned Global Address Space – PGAS)
2013 – IBM X10 2.4.0
…
2009 – IBM X10 2.0
2006 – IBM X10 1.0
Лицензия: Eclipse Public License v1.0
ОС: IBM AIX (Power), IBM Blue Gene/P, GNU/Linux,
Apple Mac OS X, Microsoft Windows
Asynchronous PGAS
66
IBM X10 реализует расширенную версию модели PGAS –
Asynchronous Partitioned Global Address Space (APGAS)
APGAS = PGAS + динамическое управление
параллельными задачами
Модель PGAS расширена двумя конструкциями:
o place
o async
Основные понятия IBM X10
77
Place (область) – непрерывная часть адресного
пространства и множество потоков (activities) работающих
с ним (place можно представить как виртуальный
мультипроцессор с общей памятью – SMP-система)
Activity – поток (задача), выполняющийся в рамках
области (place, создаётся конструкциями async и at)
В общем случае n потоков (activities) может быть привязано
к m областям (places)
IBM X10: Hello, World
88
import x10.io.Console;
class HelloWorld {
public static def main(Array[String](1)) {
finish for (p in Place.places()) {
async at (p) {
Console.OUT.println(
"Hello, World: place " + p.id);
}
}
}
}
IBM X10: Hello, World
99
$ x10c++ -o HelloWorld ./HelloWorld.x10
$ export X10_NPLACES=4
$ ./HelloWorld
Hello, World: place 1
Hello, World: place 2
Hello, World: place 3
Hello, World: place 0
IBM X10
1010
Выполнение программы начинается со статического метода
main (присутствует только у одного класса)
Спецификаторы доступа как в Java:
public, private, protected, static
Объявление переменных (Java’s non-final)
var <name>: type
Обобщенные типы (Generic type):
Array[String], Array[Int], Array[Double]
Одномерный массив из 100 элементов:
var values: Array[Double](100)
Создание неизменяемого объекта (immutable, Java’s final)
val <name> = <value>
Типы данных IBM X10
1111
public class Test
{
public static def main(args: Array[String](1)) {
val w = 5;
val x = w as Double;
val y = 3.0;
val z = y as Int;
val d1 = (Math.log(8.0) /
Math.log(2.0)) as Int;
val d2 = Math.pow(2, d1) as Int;
}
}
Типы данных IBM X10
1212
Byte, Short, Int, Long (8-bit, 16-bit, 32-bit, 64-bit)
UByte, UShort, UInt, ULong (8-bit, 16-bit, 32-bit, 64-bit)
Float, Double (IEEE single & double prec.)
Char (16-bit Unicode)
String, File, …
Function (ссылка на функцию):
(arg1Type, arg2Type, ...) => returnType
var funSum: (Array[Double](1)) => Double;
Приведение типов – оператор as:
var i: Int = 100;
var j: Long = i as Long;
Динамическое создание функций
1313
val r = new Random();
val rand = () => r.nextDouble();
val inCircle = countPoints(N, rand);
Классы IBM X10
14
class Counter {
var value: Int;
public def this() {
value = 0;
}
public def this(value: Int) {
this.value = value;
}
public def inc() {
value++;
}
public def getCount(): Int {
return value;
}
}14
this – указатель
на текущий объект
this() – конструктор
класса
Классы IBM X10
15
public class Driver {
public static def main(args: Array[String](1)) {
val c1 = new Counter();
val c2 = new Counter(12);
for (var i: Int = 0; i < 10; i++) {
c1.inc();
}
Console.OUT.println("c1 = " +
c1.getCount());
Console.OUT.println("c2 = " +
c2.getCount());
}
}
15
Классы IBM X10
16
class Team {
public static val MEMBERS_MAX = 1024;
public val name: String;
private var members: Array[String](1);
public def this() {
this(“Team”, MEMBERS_MAX);
}
public def this(name: String, size: Int) {
this.name = name;
members = new Array[String](1..size);
}
public def addMember(member: String): Int { }
protected def showMembers() { }
static def resize(team: Team, size: Int): Team { }
}
16
Наследование классов IBM X10
1717
class SoccerTeam extends Team {
public var leader: String;
public var goalkeeper: String;
public def this() {
super("SoccerTeam", 11);
}
public def this(name: String, size: Int,
leader: String,
goalkeeper: String)
{
super("SoccerTeam", 11);
this.leader = leader;
this.goalkeeper = goalkeeper;
}
}Реализовано одиночное наследование
классов (single inheritance)
Классы IBM X10
1818
Абстрактные классы (все методы абстрактные)
public abstract class Team { ... }
public abstract def getTeamName(): String;
Интерфейсы
public interface Vector[T] {
def add(item: T);
def delete(item: T);
def getByIndex(index: Int): T;
public static VERSION = “1.0.4”;
}
class VectorString implements Vector[String] {
public def add(item: String) { ... }
}
Исключительные ситуации IBM X10
19
public class ReadDBL2 {
public static def main(args: Array[String](1)) {
val inputPath = args(0);
val In = new File(inputPath);
var r: FileReader = null;
try {
r = new FileReader(In);
while(true)
Console.OUT.println(r.readDouble());
} catch(eof: x10.io.EOFException) {
Console.OUT.println("Done!");
} catch(ioe: x10.io.IOException) {
Console.ERR.println(ioe);
} finally {
if (r != null) r.close();
}
}
} 19
Исключительные ситуации IBM X10
20
{
throw new x10.io.FileNotFoundException(
“Bad path ” + path);
}
20
Массивы IBM X10
2121
Массив локальный для одной области (Place)
x10.array.Array
Индекс в n-мерном массиве
x10.array.Point
Множество точек (индексов)
x10.array.Region
Конструктор класса Array
Array[T](R, init)
1) R - Region (1..ArraySize)
2) Функция инициализации элементов: (Point) => 0
Свойства массива: a.region, a.size, a.rank
Массивы IBM X10
2222
public class Driver {
public static def main(args: Array[String](1)) {
val size = 100;
val region = 1..size; /* IntRange */
val a = new Array[Int](region,
(Point) => 0);
for ([i] in a) {
a(i) = i;
Console.OUT.println(a(i));
}
}
}
Массивы IBM X10
2323
val A1 = new Array[Int](1..10, 0);
A1(4) = A1(4) + 1;
val A4 = new Array[Int]((1..2) * (1..3) * (1..4) *
(1..5), 0);
A4(2, 3, 4, 5) = A4(1, 1, 1, 1) + 1;
Point & Range
2424
Point p = [1, 2, 3, 4, 5];
Console.OUT.println(p.rank); /* p.rank = 5 */
Console.OUT.println(p.get(2)); /* p.get(2) = 3 */
val r1 = 1..100;
val r2 = r1 as Region(1);
val r3 = (0..99) * (-1..20);
Массивы IBM X10
2525
public class Driver {
public static def main(args: Array[String](1)) {
val x = new Array[String](1..1000, "oh!");
/* y = [11, 22, 33] */
val y = new Array[Int](1..3,
(i: Point(1)) =>
11 * i(0)
/* Таблица умножения */
val z = new Array[Int]((0..9) * (0..9),
(p: Point(2)) =>
p(0) * p(1));
}
}
Массивы IBM X10
2626
static def sumArray(a: Array[Int], b: Array[Int])
{ src.region == dest.region } = {
for (p in src.region)
dest(p) += src(p);
}
public static def sum(vec: Array[Int]): Int {
var s: Int = 0;
for (p in vec)
s += vec(p);
return s;
}
Places (области) & Activities (потоки)
2727
Метод main выполняется в потоке области 0 (place 0) –
root activity
Количество областей фиксировано и задается при запуске
программы (#export X10_NPLACES=N)
o Place.places – массив областей
o Place.places()(0) – доступ к области 0
o Place.id – номер области
o Place.MAX_PLACES
o Place.FIRST_PLACE = 0, Place.LAST_PLACE
o here – ссылка на текущую область
o Place.next(), Place.prev()
async
2828
Порождение нового потока (activity) в текущей областиasync S
Управление немедленно возвращается вызвавшему
потоку
В пределах блока S можно ссылаться на val-переменные
операторного блока из которого вызвана директива async
def start() {
val a = new Calc();
async a.run();
}
finish
29
Директива finish ожидает завершения дочерних потоков,
порожденных в блоке S
finish S
В главном потоке (корневом, main) неявно выполняется
синхронизация (finish)
def start(data) {
val a = new Calc();
val b = new Calc();
finish {
async a.run();
async b.run();
}
}29
public static def main(args: Array[String](1)) {
finish {
async {
for (i: Int = 0; i < 2; i++) {
async { /*...*/ }
}
finish async { /*...*/ }
}
}
}
async + finish
3030
at
31
Директива at позволяет явно задать существующую
область P (place), в которой следует выполнить блок S
at(P) S
Новый поток в области P не создается, туда передается
выполнение текущего потока. После завершения блока S
выполнение потока возвращается в начальную область
Операция at требует копирования в область P данных
используемых блоком S – в области P создаются их
локальные копии
Поля классов со спецификатором transient не копируются
командой at, им присваиваются значения по умолчанию
31
at
32
public static def main(Array[String](1)) {
val a = [1, 2, 3];
at(here.next()) {
a(1) = 4;
Console.OUT.println(here.id + " " + a);
}
Console.OUT.println(here.id + " " + a);
}
32
1 [1,4,3]
0 [1,2,3]Place 0
o a = [1, 2, 3]
o at (1) {…}
o println(a)
Place 1
o a(1) = 4
o println(a)
Copy a
at
33
/* Копирует поле f из a в b */
def copyRemoteFields(a, b) {
at (b.home) b.f =
at (a.home) a.f;
}
33
/* Выполняет метод удаленного объекта */
def invoke(obj, arg) {
at (obj.home) {
obj().fun(arg);
}
}
Спецификатор transient полей классов
class Trans {
public val a: Int = 1;
transient public val b: Int = 2;
public def test() {
Console.OUT.println("a=" + a + " b=" + b);
at(here) {
Console.OUT.println("a=" + a +
" b=" + b);
}
}
}
34
a=1 b=2
a=1 b=034
Запуск корневого потока
3535
1. Runtime-система отыскивает контейнер C (класс)
со статическим методом main
2. Формирует из аргументов командной строки
одномерный массив s строк и запускает корневой
поток следующим образом
finish async at (Place.FIRST_PLACE) {
C.main(s);
}
Atomic blocks
3636
Директива atomic создает в текущей области критическую
секцию Satomic S
Пока блок S не завершиться в него не войдут другие
потоки области
В пределах S нельзя: порождать потоки, использовать at
def add(x: T) {
atomic {
this.list.add(x);
this.size++;
}
}
Atomic blocks
3737
Директива atomic создает в текущей области критическую
секцию Satomic S
Пока блок S не завершиться в него не войдут другие
потоки области
В пределах S нельзя: порождать потоки, использовать at
atomic def add(x: T) {
this.list.add(x);
this.size++;
}
Conditional atomic block
3838
Директива when блокирует выполнение потоков области
и не допускает их входа в критическую секцию S пока
выражение E не примет значение истинаwhen (E) S
Выражение E должно быть атомарным
В пределах S нельзя: порождать потоки, использовать at
def pop(): T {
var res: T;
when (size > 0) {
res = list.removeAt(0);
size--;
}
return res;
}
Conditional atomic block
39
class DataBuffer[T] {
var data: T;
var filled: Boolean;
def this(data: T) {
this.data = data; this.filled = true;
}
public def send(data: T) {
when (!filled) {
this.data = data; this.filled = true;
}
}
public def receive(): T {
when (filled) {
data: T = this.data;
filled = false;
return data;
}
}
} 39
Distributed arrays
4040
Распределенный массив (Distributed array) –
это массив, распределенный между несколькими
областями (places)
X10.array.DistArray[T]
Распределение (distribution, X10.array.Dist) задает
распределение точек региона (region) по областям (places)
Distributions
4141
class Driver {
public static def main(Array[String](1)) {
val R <: Region = 1..100;
val D1 <: Dist = Dist.makeBlock(R);
val D2 <: Dist = Dist.makeConstant(R, here);
}
}
D1 равномерно распределяет регион R по всем областям
(на сколько этом возможно), каждой области назначена
непрерывная последовательность индексов региона
D2 назначает все точки региона R области here
DistArray
4242
public static def main(Array[String](1)) {
val D1 = Dist.makeUnique();
val D2 = Dist.makeBlock(1..12);
val localA: DistArray[Int] =
DistArray.make[Int](D1, ((Point) => 0));
val globalA: DistArray[Int] =
DistArray.make[Int](D2,
(([i]: Point(1)) => i));
}
Place 0
0
1 2 3
Place 1
0
4 5 6
Place 2
0
7 8 9
Place 3
0
10 11 12
localA
globalA
Проход по распределенному массиву
4343
public static def main(Array[String](1)) {
val D1 = Dist.makeUnique();
val D2 = Dist.makeBlock(1..12);
val lSum: DistArray[Int] =
DistArray.make[Int](D1, ((Point) => 0));
val gSum: DistArray[Int] =
DistArray.make[Int](D2, (([i]: Point(1)) => i));
finish {
for (p in gSum.dist.places()) {
async at (p) {
for (localPoint in gSum | here)
lSum(p.id) += gSum(localPoint);
}
}
}
Проход по распределенному массиву
4444
public static def main(Array[String](1)) {
/* ... */
var sum: Int = 0;
for (p in lSum.dist.places()) {
sum += (at (p) localSum(p.id));
}
}
ateach
4545
Директива ateach позволяет выполнить блок S в каждой
областиateach (p in D) S
Действия ateach эквивалентны следующему фрагменту
for (place in D.places()) {
async at (place) {
for (p in D | here) {
S(p);
}
}
}
IBM X10 Backends
4646
Java backend (x10c)
Один процесс – все области (places) в одной виртуальной
машине JVM (Java 5)
C++ backend (x10c++)
Один процесс на SMP-узел (1 область на SMP-узел)
Пример ArraySum (serial version)
47
public class ArraySum {
var sum: Int;
val data: Array[Int](1);
def this(size: Int) {
/* 1-dim array filled with 1 */
data = new Array[Int](size, 1);
sum = 0;
}
def computeSum() {
sum = 0;
for (i in data) {
sum += data(i);
}
}
}47
Пример ArraySum (serial version)
48
public class ArraySum {
/* ... */
public static def main(args: Array[String](1)) {
var size: Int = 10;
if (args.size >= 1)
size = Int.parse(args(0));
val a = new ArraySum(size);
a.computeSum();
Console.OUT.println(“Sum: " + a.sum);
}
}
48
Пример ArraySum (parallel version 1)
49
public class ArraySum {
/* ... */
def sum(a: Array[Int](1), l: Int, h: Int): Int {
var s: Int = 0;
for (i in l..(h - 1))
s += a(i);
return s;
}
def sum(nthreads: Int) {
sum = 0;
val chunk = data.size / nthreads;
finish for (p in 0..(nthreads - 1)) async {
val sumlocal = sum(data, p * chunk,
(p + 1) * chunk);
atomic sum += sumlocal;
}
}
} 49
-3 2 2 0 4 3 4 6 7 8 9 2 4 1 5 4 5 6 5 4 5 6
Activity 0 Activity 1 Activity 2
chunk
Пример ArraySum (parallel version 1)
50
public class ArraySum {
/* ... */
public static def main(args: Array[String](1)) {
var nthreads: Int = 1;
var size: Int = 10;
if (args.size >= 1)
size = Int.parse(args(0));
if (args.size >= 2)
nthreads = Int.parse(args(1));
val a = new ArraySum(size);
a.sum(nthreads);
Console.OUT.println("Result: " + a.sum);
}
}50
Single place version
Пример ArraySum (parallel version 2)
51
public class ArraySum {
/* ... */
def sum_roundrobin(nthreads: Int) {
sum = 0;
finish for (p in 0..(nthreads - 1)) async {
var sumlocal: Int = 0;
for (var i: Int = p; i < data.size;
i += nthreads)
{
sumlocal += data(i);
}
atomic sum += sumlocal;
}
}
}51
Thread
0
Thread
1
Thread
2
Thread
0
Thread
1
Thread
2…data:
Числа Фибоначчи (serial version)
52
class Fib {
static def fib(n: Int): Int {
if (n < 2)
return n;
val x = fib(n - 1);
val y = fib(n - 2);
return x + y;
}
public static def main(args:Array[String](1)) {
val n = fib(30);
Console.OUT.println("Fib = " + n);
}
}
52
Числа Фибоначчи (parallel version)
53
class Fib {
static def fib_parallel(n: Int): Int {
val x: Int;
val y: Int;
if (n < 2) return n;
finish {
async x = fib(n - 1);
y = fib(n - 2);
}
return x + y;
}
public static def main(args:Array[String](1)) {
val n = fib_parallel(30);
Console.OUT.println("Fib = " + n);
}
}53
public class Pi {
public static def main(args:Array[String](1)) {
val N = 100000;
val init = (i: Point) => {
val r = new Random();
var m: Double = 0.0D;
for (c in 1..N) {
val x = r.nextDouble();
val y = r.nextDouble();
if (x * x + y * y <= 1.0)
m++;
}
m
};
Вычисление числа Pi
5454
y
x
1
1
𝜋 ≈4𝑚
𝑛
public class Pi {
public static def main(args:Array[String](1)) {
val N = 100000;
val init = (i: Point) => { /* ...*/ };
val r = DistArray.make[Double](
Dist.makeUnique(), init);
val pi = 4 * r.reduce(
(x: Double, y: Double) =>
x + y, 0.0) /
(N * Place.MAX_PLACES);
Console.OUT.println("Pi = " + pi);
}
}
Вычисление числа Pi
5555