๐Ÿค“ Study/MultiThread

[05์ฃผ์ฐจ] ๋™๊ธฐํ™” ์—ฐ์‚ฐ & CAS

GAMEMING 2024. 4. 24. 23:43
728x90

 

- Lock-free ์ž๋ฃŒ๊ตฌ์กฐ

 : ์—ฌ๋Ÿฌ ๊ฐœ์˜ ์“ฐ๋ ˆ๋“œ์—์„œ ๋™์‹œ์— ํ˜ธ์ถœํ–ˆ์„ ๋•Œ์—๋„ ์ •ํ•ด์ง„ ๋‹จ์œ„ ์‹œ๊ฐ„๋งˆ๋‹ค ์ ์–ด๋„ ํ•œ ๊ฐœ์˜ ํ˜ธ์ถœ์ด ์™„๋ฃŒ๋˜๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜

 : Non-blocking์ด ๋ณด์žฅ๋˜์–ด์•ผ ํ•จ

 : ์•Œ๊ณ ๋ฆฌ์ฆ˜ ๋‚ด์— Lock์ด ์žˆ์œผ๋ฉด ๋‹น์—ฐํžˆ Lock-free X, Lock์ด ์—†๋‹ค๊ณ  ํ•ด๋„ ๋ฌด์กฐ๊ฑด Lock-free์ธ ๊ฒƒ์€ ์•„๋‹˜

 : Lock์ด ์กด์žฌํ•˜๋Š” ๊ฒฝ์šฐ ๋‹ค๋ฅธ ์“ฐ๋ ˆ๋“œ๋ฅผ ์‹คํ–‰ํ•  ์ˆ˜ ์—†์–ด ์‹ฑ๊ธ€ ์“ฐ๋ ˆ๋“œ์™€ ๋‹ค๋ฅผ ๋ฐ”๊ฐ€ ์—†์Œ

 

 

 

 

 → wait-free๋ฅผ ์œ ์ง€ํ•˜๋ฉฐ, ๋ฉ”๋ชจ๋ฆฌ๋กœ Atomic Memory๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์žˆ๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์กด์žฌ?

 : ์กด์žฌ, ์ด ๊ฐ•์˜์—์„œ ์ƒ๋žต.

 

 → wait-free๋ฅผ ์œ ์ง€ํ•˜๋ฉฐ, ๊ธฐ์กด ์”ฝ๊ธ€ ์Šค๋ ˆ๋“œ ์ž๋ฃŒ๊ตฌ์กฐ๋„ Atomic Memory๋ฅผ ์‚ฌ์šฉํ•ด ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ์ž๋ฃŒ๊ตฌ์กฐ๋กœ ๋ณ€ํ™˜ ๊ฐ€๋Šฅ?

 : ๋ถˆ๊ฐ€๋Šฅํ•˜๋‹ค. ๋‚˜์ค‘์— ์ž์„ธํžˆ ๋‹ค๋ฃธ.

 : Atomic memory๋ฅผ ์‚ฌ์šฉ๋งŒํ•ด์„œ๋Š” ๋…ผ๋ธ”๋กœํ‚น ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“ค ์ˆ˜ ์—†์Œ

 

 → wait-free๋ฅผ ์œ ์ง€ํ•˜๋ฉฐ, ์ผ๋ฐ˜์ ์ธ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ์ž๋ฃŒ๊ตฌ์กฐ๋กœ ๋ณ€ํ™˜ํ•˜๊ธฐ ์œ„ํ•ด Atomic-memory๋ง๊ณ  ๋ฌด์—‡์ด ๋”
ํ•„์š”?

 : CompareAndSet() ์—ฐ์‚ฐ์ด๋ฉด ์ถฉ๋ถ„ํ•จ

 

 

 

- CompareAndSet

 : ๋™๊ธฐํ™” ์—ฐ์‚ฐ์˜ ์ผ์ข…

 

 → ๋™๊ธฐํ™” ์—ฐ์‚ฐ

  : ๊ณต์œ  ๋ฉ”๋ชจ๋ฆฌ์— ๋Œ€ํ•œ ์—ฐ์‚ฐ (ํ•œ word์— ๋Œ€ํ•œ ์—ฐ์‚ฐ - ์ฝ๊ณ  ์“ฐ๊ธฐ ๋“ฑ)

  : ๋‹ค๋ฅธ ์“ฐ๋ ˆ๋“œ์™€ ํ†ต์‹ ํ•˜๊ธฐ ์œ„ํ•œ ๊ธฐ๋ณธ ๊ธฐ๋Šฅ

  : CPU์˜ ๋ช…๋ น์–ด๋กœ ๊ตฌํ˜„ + Wait-free๋กœ ๊ตฌํ˜„๋˜์–ด์•ผ ํ•จ (๊ทธ๋ ‡์ง€ ์•Š์œผ๋ฉด Non-Blocking ์•Œ๊ณ ๋ฆฌ์ฆ˜์—์„œ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Œ)

 

 • Load (wait-free)
 • Store (wait-free)
 • Lock/Unlock (blocking)
 • atomic_thread_fence (wait-free)
 • +=, fetch_and_add (wait-free)

 

 + CAS(Compare And Set, wait-free) ์ถ”๊ฐ€ ํ•„์š”

 : bool ๋ฉ”๋ชจ๋ฆฌ::CAS(expected, update)

 : ๋ฉ”๋ชจ๋ฆฌ์˜ ๊ฐ’์ด exptected๋ฉด update๋กœ ๋ฐ”๊พธ๊ณ  true, ๋งŒ์•ฝ expected๊ฐ€ ์•„๋‹ˆ๋ฉด false๋ฅผ return.

bool ๋ฉ”๋ชจ๋ฆฌ::CAS(expected, update)
{
 	if (๋ฉ”๋ชจ๋ฆฌ.value == expected) {
 	๋ฉ”๋ชจ๋ฆฌ.value = update; 
 	return true;
 	} else return false;
}

 

 : atomic์˜ load/store๋กœ ๊ตฌํ˜„ํ•  ์ˆ˜ ์—†์Œ - wait-free ์กฐ๊ฑด ๋•Œ๋ฌธ

 

CAS๋Š” wait-free๋ฅผ ์œ ์ง€ํ•˜๋ฉฐ ์ผ๋ฐ˜์ ์ธ ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋ฉ€ํ‹ฐ์“ฐ๋ ˆ๋“œ ์ž๋ฃŒ๊ตฌ์กฐ๋กœ ์–ด๋–ป๊ฒŒ ๋ณ€ํ™˜ํ•˜๋Š”๊ฐ€? 

 : ๋ชจ๋“  ๊ธฐ์กด ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ wait-free multithread๋กœ ๋ณ€ํ™˜ํ•ด์ฃผ๋Š” ์•Œ๊ณ ๋ฆฌ์ฆ˜์ด ์กด์žฌ

 : ํ˜„์‹ค ) ๋น„ํšจ์œจ์ 

 : ํšจ์œจ์ ์ธ ๋ณ€ํ™˜์€ ์ƒ๋‹นํ•œ ๋…ธ๋ ฅ์„ ํ•„์š”๋กœ ํ•จ

 

 

์‹ค์ œ CAS์˜ ๊ตฌํ˜„

 : atomic_compare_and_set ์—†๊ณ , atomic_compare_exchange๋ฅผ ๋Œ€์‹  ์‚ฌ์šฉ

 : ๋…ผ๋ธ”๋กœํ‚น ์ž๋ฃŒ๊ตฌ์กฐ๋ฅผ ๋งŒ๋“œ๋Š” ๋ฐ ํ•„์ˆ˜ ์—ฐ์‚ฐ

bool CAS(atomic_int *addr, int expected, int new_val)
{
 	return atomic_compare_exchange_strong(addr, &expected, new_val);
}

bool CAS(volatile int *addr, int expected, int new_val)
{
 	return atomic_compare_exchange_strong(reinterpret_cast<volatile atomic_int *>(addr),
    					&expected, new_val);
}

 

 

[ CAS๋ฅผ ์ด์šฉํ•œ lock๊ณผ unlock๊ตฌํ˜„, 1์–ต ๋งŒ๋“ค๊ธฐ ํ”„๋กœ๊ทธ๋žจ ]

volatile int X;
volatile int sum;
bool g_ready = false;
int g_data = 0;
std::mutex srm;

bool CAS(volatile int* addr, int expected, int new_value)
{
    return std::atomic_compare_exchange_strong(reinterpret_cast<volatile std::atomic_int*>(addr), &expected, new_value);
}

void cas_lock()
{
    while (false == CAS(&X, 0, 1));
}

// X = 1 ๋ˆ„๊ฐ€ lock์„ ์–ป์—ˆ๋‹ค. 0์ด๋ฉด ์•„๋ฌด๋„ lock์„ ์–ป์ง€ ์•Š์•˜๋‹ค.
void cas_unlock()
{
    X = 0;
}


void thread_func(int num_threads, int th_id)
{
	for (int i = 0; i < 500'0000 / num_threads; ++i){
		cas_lock();
		sum += 2;
		cas_unlock();
	}
}

int main()
{
	for (int num_threads = 1; num_threads <= 16; num_threads *= 2) {
		sum = 0;
		auto start_t = std::chrono::high_resolution_clock::now();
		std::vector<std::thread> threads;
		for (int i = 0; i < num_threads; ++i)
			threads.emplace_back(thread_func, num_threads, i);

		for (auto& th : threads)
			th.join();

		auto exec_t = std::chrono::high_resolution_clock::now() - start_t;
		auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(exec_t).count();

		std::cout << "Thread : " << num_threads << ", sum = " << sum << ", Time = " << ms << "ms" << std::endl;
	}
}

 

 

 

 : CAS๋ฅผ ์‹คํ–‰ํ•  ๋•Œ ์˜ค์ง ํ•˜๋‚˜์˜ ์“ฐ๋ ˆ๋“œ์—์„œ๋งŒ true๋ฅผ ๋ฐ˜ํ™˜ํ•˜๋ฉด ๋จ

 : ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋Š˜์–ด๋‚  ์ˆ˜๋ก ์„ฑ๋Šฅ์€ ๋–จ์–ด์ง„๋‹ค, ์™œ?

 1) CAS๋ฅผ ์‹คํŒจํ–ˆ์„ ๊ฒฝ์šฐ ๊ณ„์†ํ•ด์„œ CAS ํ•จ (mutex๋ณด๋‹ค๋Š” ์•„๋‹ˆ์ง€๋งŒ, ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ์กด์žฌํ•œ๋‹ค)

 2) ์“ฐ๋ ˆ๋“œ๊ฐ€ ๋Š˜์–ด๋‚˜๋Š” ๋งŒํผ ๊ณ„์†ํ•ด์„œ CAS๋ฅผ ์ง„ํ–‰ํ•œ๋‹ค