Лекция 12 (часть 2): Языки программирования семейства PGAS: IBM X10
Upcoming SlideShare
Loading in...5
×
 

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

on

  • 501 views

 

Statistics

Views

Total Views
501
Views on SlideShare
501
Embed Views
0

Actions

Likes
0
Downloads
1
Comments
0

0 Embeds 0

No embeds

Accessibility

Categories

Upload Details

Uploaded via as Adobe PDF

Usage Rights

CC Attribution License

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
    Processing…
Post Comment
Edit your comment

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

  • Лекция 12 (часть 2): Язык параллельного программирования IBM X10 Курносов Михаил Георгиевич к.т.н. доцент Кафедры вычислительных систем Сибирский государственный университет телекоммуникаций и информатики http://www.mkurnosov.net
  • Программные модели ВС Вычислительная система с общей памятью (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
  • Программные модели ВС P P P M Shared memory model P P P M M M Distributed memory model P P P Process/thread/task Memory (address space) Message passing Memory access PGAS 3
  • IBM X10  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 4
  • IBM X10  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 5
  • Asynchronous PGAS  IBM X10 реализует расширенную версию модели PGAS – Asynchronous Partitioned Global Address Space (APGAS)  APGAS = PGAS + динамическое управление параллельными задачами  Модель PGAS расширена двумя конструкциями: o place o async 6
  • Основные понятия IBM X10  Place (область) – непрерывная часть адресного пространства и множество потоков (activities) работающих с ним (place можно представить как виртуальный мультипроцессор с общей памятью – SMP-система)  Activity – поток (задача), выполняющийся в рамках области (place, создаётся конструкциями async и at) В общем случае n потоков (activities) может быть привязано к m областям (places) 7
  • IBM X10: Hello, World 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); } } } } 8
  • IBM X10: Hello, World $ 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 9
  • IBM X10  Выполнение программы начинается со статического метода 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> 10
  • Типы данных IBM X10 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; } } 11
  • Типы данных IBM X10  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; 12
  • Динамическое создание функций val r = new Random(); val rand = () => r.nextDouble(); val inCircle = countPoints(N, rand); 13
  • Классы IBM X10 class Counter { var value: Int; public def this() { value = 0; }  this – указатель на текущий объект  this() – конструктор класса public def this(value: Int) { this.value = value; } public def inc() { value++; } public def getCount(): Int { return value; } } 14
  • Классы IBM X10 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 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 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) 17
  • Классы IBM X10  Абстрактные классы (все методы абстрактные) 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) { ... } } 18
  • Исключительные ситуации IBM X10 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 { throw new x10.io.FileNotFoundException( “Bad path ” + path); } 20
  • Массивы IBM X10  Массив локальный для одной области (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 21
  • Массивы IBM X10 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)); } } } 22
  • Массивы IBM X10 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; 23
  • Point & Range 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); 24
  • Массивы IBM X10 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)); } } 25
  • Массивы IBM X10 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; } 26
  • Places (области) & Activities (потоки)  Метод 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() 27
  • async  Порождение нового потока (activity) в текущей области async S  Управление немедленно возвращается вызвавшему потоку  В пределах блока S можно ссылаться на val-переменные операторного блока из которого вызвана директива async def start() { val a = new Calc(); async a.run(); } 28
  • finish  Директива 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
  • async + finish public static def main(args: Array[String](1)) { finish { async { for (i: Int = 0; i < 2; i++) { async { /*...*/ } } finish async { /*...*/ } } } } 30
  • at  Директива at позволяет явно задать существующую область P (place), в которой следует выполнить блок S at(P) S  Новый поток в области P не создается, туда передается выполнение текущего потока. После завершения блока S выполнение потока возвращается в начальную область  Операция at требует копирования в область P данных используемых блоком S – в области P создаются их локальные копии  Поля классов со спецификатором transient не копируются командой at, им присваиваются значения по умолчанию 31
  • at 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); } 1 [1,4,3] 0 [1,2,3] Place 0 o a = [1, 2, 3] o at (1) {…} o println(a) Place 1 Copy a o a(1) = 4 o println(a) 32
  • at /* Копирует поле f из a в b */ def copyRemoteFields(a, b) { at (b.home) b.f = at (a.home) a.f; } /* Выполняет метод удаленного объекта */ def invoke(obj, arg) { at (obj.home) { obj().fun(arg); } } 33
  • Спецификатор 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); } } } a=1 b=2 a=1 b=0 34
  • Запуск корневого потока 1. Runtime-система отыскивает контейнер C (класс) со статическим методом main 2. Формирует из аргументов командной строки одномерный массив s строк и запускает корневой поток следующим образом finish async at (Place.FIRST_PLACE) { C.main(s); } 35
  • Atomic blocks  Директива atomic создает в текущей области критическую секцию S atomic S  Пока блок S не завершиться в него не войдут другие потоки области  В пределах S нельзя: порождать потоки, использовать at def add(x: T) { atomic { this.list.add(x); this.size++; } } 36
  • Atomic blocks  Директива atomic создает в текущей области критическую секцию S atomic S  Пока блок S не завершиться в него не войдут другие потоки области  В пределах S нельзя: порождать потоки, использовать at atomic def add(x: T) { this.list.add(x); this.size++; } 37
  • Conditional atomic block  Директива 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; } 38
  • Conditional atomic block 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  Распределенный массив (Distributed array) – это массив, распределенный между несколькими областями (places)  X10.array.DistArray[T]  Распределение (distribution, X10.array.Dist) задает распределение точек региона (region) по областям (places) 40
  • Distributions 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 41
  • DistArray 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 localA globalA Place 1 Place 2 Place 3 0 0 0 0 123 456 789 10 11 12 42
  • Проход по распределенному массиву 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); } } } 43
  • Проход по распределенному массиву public static def main(Array[String](1)) { /* ... */ var sum: Int = 0; for (p in lSum.dist.places()) { sum += (at (p) localSum(p.id)); } } 44
  • ateach  Директива ateach позволяет выполнить блок S в каждой области ateach (p in D) S  Действия ateach эквивалентны следующему фрагменту for (place in D.places()) { async at (place) { for (p in D | here) { S(p); } } } 45
  • IBM X10 Backends  Java backend (x10c) Один процесс – все области (places) в одной виртуальной машине JVM (Java 5)  C++ backend (x10c++) Один процесс на SMP-узел (1 область на SMP-узел) 46
  • Пример ArraySum (serial version) 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) 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) public class ArraySum { /* ... */ def sum(a: var s: for (i s return } Array[Int](1), l: Int, h: Int): Int { Int = 0; in l..(h - 1)) += a(i); Activity 1 Activity 2 Activity 0 s; -3 2 2 0 4 3 4 6 7 8 9 2 4 1 5 4 5 6 5 4 5 6 def sum(nthreads: Int) { chunk 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
  • Пример ArraySum (parallel version 1) 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); } } Single place version 50
  • Пример ArraySum (parallel version 2) 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; } data: Thread Thread Thread Thread Thread Thread … } 0 1 2 0 1 2 51
  • Числа Фибоначчи (serial version) 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) 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
  • Вычисление числа Pi public class Pi { public static def main(args:Array[String](1)) { val N = 100000; val init = (i: Point) => { y val r = new Random(); 1 var m: Double = 0.0D; 1 x for (c in 1..N) { val x = r.nextDouble(); val y = r.nextDouble(); 4𝑚 if (x * x + y * y <= 1.0) 𝜋≈ 𝑛 m++; } m }; 54
  • Вычисление числа Pi 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); } } 55