High-Performance Fork/Join - под капотом паралеллизма в ... ·...
Transcript of High-Performance Fork/Join - под капотом паралеллизма в ... ·...
High-Performance Fork/Joinпод капотом паралеллизма в платформе
Алексей Шипилёв[email protected], @shipilev
The following is intended to outline our generalproduct direction. It is intended for informationpurposes only, and may not be incorporated into anycontract. It is not a commitment to deliver anymaterial, code, or functionality, and should not berelied upon in making purchasing decisions. Thedevelopment, release, and timing of any features orfunctionality described for Oracle’s products remainsat the sole discretion of Oracle.
Slide 2/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Введение
Slide 3/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Введение: предпосылки
Мы, человеки, хорошо умеем писатьпоследовательный софт
Писать паралелльный софт сложноСохранить иллюзию последовательности?Хардвар всё равно последовательный?
«Разделяй и властвуй»Разбить большую задачу на подзадачиВыполнить подзадачи параллельноПоклеить результаты
Slide 4/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Введение: уже и так всё ясно
Каждый девелопер уже писал Fork/Join:
Slide 5/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Введение: проблемы
Привет, закон Амдала:Порезка задачи последовательнаСклейка результатов последовательна(доминирует время исполнения)
Балансировка нагрузкиПоднятие потоковРаздача работы
Иерархические декомпозицииГарантии прогресса
Slide 6/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Введение: общий вид
Result solve(Problem problem) {if (problem.smallEnough ())
return solveDirectly(problem );else {
(task1 , task2) =split(problem );
(result1 , result2) =invokeAll(task1 , task2);
return merge(result1 , result2 );}
}
Slide 7/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Введение: банальные проблемы
Для N-арного дерева глубины K:в худшем случае 𝑁𝐾−1
𝑁−1 + 1 потоков
Slide 8/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Введение: суть
Блокирующее ожиданиетеряет активный потокИдея: join() не должензанимать поток!Заставим «застрявшие»потоки работать:требуется нехилаякоординация
Slide 9/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Для начинающих
Slide 10/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Для начинающих: API
ForkJoinPool...extends ExecutorServiceпринимает в себя Runnable’ы, Callable’ыи вообще работает как обычный пул
ForkJoinTask<V>общий предок всех задач в FJPудобнее стандартные подклассы:RecursiveTask<V> – с газомRecursiveAction – без газа
Slide 11/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Для начинающих: API
ForkJoinPool fjp = new ForkJoinPool ();
fjp.submit(new MyRunnable ());fjp.invoke(new MyCallable ());fjp.invoke(new MyRecursiveTask ());
class MyRunnableimplements Runnable { ... };
class MyCallable <T>implements Callable <T> { ... };
class MyRecursiveTask <T>extends RecursiveTask <T> { ... };
Slide 12/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Балансировка задач: подходы
балансировка хорошо решается в динамике
три базовых подхода:
1. Work arbitrage: общий арбитр, раздающийзадачи; часто обычная blocking queue
2. Work dealing: у каждого свой набор задач,перегруженные потоки отдают свои задачи насторону
3. Work stealing: у каждого свой набор задач,свободные потоки «крадут» задачи уперегруженных
Slide 13/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Балансировка задач: очередь?
0
10
20
30
0 10 20 30submitter threads
wo
rke
r th
rea
ds
0.5 1.0 1.5 2.0 2.5throughput, ops/usec
TPE(ABQ) submission throughput
2x8x2 SandyBridge, RHEL 5.5, JDK 8b89 + jsr166 2013-05-15Slide 14/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Балансировка задач: Work stealing
Локальная очередь для каждого потокаlock-free WorkQueue ∼ ForkJoinTask<?>[]тщательно изолирована от остальных данных
Владелец работает с головой очередибез синхронизации!
Другие потоки могут «тырить» из хвоста«stealing» с минимальной синхронизацией
Slide 15/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Балансировка задач: submit
Куда происходит submit() внешних задач?
в голову случайной очереди? синхронизация!в хвост случайной очереди? FIFO!
Отдельная очередь для внешних задач?Расклеить очереди входных задач!
Slide 16/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Балансировка задач: submit
Куда происходит submit() внешних задач?в голову случайной очереди? синхронизация!в хвост случайной очереди? FIFO!
Отдельная очередь для внешних задач?Расклеить очереди входных задач!
Slide 16/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Балансировка задач: результат
0
10
20
30
0 10 20 30submitter threads
wo
rke
r th
rea
ds
5 10 15 20throughput, ops/usec
FJP submission throughput
2x8x2 SandyBridge, RHEL 5.5, JDK 8b89 + jsr166 2013-05-15Slide 17/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Балансировка задач: результат
0
10
20
30
0 10 20 30submitter threads
wo
rke
r th
rea
ds
0 500 1000 1500 2000 2500%change
FJP vs. TPE(ABQ) submission throughput ratio
2x8x2 SandyBridge, RHEL 5.5, JDK 8b89 + jsr166 2013-05-15Slide 18/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Типичная задача: в кодеprivate static class StandardTask extends RecursiveTask <Long > {
private final Problem problem;private final int l;private final int r;
public StandardTask(Problem p, int l, int r) {this.problem = p;this.l = l;this.r = r;
}
@Overrideprotected Long compute () {
if (r - l <= THRESHOLD) {return problem.solve(l, r);
}
int mid = (l + r) >>> 1;ForkJoinTask <Long > t1 = new StandardTask(problem , l, mid);ForkJoinTask <Long > t2 = new StandardTask(problem , mid , r);t1.fork (); // fork 1t2.fork (); // fork 2
long res = 0;res += t2.join (); // join 2res += t1.join (); // join 1return res;
}}
Slide 19/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Типичная задача: в коде, поближе
ForkJoinTask <Long > t1 , t2;
int mid = (l + r) >>> 1;t1 = new StandardTask(problem , l, mid);t2 = new StandardTask(problem , mid , r);t1.fork (); // fork 1t2.fork (); // fork 2
long res = 0;res += t2.join (); // join 2res += t1.join (); // join 1return res;
Slide 20/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Типичная задача: ещё ближе
ForkJoinTask <Long > t1 , t2;
int mid = (l + r) >>> 1;t1 = new StandardTask(problem , l, mid);t2 = new StandardTask(problem , mid , r);
ForkJoinTask.invokeAll(t1, t2);
long res = 0;res += t2.join (); // get result 1res += t1.join (); // get result 2return res;
Slide 21/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Типичная задача: fork().join()
fork()кладёт в очередь и возвращаетсядаёт возможность «украсть» задачу
join()«ждать, пока закончится»даёт возможность занять поток ещё чем-нибудь
Slide 22/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Типичная задача: join()
магия начинается здесь!
можно сделать одно из действий:1. выполнить эту join-ящуюся задачу2. найти в очереди задачу и выполнить3. пойти в чужую очередь и «украсть» задачу4. ...5. заблокироваться
Slide 23/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Главный козырь: идеи
обычно в локальной очереди куча задачпочти все потоки почти всегда занятыбалансировка требуется редко«украденная» задача засевает очередь
Slide 24/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Главный козырь: Task Trees
2x8x2 SandyBridge, RHEL 5.5, JDK 8b89 + jsr166 2013-05-15Slide 25/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Главный козырь: Queue Occupancy
2x8x2 SandyBridge, RHEL 5.5, JDK 8b89 + jsr166 2013-05-15Slide 26/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Главный козырь: Workers State
2x8x2 SandyBridge, RHEL 5.5, JDK 8b89 + jsr166 2013-05-15Slide 27/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Выбор ширины: проблема
protected Long compute () {if (r - l <= THRESHOLD) {
return seq();}return par();
}
как выбрать THRESHOLD?слишком маленький = куча объектов-задачслишком большой = мало параллелизма
Slide 28/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Выбор ширины: наивный подход
𝑇 = 𝑁𝐶 ,
где𝑁 – размер задачи,𝐶 – количество CPU
Плохо работает при неравномерных задачах,несимметричных потоках, непредсказуемых
задержках.
Slide 29/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Выбор ширины: наивный подход
𝑇 = 𝑁𝐶 ,
где𝑁 – размер задачи,𝐶 – количество CPU
Плохо работает при неравномерных задачах,несимметричных потоках, непредсказуемых
задержках.
Slide 29/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Выбор ширины: правильный метод
𝑇 = 𝑁𝐶𝐿,
где𝑁 – размер задачи,𝐶 – количество CPU,
𝐿 – load factor
Load factor даёт «запас» задач, которыеамортизируют задержки и неравномерности.
Обычное значение 𝐿 ∈ [10..100]
Slide 30/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Выбор ширины: правильный метод
𝑇 = 𝑁𝐶𝐿,
где𝑁 – размер задачи,𝐶 – количество CPU,
𝐿 – load factor
Load factor даёт «запас» задач, которыеамортизируют задержки и неравномерности.
Обычное значение 𝐿 ∈ [10..100]
Slide 30/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Выбор ширины: threshold
0
5
10
15
101
103
105
107
threshold
para
llel speedup, X
Cold Hot Seq x16 Seq x1
FJP: Throughput vs. Threshold (problem size = 10M)
Slide 31/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Выбор ширины: load factor
0
5
10
15
100
102
104
106
load factor
pa
ralle
l sp
ee
du
p,
X
Cold Hot Seq x16 Seq x1
FJP: Throughput vs. Load Factor (problem size = 10M)
Slide 32/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Для продолжающих
Slide 33/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: зачем
WTF: заблокированные потоки в хвосте?
Slide 34/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: зачем
WTF: заблокированные потоки в хвосте?
Slide 34/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: помельче
Рекурсивные join()-ы в хвосте!
Slide 35/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: идея
в отсутствие работы потоки блокируютсяразбудить поток – целое делочем глубже разбиение, тем больше будитьна мелких задачах пожирает время
Идея: вот бы у нас были continuation-ы...
Slide 36/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: идея
в отсутствие работы потоки блокируютсяразбудить поток – целое делочем глубже разбиение, тем больше будитьна мелких задачах пожирает время
Идея: вот бы у нас были continuation-ы...
Slide 36/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: их есть у нас!
public abstract class CountedCompleter <T>extends ForkJoinTask <T> {
// bind to parentpublic CountedCompleter(CountedCompleter <?> parent) { ... }
// compute as usualpublic abstract void compute () { ... }
// pending count = how many subtasks are still alivepublic void setPendingCount(int count) { ... }
// try to completepublic void tryComplete () { ... }
// to be called on completionpublic void onCompletion () { ... }
}
Slide 37/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: логика
образуют дерево назадачахУ каждой задачиесть pendingCount= количествоневыполненныхподзадач
Slide 38/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: логика
void tryComplete () {pendingCount --;if (pendingCount <= 0) {
onCompletion ();if (parent != null) {
parent.tryComplete ();}
}}
Slide 39/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: логика
void tryComplete () {pendingCount --;if (pendingCount <= 0) {
onCompletion ();if (parent != null) {
parent.tryComplete ();}
}}
Slide 39/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: логика
void tryComplete () {pendingCount --;if (pendingCount <= 0) {
onCompletion ();if (parent != null) {
parent.tryComplete ();}
}}
Slide 39/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: логика
void tryComplete () {pendingCount --;if (pendingCount <= 0) {
onCompletion ();if (parent != null) {
parent.tryComplete ();}
}}
Slide 39/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: логика
void tryComplete () {pendingCount --;if (pendingCount <= 0) {
onCompletion ();if (parent != null) {
parent.tryComplete ();}
}}
Slide 39/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: логика
void tryComplete () {pendingCount --;if (pendingCount <= 0) {
onCompletion ();if (parent != null) {
parent.tryComplete ();}
}}
Slide 39/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: логика
void tryComplete () {pendingCount --;if (pendingCount <= 0) {
onCompletion ();if (parent != null) {
parent.tryComplete ();}
}}
Slide 39/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: логика
void tryComplete () {pendingCount --;if (pendingCount <= 0) {
onCompletion ();if (parent != null) {
parent.tryComplete ();}
}}
Slide 39/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: логика
void tryComplete () {pendingCount --;if (pendingCount <= 0) {
onCompletion ();if (parent != null) {
parent.tryComplete ();}
}}
Slide 39/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: логика
void tryComplete () {pendingCount --;if (pendingCount <= 0) {
onCompletion ();if (parent != null) {
parent.tryComplete ();}
}}
Slide 39/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: логика
void tryComplete () {pendingCount --;if (pendingCount <= 0) {
onCompletion ();if (parent != null) {
parent.tryComplete ();}
}}
Slide 39/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: в коде
public static class MyTask extends CountedCompleter <Void > {
public MyTask(CountedCompleter <?> parent , ...) {super(parent );...
}
@Overridepublic void compute () {
if (size < thresh) {seq (...);
} else {setPendingCount (2); // expect two subtasksnew MyTask(this , size / 2, thresh ).fork ();new MyTask(this , size / 2, thresh ).fork ();
}tryComplete (); // increment and try to call completion
}}
Slide 40/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: получилось
«никаких разрывов»Slide 41/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: рецепты
сильно лучше на мелких задачахrecursive: 450± 15 𝜇𝑠
completers: 290± 10 𝜇𝑠
так же быстры на крупных задачах
сильно усложняют логику сборки результатов(упражнение читателю)
Slide 42/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
CountedCompleter: рецепты
сильно лучше на мелких задачахrecursive: 450± 15 𝜇𝑠
completers: 290± 10 𝜇𝑠
так же быстры на крупных задачах
сильно усложняют логику сборки результатов(упражнение читателю)
Slide 42/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Over-signalling: проблема
разбудить весь пул –большая проблемана мелких задачаххарактерное времядля одного потока∼ 50𝜇𝑠
для 𝑁 потоковможет съесть кучувремени∼ 50 𝑙𝑜𝑔(𝑁) 𝜇𝑠
Slide 43/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Over-signalling: решение
∼ 50 𝑙𝑜𝑔(𝑁) 𝜇𝑠
константа от нас независит, только OSможно только базулогарифмауменьшить«полундра!»-mode:будить всех и всяпри всяком удобномслучае
Slide 44/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Common Pool: проблема
в конце концов:первый hand-off впул тормозит всёхарактерное времяот первого wakeup’адо полногопараллелизма∼ 200𝜇𝑠
Slide 45/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Common Pool: решение
Заставить submitter-ыработать на нас!
...они будят пул
...и вообще на насработают
Выигрываем 100𝜇𝑠 ибольше!
Slide 46/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Common Pool: реализация
submitter-у нужно прикинуться FJPThread-омгде-то держать WorkQueue?занять WorkQueue у ForkJoinPool?у какого ForkJoinPool?
Сделаем общесистемный пул по умолчанию= FJP common pool.
Slide 47/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Common Pool: реализация
submitter-у нужно прикинуться FJPThread-омгде-то держать WorkQueue?занять WorkQueue у ForkJoinPool?у какого ForkJoinPool?
Сделаем общесистемный пул по умолчанию= FJP common pool.
Slide 47/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Common Pool: APIpublic class MyTask
extends RecursiveAction { ... }
// submit to the pool.pool.submit(new MyTask ());
// implicitly runs in caller.// may submit to common pool.new MyTask (). invoke ();
// fire and forgetnew MyTask (). fork ();
Fork/Join – неиллюзорная часть платформы.
Slide 48/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Common Pool: APIpublic class MyTask
extends RecursiveAction { ... }
// submit to the pool.pool.submit(new MyTask ());
// implicitly runs in caller.// may submit to common pool.new MyTask (). invoke ();
// fire and forgetnew MyTask (). fork ();
Fork/Join – неиллюзорная часть платформы.
Slide 48/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Для проклятых
Slide 49/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Для проклятых: False Sharing
WorkQueue чувствительны к memory layoutfalse sharing ⇒ 1-10x slowerособенно из-за WorkQueue[] ← лягут рядом
Нас это задолбало, и мы форсировалиработы по @Contended:
@sun.misc.Contendedclass MyClass {
private int myIsolatedField;}
Slide 50/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Для проклятых: False Sharing
WorkQueue чувствительны к memory layoutfalse sharing ⇒ 1-10x slowerособенно из-за WorkQueue[] ← лягут рядом
Нас это задолбало, и мы форсировалиработы по @Contended:
@sun.misc.Contendedclass MyClass {
private int myIsolatedField;}
Slide 50/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Для проклятых: TLR
FJP нуждается в быстром ThreadLocalRandomTLR.current() ∼ ThreadLocal.get()Для критичного кода это очень плохо
Поэтому мы утащили TLR в Thread:
class Thread {long threadLocalRandomSeed;int threadLocalRandomProbe;int threadLocalRandomSecondarySeed;
}
Slide 51/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Для проклятых: TLR
FJP нуждается в быстром ThreadLocalRandomTLR.current() ∼ ThreadLocal.get()Для критичного кода это очень плохо
Поэтому мы утащили TLR в Thread:
class Thread {long threadLocalRandomSeed;int threadLocalRandomProbe;int threadLocalRandomSecondarySeed;
}
Slide 51/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Для проклятых: new intrinsics
из соображений атомарности:class ForkJoinPool {
// packed statevolatile long state;...
}
часто нужно делать апдейт конкретных битовприходится делать CAS loop
Поэтому у нас теперь естьUnsafe.getAndAddLong() сотоварищи.
Slide 52/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
Для проклятых: new intrinsics
из соображений атомарности:class ForkJoinPool {
// packed statevolatile long state;...
}
часто нужно делать апдейт конкретных битовприходится делать CAS loop
Поэтому у нас теперь естьUnsafe.getAndAddLong() сотоварищи.
Slide 52/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
: Параллелизм в моей Джаве
Fork/Join:Путь к параллелизму на платформеВсегда есть, всегда доступен, всегда готовЖёстко затюнен и тюнитсяПоддерживается платформой на всех уровняхМножество подсистем уже использует
Миллионы леммингов не могут ошибаться:JDK 8, Scala/Akka, GPars, Clojure, X10, Fortress
Slide 53/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.
: Ссылки
JSR 166 Interest:http://g.oswego.edu/dl/concurrency-interest/
java-forkjoin-trace:https://github.com/shipilev/java-forkjoin-trace/
Slide 54/54. Copyright c○ 2013, Oracle and/or its affiliates. All rights reserved.