Неблокирующая синхронизация

59
Неблокирующая синхронизация Владимир Озеров GridGain

Upload: others

Post on 04-Jun-2022

12 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: Неблокирующая синхронизация

Неблокирующая синхронизация

Владимир Озеров

GridGain

Page 2: Неблокирующая синхронизация

План

2

• Мотивация

• Структура

• Производительность

Page 3: Неблокирующая синхронизация

Кто?

3

Page 4: Неблокирующая синхронизация

План

4

• Мотивация

• Структура

• Производительность

Page 5: Неблокирующая синхронизация

Мотивация: определения

5

• BLOCKING – один поток может не давать работать другим потокам неограниченно долго

Page 6: Неблокирующая синхронизация

Мотивация: определения

6

• BLOCKING – один поток может не давать работать другим потокам неограниченно долго

• NON-BLOCKING = !BLOCKING

Page 7: Неблокирующая синхронизация

Мотивация: безопасность

7

Page 8: Неблокирующая синхронизация

Мотивация: безопасность в Java

8

1: lock.lock();

2:

3: try {

4: doSomething();

5: } finally {

6: lock.unlock();

7: }

Page 9: Неблокирующая синхронизация

Мотивация: безопасность в Java

9

1: lock.lock();

2:

3: try {

4: doSomething();

5: } finally {

6: lock.unlock();

7: } Stack overflow!

http://openjdk.java.net/jeps/270

Page 10: Неблокирующая синхронизация

Мотивация: безопасность в .NET :-)

10

1: try {

2: } finally {

3: lock.lock();

4:

5: try {

6: doSomething();

7: } finally {

8: lock.unlock();

9: }

10: }

MSDN: “Unexecuted finally blocks are executed before the thread is aborted.”

Page 11: Неблокирующая синхронизация

Мотивация: инверсия приоритетов

11

Page 12: Неблокирующая синхронизация

Мотивация: инверсия приоритетов

12

1: Connection conn = pool.acquire();

2:

3: try {

4: execute(conn);

5: } finally {

6: pool.release(conn);

7: }

Page 13: Неблокирующая синхронизация

Мотивация: инверсия приоритетов

13

1: Connection conn = pool.tryAcquire(5000L);

2:

3: if (conn != null) {

4: ...

5: } else {

6: conn = Connection.create(...);

7: ...

8: }

Page 14: Неблокирующая синхронизация

Мотивация: deadlocks

14

"ContainersLauncher #27":

at AllocatorPerContext.getLocalPathForWrite(LocalDirAllocator.java:331)

- waiting to lock <B> (AllocatorPerContext)

[3 other calls]

at LocalDirsHandlerService.getLocalPathForWrite(LocalDirsHandlerService.java:262)

- locked <A> (Configuration)

"New I/O server worker #1-3":

at Configuration.getProps(Configuration.java:1373)

- waiting to lock <A> (Configuration)

[7 other calls]

at AllocatorPerContext.getLocalPathToRead(LocalDirAllocator.java:425)

- locked <B> (AllocatorPerContext)

Page 15: Неблокирующая синхронизация

Мотивация: производительность

15

BLOCKING IDEAL

Page 16: Неблокирующая синхронизация

Мотивация: итоги

16

БезопасностьИнверсия приоритетовDeadlock

Производительность

LowLowLow

High

Page 17: Неблокирующая синхронизация

Мотивация: итоги

17

БезопасностьИнверсия приоритетовDeadlock

ПроизводительностьУдобство

LowLowLow

HighHigh

Page 18: Неблокирующая синхронизация

План

18

• Мотивация

• Структура

• Производительность

Page 19: Неблокирующая синхронизация

Структура: compare-and-set

19

1: bool CAS(T* address, T expected, T new) {

2: if (*address == expected) {

3: *address = new;

4:

5: return true;

6: } else

7: return false;

8: }

Page 20: Неблокирующая синхронизация

Структура: compare-and-set

20

Page 21: Неблокирующая синхронизация

Структура: atomics

21

1: synchronized (...) {

2: if (starting)

3: throw new AlreadyStartedException();

4:

5: starting = true;

6: }

7:

8: doStart();

Page 22: Неблокирующая синхронизация

Структура: atomics

22

1: synchronized (...) {

2: if (starting)

3: throw new AlreadyStartedException();

4:

5: starting = true;

6: }

7:

8: doStart();

1: if (!starting.compareAndSet(false, true))

2: throw new AlreadyStartedException();

3:

4: doStart();

Page 23: Неблокирующая синхронизация

Структура: races

23

Page 24: Неблокирующая синхронизация

Структура: races

24

Page 25: Неблокирующая синхронизация

Упрощаем: меньше shared state

25

1: volatile int a;

2: volatile int b;

Page 26: Неблокирующая синхронизация

Упрощаем: меньше shared state

26

1: volatile int a;

2: volatile int b;

1: class State {

2: final int a;

3: final int b;

4: }

5:

6: volatile State state;

Page 27: Неблокирующая синхронизация

Упрощаем: обратно в blocking

27

1: Node node = new Node(key, val);

2:

3: if (table.compareAndSet(index, null, node))

4: return null;

5: else {

6: Node oldNode = table.get(index);

7:

8: synchronized (oldNode) {

9: ...

10: }

11: }

ConcurrentHashMap

Page 28: Неблокирующая синхронизация

Упрощаем: меньше гарантий

28

1: private final ConcurrentLinkedQueue queue;

2: private final LongAccumulator size;

3:

4: public boolean offer(E e) {

5: size.accumulate(1L);

6:

7: return queue.offer(e);

8: }

Page 29: Неблокирующая синхронизация

Упрощаем: меньше гарантий

29

1: private final ConcurrentLinkedQueue queue;

2: private final LongAccumulator size;

3:

4: public boolean offer(E e) {

5: size.accumulate(1L);

6:

7: return queue.offer(e);

8: }

assert q.size() > 0 && q.peek() != null;

FAIL!

Page 30: Неблокирующая синхронизация

Структура: lock-free

30

Page 31: Неблокирующая синхронизация

Пример: busy lock

31

1: class BusyLock {

2: boolean tryAcquire();

3: void release();

4:

5: void block();

7: }

Page 32: Неблокирующая синхронизация

Структура: lock-free

32

1: final AtomicInteger state = new AtomicInteger();

2:

3: boolean tryAcquire() {

4: while (true) {

5: int state0 = state.get();

6:

7: if (isBlocked(state0))

8: return false;

9:

10: if (state.compareAndSet(state0, state0 + 1)

11: return true;

12: }

13: }

Page 33: Неблокирующая синхронизация

Структура: wait-free

33

Page 34: Неблокирующая синхронизация

Структура: wait-free

34

1: final AtomicInteger acquired = new AtomicInteger();

2: volatile boolean blocked;

3:

4: boolean tryAcquire() {

5: acquired.incrementAndGet();

6:

7: if (blocked) {

8: acquired.decrementAndGet();

9:

10: return false;

11: }

12:

13: return true;

14: }

Page 35: Неблокирующая синхронизация

Структура: assist

35

Page 36: Неблокирующая синхронизация

Структура: assist

36

1: AtomicReference<Batch> batch;

2:

3: void addToBatch(Object item) {

4: while (true) {

5: Batch batch0 = batch.get();

6:

7: if (batch0.tryAdd(item))

8: return;

9:

10:

11: }

12: }

Page 37: Неблокирующая синхронизация

Структура: assist

37

1: AtomicReference<Batch> batch;

2:

3: void addToBatch(Object item) {

4: while (true) {

5: Batch batch0 = batch.get();

6:

7: if (batch0.tryAdd(item))

8: return;

9: else

10: batch.compareAndSet(batch0, new Batch());

11: }

12: }

Page 38: Неблокирующая синхронизация

План

38

• Мотивация

• Структура

• Производительность

Page 39: Неблокирующая синхронизация

Производительность: latency

39

Page 40: Неблокирующая синхронизация

Производительность: lock convoy

40

Page 41: Неблокирующая синхронизация

Производительность: throughput

41

BLOCKING IDEAL

Page 42: Неблокирующая синхронизация

Производительность: throughput

42

1: BusyLock lock;

2:

3: void doBenchmark() {

4: if (lock.tryAcquire()) {

5: try {

6: payload(); // e.g. consumeCPU(x)

7: } finally {

8: lock.release();

9: }

10: }

11: }

Page 43: Неблокирующая синхронизация

Производительность: CAS

43

0

10

20

30

40

50

1 2 4 8

MO

ps\

sec

Threads

CAS

Page 44: Неблокирующая синхронизация

Производительность: synchronized

44

0

10

20

30

40

50

1 2 4 8

MO

ps\

sec

Threads

CAS

synchronized

Page 45: Неблокирующая синхронизация

Производительность: contention

45

CAS/increment

Page 46: Неблокирующая синхронизация

Производительность: contention

46

CAS/increment synchronized

http://mechanical-sympathy.blogspot.ru/2013/01/further-adventures-with-cas.htmlhttp://www.intel.com/content/dam/www/public/us/en/documents/white-papers/xeon-lock-scaling-analysis-paper.pdf

Page 47: Неблокирующая синхронизация

Производительность: batching

47

1: AtomicLong generator;

2:

3: return generator.getAndIncrement();

Page 48: Неблокирующая синхронизация

Производительность: batching

48

1: AtomicLong generator;

2:

3: return generator.getAndIncrement();

1: AtomicLong generator;

2:

3: return generator.getAndAdd(1000);

Page 49: Неблокирующая синхронизация

Производительность: backoff

49

1: boolean tryAcquire() {

2: for (int i = 0;; i++) {

3: ...

4:

5: if (isSpin(i))

6: for (...)

7: Runtime.getRuntime().onSpinWait();

8: else if (isYield(i))

9: Thread.sleep(0);

10: else

11: Thread.sleep(1);

12: }

13: }

Page 50: Неблокирующая синхронизация

Производительность: backoff

50

0

10

20

30

40

50

1 2 4 8

MO

ps\

sec

Threads

CAS

CAS + backoff

Page 51: Неблокирующая синхронизация

Производительность: no stores

51

1: volatile HashMap<K, V> map;

2:

3: V get(K key) {

4: return map.get(key);

5: }

6:

7: V put(K key, V val) {

8: synchronized (this) {

9: map = copyAndPut(key, val);

10: }

11: }

Page 52: Неблокирующая синхронизация

Производительность: stripes

52

Page 53: Неблокирующая синхронизация

Производительность: stripes

53

0

50

100

150

1 2 4 8

MO

ps\

sec

Threads

CAS

CAS + backoff

CAS + stripes

Page 54: Неблокирующая синхронизация

Производительность: local state

54

Page 55: Неблокирующая синхронизация

Производительность: консенсус

55

1: void resize(Node[] oldTable, int newSize) {

2: Node[] newTable = new Node[newSize];

3:

4: newTable.compareAndSet(oldTable, newTable);

5:

6: ...

7: }

NonBlockingHashMap

https://github.com/boundary/high-scale-lib/blob/master/src/main/java/org/cliffc/high_scale_lib/NonBlockingHashMap.java#L752

Page 56: Неблокирующая синхронизация

Выводы

56

Мотивация:• В первую очередь – удобство и performance• Во вторую – дедлоки, priority inversion, safety

Структура:• Минимум операций над shared variables• Не удерживаем non-blocking, если оно не нужно• Гарантии: lock-free vs wait-free

Performance:• Latency: непрерывное выполнение кода потоком• Throughput: боремся с contention, а не с блокировками

Page 57: Неблокирующая синхронизация

Ссылки

57

Площадки:• http://jsr166-concurrency.10961.n7.nabble.com/• https://groups.google.com/forum/#!forum/mechanical-sympathy

Люди:• Dmitry Vyukov – http://www.1024cores.net/• Nitsan Wakart – http://psy-lob-saw.blogspot.ru/• Martin Thompson – http://mechanical-sympathy.blogspot.ru/

Проекты:• JCTools – https://github.com/JCTools/JCTools• Agrona (Aeron) – https://github.com/real-logic/Agrona

Page 58: Неблокирующая синхронизация

Контакты

58

Twitter:• https://twitter.com/devozerov

GitHub:• https://github.com/devozerov/ozerov_2016_jpoint

Page 59: Неблокирующая синхронизация

59

Вопросы?