- ๋ฉํฐ์ฐ๋ ๋ ํ๋ก๊ทธ๋๋ฐ ์ฃผ์ ์ฌํญ
: ์ฌ๋ฐ๋ฅธ ๊ฒฐ๊ณผ๊ฐ ๋์์ผ ํจ ( ๋ฌดํ๋ฃจํ์ ๋น ์ง๊ฑฐ๋, ํ๋ก๊ทธ๋จ์ด ์ค๋ฅ๋ก ์ข ๋ฅ๋๋ฉด ์๋จ )
: ์ด๋ฅผ ์ฌ์ฉํ ์ฑ๋ฅํฅ์์ด ์ปค์ผํจ ( ์ ์ผ๋ฉด ๋ฉํฐ์ฐ๋ ๋๋ฅผ ์ฐ๋ ์ด์ ๊ฐ ์์ )
: ๋ฉ์ฐ ํ๋ก๊ทธ๋๋ฐ์ ํ๋์ ํ๋ก๊ทธ๋จ์ ๋๋ ์ ์์ฑํ๋ ๊ฒ
- ์์์ ํ๋ฆฐ ๊ฒฐ๊ณผ๊ฐ ๋์จ ์ด์
: Data Race -> sum += 2
: ๊ณต์ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฌ๋ฌ ์ฐ๋ ๋์์ ์ฝ๊ณ ์ฐ๊ณ , ์ด ์์์ ๋ฐ๋ผ ์คํ ๊ฒฐ๊ณผ๊ฐ ์์๊ณผ๋ ๋ฌ๋ผ์ง๋ค
: Data Race ⇒ ๋ณต์์ ์ฐ๋ ๋๊ฐ ํ๋์ ๋ฉ๋ชจ๋ฆฌ์ ๋์์ ์ ๊ทผ & ์ ์ด๋ ํ ๊ฐ์ write
: ์ฝ๊ณ - ๋ํ๊ณ - ์ฐ๋ ์ด ์ฌ์ด์ context switching์ด ์ผ์ด๋์ ๋ฌธ์ (๋์ ์ ๊ทผ -> read, write)
โ ์์ ํ๋ก๊ทธ๋จ์ ์ฑ๊ธ ์ฝ์ด์์ ๋์์ํค๋ฉด?
โ ์์ ํ๋ก๊ทธ๋จ์ “sum+=2”๋ก ๋ฐ๊พธ๋ฉด?
โ ์์ ํ๋ก๊ทธ๋จ์ “sum+=2”๋ฅผ “_asm add sum,2”๋ก ๋ฐ๊พธ๋ฉด?
โ “_asm add sum,2”๋ก ๋ฐ๊พผ ํ ์ฑ๊ธ ์ฝ์ด์์ ๋์?
4๋ฒ์งธ์ ๊ฒฝ์ฐ๋ฅผ ์ ์ธํ๊ณ ์ฌ๋ฐ๋ฅธ ๊ฐ์ด ๋์ค์ง ์๋๋ค.
→ Data Race๋ฅผ ์์ ๋ฉด ๋๋ค
→ Lock / Unlock ์ฌ์ฉ : ์ฌ๋ฌ ๊ฐ์ ์ฐ๋ ๋๊ฐ ๋์์ ์ ๊ทผํ ์ ์๋๋ก ํจ (๋์์ ํ๋๋ง ์ ๊ทผ ํ๋๋ก ํ๋ค)
- Lock๊ณผ Unlock
#include <iostream>
#include <thread>
#include <mutex>
int sum;
std::mutex m1;
void thread_work()
{
for (auto i = 0; i < 25000000; ++i) {
m1.lock();
// Critical Section
sum = sum + 2;
m1.unlock();
}
}
int main()
{
std::thread t1{ thread_work };
std::thread t2{ thread_work };
t1.join();
t2.join();
std::cout << "sum = " << sum << std::endl;
}
: Mutex ๊ฐ์ฒด๋ ์ ์ญ ๋ณ์๋ก ์ ์ธํ๊ณ ๊ฐ์ ๊ฐ์ฒด ์ฌ์ด์์๋ง Lock๊ณผ Unlock์ด ๋์ํ๋๋ก ํ๋ค.
: ์๋ก ๋์์ ์คํ๋์ด๋ ๊ด์ฐฎ์ critical section์ด ์กด์ฌํ๋ฉด ๋ค๋ฅธ mutex๊ฐ์ฒด๋ก ๋ณดํธํ๋ ๊ฒ์ด ์ข๋ค
→ ๊ฐ์ mutex๋ฅผ ์ฌ์ฉํ๋ฉด ๋์์ ์คํ์ด ๋ถ๊ฐ๋ฅ ํจ
์๋ก ์ํธ ๋ฐฐ์ ๋ฅผ ํด์ผ ํ๋๋ฐ ๋ชจ๋ ํฌ๋ฆฌํฐ์ปฌ ์น์
์ ๋ค ์ํธ ๋ฐฐ์ ๋ฅผ ํด์ผ ํ๋๊ฐ?
→ no (์ฑ๋ฅ ์ ํ), ๊ฐ์ด ์คํ ๋์ด๋ ๋ฌธ์ ๊ฐ ์์ผ๋ฉด ์ ํด๋ ๋จ. ๋ฐ๋ผ์ ๊ฐ์ฒด๋ก ์ ์ธํด์ ๊ตฌํํจ.
[ Lock()์ ์ฌ์ฉํ ํ๋ก๊ทธ๋จ๊ณผ Lock()์ ์ฌ์ฉํ์ง ์์ ํ๋ก๊ทธ๋จ์ ์๋ ๋น๊ต ]
→ ๊ทผ๋ฐ ์ No lock์ธ๋ฐ ์ฐ์ฐ ๊ฒฐ๊ณผ๊ฐ 1์ต์ด ๋์ค๋๊ฐ? DataRace๋ก ์ธํด ์๋ชป๋ ๊ฒฐ๊ณผ ๊ฐ์ด ๋์์ผ ํ๋๊ฑฐ ์๋๊ฐ?
→ ํ์ฌ Release ๋ชจ๋๋ก ์คํ ไธญ → vs ์ปดํ์ผ๋ฌ ์ธ๊ณต์ง๋ฅ์ ์ํด ์๋ ์ต์ ํ๋จ (๋ฃจํ ์๋์๊ฐ๊ณ ๋จ์ ๋ ์ง์คํฐ ์ฐ์ฐ ์ฒ๋ฆฌ )
→ ์ต์ ํ๋ฅผ ํ์ง ์๊ธฐ ์ํด ๋ณ์๋ฅผ volatile์ ์ฌ์ฉํ์ฌ ์ ์ธํด์ฃผ๋ฉด ์ต์ ํ ๋์ง ์์ ๊ฐ์ด ๋์ด
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <vector>
using namespace std;
using namespace std::chrono;
volatile int sum;
mutex a;
// ์ ์ญ๋ณ์๋ฅผ ์ต๋ํ ๋ง๋ค์ง๋ง๋ผ -> ํจ์ ๋งค๊ฐ๋ณ์๋ก ๋๊ฒจ๋ผ
void thread_work(const int num_threads)
{
for (auto i = 0; i < 5000'0000 / num_threads; ++i) {
// a.lock();
sum = sum + 2;
// a.unlock();
}
}
int main()
{
/*char ch;
cin >> ch;*/
for (int num_threads = 1; num_threads <= 16; num_threads *= 2) {
sum = 0;
auto start_t = high_resolution_clock::now();
vector<thread> threads;
for (int i = 0; i < num_threads; ++i)
threads.emplace_back(thread_work, num_threads);
for (auto& th : threads)
th.join();
auto exec_t = high_resolution_clock::now() - start_t;
auto ms = duration_cast<milliseconds>(exec_t).count();
std::cout << "Thread : " << num_threads << ", sum = " << sum << ", Time = " << ms << "ms" << endl;
}
}
- Lock() : ํ ๋ฒ์ ํ๋์ ์ฐ๋ ๋๋ง์ ์คํ ์ํค๊ธฐ์ ๋ณ๋ ฌ์ฑ์ด ๊ฐ์ํจ, Lock์ ์ป์ง ๋ชปํ๋ฉด Queue์ ์์๋ฅผ ์ ์ฅํ๊ณ ์คํ
- Lock ์์ฒด๊ฐ ์ค๋ฒํค๋์ด๊ธฐ์ ์ฌ๊ฐํ๊ฒ ์ฑ๋ฅ์ด ์ ํ๋๋ ํ์์ ๋ณผ ์ ์์
→ ํด๊ฒฐ๋ฐฉ๋ฒ์?
→ Lock์ ์์ฐ๋ฉด ๋จ. Sum += 2๋ฅผ ํ ๋์ ๋ค๋ฅธ thread๊ฐ ์คํ๋์ง ๋ชปํ๋๋ก ํ๋ฉด ๋จ
→ atomic : lcok์์ด ์ํธ๋ฐฐ์ ๋ฅผ ๊ณ ๋ คํด์ ๊ตด๋ฌ๊ฐ, ํจ๋ถ๋ก ์ต์ ํํ์ง ์์, ์ ํด์ง ์์๋๋ก ์ฝ๊ณ ์. atomic ์ฐ์ฐ๋ผ๋ฆฌ๋ ์คํ์์ ๋ฐ๋์ง ์์ + ๊ทผ๋ฐ ๋ค๋ฅธ ์ฐ์ฐ์ด๋๋ ๋ฐ๊พธ์ง ์์
#include <iostream>
#include <thread>
#include <mutex>
#include <chrono>
#include <vector>
#include <atomic>
using namespace std;
using namespace std::chrono;
atomic <int> sum = 0;
mutex a;
void thread_work(const int num_threads)
{
for (auto i = 0; i < 5000'0000 / num_threads; ++i) {
// a.lock();
sum = sum + 2;
// a.unlock();
}
}
int main()
{
/*char ch;
cin >> ch;*/
for (int num_threads = 1; num_threads <= 16; num_threads *= 2) {
sum = 0;
auto start_t = high_resolution_clock::now();
vector<thread> threads;
for (int i = 0; i < num_threads; ++i)
threads.emplace_back(thread_work, num_threads);
for (auto& th : threads)
th.join();
auto exec_t = high_resolution_clock::now() - start_t;
auto ms = duration_cast<milliseconds>(exec_t).count();
std::cout << "Thread : " << num_threads << ", sum = " << sum << ", Time = " << ms << "ms" << endl;
}
}
- ๋ฌธ์ ๋ฐ์ ) ์๊ฐ๋ณด๋ค ์ฑ๋ฅ์ด ๊ทน์ ์ผ๋ก ๋ง์ด ์ค์ง๋ ์์๊ณ , ๊ฐ๋ ์ํฐ๋ฆฌ๋ก ๋์ด.
- sum += 2์ sum = sum + 2๋ ๊ฒฐ๊ณผ๊ฐ์ด ๋ค๋ฅด๋ค (์์ ์ฌ์ง)
_asm add sum,2
_asm lock add sum, 2;
→ ๊ทธ๋ผ ์ด๋ป๊ฒ ํด?
→ ์ฒ์๋ถํฐ DataRace๊ฐ ์ ๋๋ก ์์ฑํ๋ฉด ์ข๋ค
→ Lock์ด๋ Atomic ์ฐ์ฐ์ ์์ ํ ์์ ๋ ๊ฒ์ด ๊ฐ๋ฅํ๊ฐ?
→ No.
// Data Race ์ต์ํ
void optimal_thread_work(const int num_threads)
{
volatile int local_sum = 0;
for (auto i = 0; i < 5000'0000 / num_threads; ++i)
local_sum += 2;
a.lock();
sum += local_sum;
a.unlock();
}
→ ๋ค๋ฅธ ์ ๋ต์ ์์๊น?
volatile int sum;
volatile int sum_arr[16 * 16];
mutex a;
void thread_work(const int num_threads, const int th_id)
{
volatile int local_sum = 0;
for (auto i = 0; i < 5000'0000 / num_threads; ++i) {
sum_arr[th_id * 16] = sum_arr[th_id * 16] + 2;
}
}
int main()
{
for (int num_threads = 1; num_threads <= 16; num_threads *= 2) {
sum = 0;
auto start_t = high_resolution_clock::now();
vector<thread> threads;
for (int i = 0; i < num_threads; ++i)
threads.emplace_back(thread_work, num_threads, i);
for (auto& th : threads)
th.join();
auto exec_t = high_resolution_clock::now() - start_t;
auto ms = duration_cast<milliseconds>(exec_t).count();
std::cout << "Thread : " << num_threads << ", sum = " << sum << ", Time = " << ms << "ms" << endl;
}
}
→ Cache Thrashing - Invalidation PingPong ๋ฐ์ํ ์๋.
→ ์์ธ์ False Sharing์ด๋ค.
: Cache Thrashing - CPU์ ์บ์ ๋ฉ๋ชจ๋ฆฌ๊ฐ ๊ณ์ ๋ฌดํจํ๋๊ณ ๋ค์ ์ฑ์์ง๋ ํ์
: Invalidation PingPong - ๋ ๊ฐ ์ด์์ ์ฝ์ด๊ฐ ๋์ผํ ๋ฐ์ดํฐ์ ๋์์ ์ ๊ทผํ์ฌ ๊ทธ ๋ฐ์ดํฐ์ ์บ์ ๋ผ์ธ์ ๋ฒ๊ฐ์ ๋ฌดํจํจ
: False Sharing - ๋ ๊ฐ ์ด์์ ์ฝ์ด๊ฐ ๋์์ ์ ๊ทผํ๋ ๋ฉ๋ชจ๋ฆฌ ์์ญ์ด ๊ฐ์ ์บ์๋ผ์ธ์ ์์นํด ๋ฐ์ํจ. ๊ณต์ ๋์ง ์์ ๋ฉ๋ชจ๋ฆฌ์ ๋ํ ๋ฌธ์ .
๊ด๋ จ ๊ธ์ ์ด ๊ธ์ ์ฐธ๊ณ ํ์ → https://hwan-shell.tistory.com/230
C++ false sharing์ด๋?(๊ฑฐ์ง ๊ณต์ )
1. false sharing ์ด๋?? false sharing์ ๋ฉํฐ ์ฐ๋ ๋ ํ๊ฒฝ + CPU์ ๋ฉํฐ ์ฝ์ด์์ ๋ฐ์๋ฉ๋๋ค. cpu ๋ด๋ถ์ ์ฝ์ด์ ์ฝ์ด๊ฐ์ ๋ฉ๋ชจ๋ฆฌ ์ ๋ณด๊ฐ ๊ณต์ ๋์ด ํ๋์จ์ด ์ ์ผ๋ก ๋ณ๋ชฉํ์์ด ์ผ์ด๋๋ ๊ฒ์ ๋ปํฉ๋๋ค.
hwan-shell.tistory.com
→ Cache Thrashing ํด๊ฒฐ
: ์ฐ๋ ๋ ๋ง๋ค ์๋ก ๋ค๋ฅธ ์บ์ ๋ผ์ธ์ ์ฌ์ฉํ๋๋ก ํ๋ค. alignas ํค์๋ ์ฌ์ฉ or ํจ๋ฉ(padding)
volatile int sum = 0;
struct NUM {
alignas(64) volatile int sum;
};
NUM sum_arr[16];
mutex a;
void thread_work(const int num_threads, const int th_id)
{
for (auto i = 0; i < 5000'0000 / num_threads; ++i) {
sum_arr[th_id].sum = sum_arr[th_id].sum + 2;
}
}
int main()
{
for (int num_threads = 1; num_threads <= 16; num_threads *= 2) {
sum = 0;
for (auto& s : sum_arr) s.sum = 0;
auto start_t = high_resolution_clock::now();
vector<thread> threads;
for (int i = 0; i < num_threads; ++i)
threads.emplace_back(thread_work, num_threads, i);
for (auto& th : threads)
th.join();
for (int i = 0; i < num_threads; ++i)
sum = sum + sum_arr[i].sum;
auto exec_t = high_resolution_clock::now() - start_t;
auto ms = duration_cast<milliseconds>(exec_t).count();
std::cout << "Thread : " << num_threads << ", sum = " << sum << ", Time = " << ms << "ms" << endl;
}
sum = 0;
auto start_t2 = high_resolution_clock::now();
for (int i = 0; i < 5000'0000; ++i)
sum = sum + 2;
auto exec_t2 = high_resolution_clock::now() - start_t2;
auto ms2 = duration_cast<milliseconds>(exec_t2).count();
std::cout << "sum = " << sum << ", Time = " << ms2 << "ms" << endl;
}
< ์ด๊ฒ์ ๊ผญ ์์ >
- ๋ณ๋ ฌ ์ปดํจํ ์ด๋ ๋ฌด์์ธ๊ฐ?
- ์ฐ๋ ๋๋ ๋ฌด์์ธ๊ฐ?
- ์ ๋ฉํฐ์ฐ๋ ๋ ํ๋ก๊ทธ๋๋ฐ์ ํด์ผ ํ๋๊ฐ?
- ๋ฉํฐ์ฐ๋ ๋ ํ๋ก๊ทธ๋๋ฐ์ ์ด๋ป๊ฒ ํ๋๊ฐ?
- ๋ฉํฐ์ฐ๋ ๋ ํ๋ก๊ทธ๋๋ฐ์ ์ด๋ ค์
- Data Race
- ์ฑ๋ฅ
- ๋ฉํฐ์ฐ๋ ๋ ํ๋ก๊ทธ๋๋ฐ์ ์ข ๋ฅ
1) Heterogeneous ๋ฉํฐ์ฐ๋ ๋ฉ
: ์ฐ๋ ๋ ๋ง๋ค ๋งก์ ์ญํ ์ด ๋ค๋ฆ
: ๋ค๋ฅธ ์ฝ๋ ํํธ๋ฅผ ์คํํ๊ธฐ ๋๋ฌธ์ ์ฐ๋ ๋ ๊ฐ์ Load Balancing(๋ถํ ๋ถ์ฐ) ํ๋ฆ
: ๋ณ๋ ฌ์ฑ์ด ์ ํ๋จ
2) Homogeneous ๋ฉํฐ์ฐ๋ ๋ฉ
: Data/Event Driven ํ๋ก๊ทธ๋๋ฐ
: ๋ชจ๋ ์ฐ๋ ๋๋ Symmetric(๋์นญ์ )ํจ → ๋์ผํ ์ญํ ์ ์ํ
: ์๋์ ์ธ Load Balancing
: ๋ณ๋ ฌ์ฑ ์ ํ ์์
: ์์ ๋ถ๋ฐฐ Queue๋ฅผ ๋น๋กฏํ ์ผ๋ฐ์ ๋ณ๋ ฌ ์๋ฃ๊ตฌ์กฐ ํ์
- ๊ฒ์ ์๋ฒ (MMORPG)
: Window ๋ฉํฐ์ฐ๋ ๋ + Network I/O API์ธ IOCP
: ์ผ๋ฐ์ Select()์ผ๋ก๋ ๋ช ์ฒ๊ฐ์ Socket ์ ๊ด๋ฆฌํ ์ ์์ผ๋ฉฐ ์์ผ ํ๋๋น ํ๋์ ์ฐ๋ ๋๋ OS์ ๊ณผ๋ถํ
: Homogeneous ๋ฉํฐ์ฐ๋ ๋ฉ → Worker thread์ pool ์ฌ์ฉ(HW Core ๊ฐ์์ 1.5๋ฐฐ, ๋คํธ์ํฌ ๋ฐ์ดํฐ ์ฒ๋ฆฌ์ AI๋ฃจํด)
: ์์ผ์ ํตํด ํจํท์ด ์ฌ ๋ ๋ง๋ค, OS๊ฐ thread pool์ ์ฐ๋ ๋๋ฅผ ํ๋ ๊นจ์์ ํจํท์ ์ฒ๋ฆฌ๋ฅผ ๋งก๊น.
: DataBase Query - ์ฐ๋ ๋์ blocking์ ์ด๋ํ๊ธฐ์ blocking ์ ์ฉ ์ฐ๋ ๋๋ฅผ ๋ฐ๋ก ๋ฌ ์์
: NPC AI - ๋ณดํต NPC๋ ๋๋ฌด ๋ง์ด ์กด์ฌํจ, Timer ์ฐ๋ ๋๋ฅผ ์ฌ์ฉํด ํ์ฑํํ NPC์ ์ด๋ฒคํธ๋ง์ ์ฒ๋ฆฌํ๋ค
: ํ์์์ CPU ๋ญ๋น๋ ๊ด์ฐฎ๋ค. ์ต๋ ๋์ ์ด ์ค์
'๐ค Study > MultiThread' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ
[05์ฃผ์ฐจ] ๋๊ธฐํ ์ฐ์ฐ & CAS (0) | 2024.04.24 |
---|---|
[04์ฃผ์ฐจ] ๋ฉ๋ชจ๋ฆฌ ์ผ๊ด์ฑ (0) | 2024.04.18 |
[03์ฃผ์ฐจ] ๋ฉํฐ์ฐ๋ ๋ ํ๋ก๊ทธ๋๋ฐ ์ฃผ์์ฌํญ & ์ํธ๋ฐฐ์ ์๊ณ ๋ฆฌ์ฆ (1) | 2024.04.18 |
[01์ฃผ์ฐจB] ๋ฉํฐ์ฝ์ด HW & ๋ฉํฐ์ฐ๋ ๋ํ๋ก๊ทธ๋๋ฐ ์์ (0) | 2024.03.28 |
[01์ฃผ์ฐจA] ๋ฉํฐ์ฐ๋ ๋ ํ๋ก๊ทธ๋๋ฐ ์๊ฐ (0) | 2024.03.12 |