Лекция 12 (часть 2): Языки программирования семейства PGAS:...

Preview:

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

Recommended