Максим Хижинский Lock-free maps
-
Upload
platonov-sergey -
Category
Software
-
view
369 -
download
1
Transcript of Максим Хижинский Lock-free maps
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