+ All Categories
Home > Software > Максим Хижинский Lock-free maps

Максим Хижинский Lock-free maps

Date post: 05-Aug-2015
Category:
Upload: platonov-sergey
View: 365 times
Download: 1 times
Share this document with a friend
27
Concurrent mapS C++ Russia 2015 Maxim Khizhinsky
Transcript

Concurrent mapS

C ++ Russia 2015Maxim Khizhinsky

Lock-free hash tableLock-free hash table

LIBCDS

Максим Хижинский, C++ Russia 2015

0 1 2 3 4 5 6

X X Xk1

k

k

k k

k

kk

k

k

2

3

4 5

6

7

8

9

T[8]

Lock-freeсписок

коллизий

7

k10

Lock-free ordered listLock-free ordered list

LIBCDS

Максим Хижинский, C++ Russia 2015

Операции:

● insert( node )● erase( key )● find( key )

template <class T>struct node { std::atomic<node*> next_; T data_;};

H T52 8

Lock-free примитивы:● atomic load/store● atomic compare-and-swap (CAS)

CAS — compare-and-swapCAS — compare-and-swap

LIBCDS

Максим Хижинский, C++ Russia 2015

template <typename T>bool CAS( T * pAtomic, T expected, T desired )atomically { if ( *pAtomic == expected ) { *pAtomic = desired; return true;

} else return false;};

Lock-free list: insertLock-free list: insert

LIBCDS

Максим Хижинский, C++ Russia 2015

H T52 8

3

H T52 8

3

3. prev->next_.CAS( next, new_node )

1. find insert position for key 3

2. new_node.next_.store( next )

H T52 8prev next

new_node

Lock-free list: eraseLock-free list: erase

LIBCDS

Максим Хижинский, C++ Russia 2015

1. find key 3

2. prev->next_.CAS( found, next )

H T52 8

prev

3

found

H T52 83

Проблема: параллельный insert

next

Lock-free list: insert/eraseLock-free list: insert/erase

LIBCDS

Максим Хижинский, C++ Russia 2015

A: find key 3

H T52 8prev

3found

B: find insert pos for key 4

iprev inextA: erase key 3

H T52 83

prev->next_.CAS( found, next )

next

B: insert key 4

H T52 83

4

iprev->next_.CAS( inext, new_item )

local vars

Marked pointerMarked pointer

LIBCDS

Максим Хижинский, C++ Russia 2015

[ T.Harris, 2001 ]

Двухфазное удаление:● Логическое удаление — помечаем элемент● Физическое удаление — исключаем элемент

В качестве метки используем младший бит указателя

Lock-free list: marked pointerLock-free list: marked pointer

LIBCDS

Максим Хижинский, C++ Russia 2015

H T52 8prev

3foundiprev inext

nextA: eraseB: insert

A: Logical deletion - mark item found

H T52 83

found->next_.CAS( next, next | 1 )B: iprev->next_.CAS( inext, new_item ) - failed!!!

A: Physical deletion - remove item found

H T52 83

prev->next_.CAS( found, next )

Lock-free list: problemsLock-free list: problems

LIBCDS

Максим Хижинский, C++ Russia 2015

H T52 8

prev

3

foundiprev inext

nextA: eraseB: insert

iprev->next_.CAS( inext, new_item )

prev->next_.CAS( found, next )

local vars

Вдруг уже удалены?..

Lock-free list: problemsLock-free list: problems

LIBCDS

Максим Хижинский, C++ Russia 2015

Проблемы:

● Защита локальных данных — когда элемент можно безопасно удалить?

● ABA-проблема

ABA-проблемаABA-проблема

LIBCDS

Максим Хижинский, C++ Russia 2015

52

prev

3

found next

Thread A: erase(3) Thread B

52 3

erase(3); erase(5)

2 3insert(4)

Heap

new node(4) alloc

delete

42

preempted...

42

prev found next

5

addr(3) == addr(4)

prev->next_.CAS( found, next ) - success!!!

2 мусор

SMRSMR

LIBCDS

Максим Хижинский, C++ Russia 2015

Проблемы:● Защита локальных данных — когда элемент можно

безопасно удалить?● ABA-проблема

Решение: Safe memory reclamation (SMR)● Tagged pointers● Hazard Pointers● User-space RCU

Tagged pointersTagged pointers

LIBCDS

Максим Хижинский, C++ Russia 2015

pointer tag

prev->next_.dwCAS( found, <next.ptr, prev->next_.tag + 1> )

template <class T>struct tagged_ptr { T * ptr; uintptr_t tag;};

Требует dwCAS — не везде есть Решает только ABA-проблему

Освободить память нельзя, нужен free-list

[ boost.lock-free ]

H T52 8prev

3found next

Tagged pointers: historyTagged pointers: history

LIBCDS

Максим Хижинский, C++ Russia 2015

ABA-проблема характерна только для CAS

Архитектуры процессоров

LL/SC: ● IBM PowerPC● MIPS● ARM

➢ LL — load linked➢ SC — store conditional

bool weak_CAS( T * ptr, T expected, T desired ){ T cur = LL( ptr ); return cur == expected && SC( ptr, desired );}

Эмуляция LL/SC на CAS — намного труднее

CAS: ● x86, amd64● Sparc● Itanium

С++11 — только CAS

Hazard pointersHazard pointers

LIBCDS

Максим Хижинский, C++ Russia 2015

✔ Использует только атомарные чтение/запись Защищает только локальные ссылки✔ Размер массива отложенных (готовых к

удалению) элементов ограничен сверху Перед работой с указателем его следует

объявить как hazard

решает ABA-проблему Физическое удаление элементов

Hazard pointersHazard pointers

LIBCDS

Максим Хижинский, C++ Russia 2015

H T52 8prev

3found next

erase( Key k ) { hp_guard h1 = get_guard(); hp_guard h2 = get_guard();retry: node * prev = Head; do { node * found = h2.protect( prev->next_); if ( found->key == k ) if (prev->next_.CAS( found, found->next_)) { hp_retire( found ); return true; } else goto retry; h1 = h2; prev = found; } while ( found->key < k ); return false; }

Распределяем HP (TLS)

Защищаем элемент

Удаляем элемент

Hazard pointersHazard pointers

LIBCDS

Максим Хижинский, C++ Russia 2015

P – thread count

Thread 0

Thread HP Manager

0

1

K - 1

HP[K]0

1

2

R - 1

Retired[R]

Hazard Pointer Singleton

Thread 1

Thread P - 1

K = 4 R = 2 KP

<K,P, R> : R > K * P

Hazard PointersHazard Pointers

LIBCDS

Максим Хижинский, C++ Russia 2015

Объявление Hazard Pointer'а – защита локальной ссылки

class hp_guard {

void * hp;

// ...

};

T * hp_guard::protect(

std::atomic<T*>& what) {

T * t;

do {

hp = t = what.load();

} while (t != what.load());

return t;

}

0

1

K - 1

HP[K]

Hazard PointersHazard Pointers

LIBCDS

Максим Хижинский, C++ Russia 2015

Удаление элемента

void hp_retire( T * what ) {

push what to current_thread.Retired array

if ( current_thread.Retired is full )

hp.Scan( current_thread );

}

void hp::Scan() {

void * guarded[K*P] = union HP[K] for all P thread;

foreach ( p in current_thread.Retired[R] )

if ( p not in guarded[] )

delete p;

}

<K,P, R> : R > K * P

012…

R - 1

Retired[R]

01…

K - 1

HP[K]

User-space Read-Copy UpdateUser-space Read-Copy Update

LIBCDS

Максим Хижинский, C++ Russia 2015

✔ RCU — метод синхронизации: RCU.lock() / RCU.unlock()

✔ Разработан для почти-read-only данных (map, set)✔ Очень легкие read-side lock✔ Удаление элемента — ожидание окончания эпохи

решает ABA-проблему Физическое удаление элементов

RCU.lock(): ничего не блокируетОбъявляет, что поток входит в текущую RCU-эпоху

User-space RCUUser-space RCU

LIBCDS

Максим Хижинский, C++ Russia 2015

Map.find( ... );

Set.insert( ... );

Map.find( ... );

Map.erase( ... )...

Thread 1

Set.find( ... );

Map.insert( ... );

Set.find( ... );

Set.insert( ... );

Thread N

Эпоха 1

RCU.sync() - ждем, пока все потоки покинут эпоху 1

Set.find( ... );

Map.insert( ... );

Set.find( ... );

Set.insert( ... );

... Map.erase;

Map.find( ... );

Set.insert( ... );

Map.find( ... );

++Эпоха

Эпоха 2

Lock-free hash tableLock-free hash table

LIBCDS

Максим Хижинский, C++ Russia 2015

0 1 2 3 4 5 6

X X Xk1

k

k

k k

k

kk

k

k

2

3

4 5

6

7

8

9

T[8]

Lock-freecписок:HP/RCU

+ marked pointers

7

k10

Hash table + Lock-free ordered list

No rehashing

Split-ordered listSplit-ordered list

LIBCDS

Максим Хижинский, C++ Russia 2015

0 8 2 1 9 13T[0]

T[1]

k iSentinel node Regular node

N = 2 size() = 4

Load factor L: size() / N ≤ L

Если L = 2, то вставка нового элемента приводит к увеличениюhash table

Hash table

Lock-free ordered list

Skip listSkip list

LIBCDS

Максим Хижинский, C++ Russia 2015

XXXXXXXX10 15 23 34 5542

23

Tower

h = 3

Вероятностная структура данных:

P[ h == 1 ] = 1/2

P[ h == k ] = 1/2k, 0 < k < 32

h = lsb( rand() )

O(log(N))

PerformancePerformance

LIBCDS

Максим Хижинский, C++ Russia 2015

Intel Dual Xeon X5670 2.93 GHz 12 cores 24 threads / 24 GB RAM

Concurrent mapsConcurrent maps

Максим Хижинский, C++ Russia 2015

Спасибо за внимание[email protected]

https://github.com/khizmax/libcds


Recommended