[02μ£Όμ°¨] νκ²½
- MMO Game Server
: κ³ μ¬μ νμ (κ°μ±λΉλ₯Ό λ°μ Έ μ μ¬μμ μ¬λ¬ λλ‘ λμ μ μ¬λ¦¬μ§λ μμ)
: λ§μ μ¬λμ λ°μ μ μλ€ → λμ UP → μ¬λ―Έ UP → μ¬μ©μκ° λ λͺ°λ¦Ό → μμ UP
: CPU - μ±κΈ μ½μ΄λ‘λ λμ 1000λ νλ€λ€. λ©ν° μ½μ΄ μ¬μ© νμ
: Memory - 128GB μ΄μ νμ (μλ μ 보, NPC/Monster μ 보, νλ μ΄μ΄ μ 보 λ±)
: Network - μ¬λ¬ κ°μ λ€νΈμν¬ ν¬νΈ νμ
: HWλ§ μ’μΌλ©΄ λλκ°? → νλμ¨μ΄λ₯Ό μΆ©λΆν μ΄μ©ν μ μλ νλ‘κ·Έλλ°μ΄ νμ
: νλμ¨μ΄ μ§μ νμ → νλμ¨μ΄κ° μ±λ₯μ μ΄λ€ μν₯μ λ―ΈμΉλμ§, νλμ¨μ΄μ μ±λ₯μ μ΅λν μ΄λμ΄λ΄λ νλ‘κ·Έλλ° λ±
- CPU
: X86 κ³μ΄, μλ²μ© CPUκ° λ°λ‘ μ‘΄μ¬ν¨(Intel Xeon, AMD EPYC ..)
: 64λΉνΈ(16ExaByte λ©λͺ¨λ¦¬ κ°) μ λ©ν°μ½μ΄
: μ½μ΄ κ°μ, ν΄λμλμ μΊμ¬ ν¬κΈ°, λ©λͺ¨λ¦¬ λ²μ€ λμνμ΄ μ€μ
: NOW (NUMA) - λ©λͺ¨λ¦¬μ£Όμλ₯Ό λλ non μ λνΌ λ©λͺ¨λ¦¬ μ΅μΈμ€
: OLD (UMA) - λͺ¨λ λ©λͺ¨λ¦¬λ₯Ό μ½μ λ μλκ° κ°μ
( CPU κ°μκ° λ§μμ§ μλ‘ κ³Όκ±° λ°©μμμλ μ±λ₯μ νκ³ μ‘΄μ¬ )
: νλ‘κ·Έλλ° μ intμ ν¬μΈν°μ ν¬κΈ°κ° λ¬λΌμ§λ κ²μ μ£Όμνλ€. 리λ μ€λ§ longμ΄ 64λΉνΈ.
1) λ©ν° νλ‘μΈμ
: SMP
: λΉ λ₯Έ λ€νΈμν¬ μλ΅ μλμ μ²λ¦¬ μλ κ°μ μ μν΄ μ¬μ©
2) λ©ν° μ½μ΄
: λ°μ΄μ λ§ν CPUμ μ±λ₯ ν₯μ μ νμ 극볡νκΈ° μν λ°©λ²
: 8~64κ°μ μ½μ΄λ₯Ό νμ©νλλ‘ κ°λ₯
: λ©ν° μ½μ΄ VS λ©ν° νλ‘μΈμ → SWμ μΌλ‘λ μ°¨μ΄κ° μμΌλ HWμ μΌλ‘ λ©λͺ¨λ¦¬ μ κ·Ό μ μ±λ₯ μ°¨μ΄ μ‘΄μ¬
→ λ©ν° μ½μ΄λ νλμ μΉ©μ μ¬λ¬ κ°μ μ½μ΄κ° ν¬ν¨λμ΄ κ°μ λ©λͺ¨λ¦¬ 곡μ / λ©ν° νλ‘μΈμλ μ¬λ¬ κ°μ λ 립 μΉ©μ΄ μλ‘ ν΅μ ν΄ μλνκΈ°μ κ° νλ‘μΈμλ μ체 λ©λͺ¨λ¦¬ κ°μ§
→ (λ©ν° μ½μ΄) CPUμ κ°μ λ§νΌ λ©λͺ¨λ¦¬ λμν μ¦κ°, (λ©ν° νλ‘μΈμ) NUMA(Non Uniform Memory Access) λ¬Έμ
: CPUμ λ°μ
1) ν΄λ μλ μ¦κ° (~2000) : λ°μ΄μ νκ³ λλ
2) Clock λΉ μνλλ λͺ λ Ήμ΄ κ°μ μ¦κ°(IPC) : μν€λ μ³ κ°μ , νμ΄ν λΌμΈ λ±, μΊμ μ©λ μ¦κ° / (νκ³) νκ³ ν¨μ©μ λ²μΉ
3) μ½μ΄ κ°μ μ¦κ° (2005~) : νλ‘κ·Έλ¨ μμ± λ°©μ λ³κ²½
: νμ΄ν λΌμΈμ λ°μ
1) νμ΄ν λΌμΈμ΄ 무ν¨ν λμ§ μλ ν νλ‘κ·Έλ¨μ μ€νλ¨ μλλ λ©λͺ¨λ¦¬ Readμ μ’ μ.
(λͺ λ Ήμ΄κ° λ§μλ λ©λͺ¨λ¦¬ readκ° μ μΌλ©΄ λ λΉ λ¦)
2) SIMD λͺ λ Ήμ΄ λ°μ - νλμ λͺ λ Ήμ΄λ‘ μ¬λ¬ κ°μ μ€μ λμ κ³μ°
3) νμ΄ν κ³ λνμ λ°λ₯Έ μ£Όμ
- νμ΄ν λΌμΈμ 리μ μν€λ©΄ μν΄κ° λ무 νΌ
- 리μ μμΈ : μμ€ν μ½, λΆκΈ° μμΈ‘ μ€λ₯, μΈν°λ½νΈ, μμΈ → μμ€ν μ½ μ§μ, if / switch λ± μμ
- stall μμΈ : μΊμ 리λ λ―Έμ€
: Cache - νλ‘κ·Έλ¨ μ€ν μλμ κ°μ₯ ν° μν₯
- κ°μ΄ μ°μ΄κ² λλ λ°μ΄ν°λ λ¬Άμ
- 루ν μμμ μ¬μ©νλ λ°μ΄ν°λ μΊμ¬μ λ€ μ¬λΌμ¬ μ μλλ‘
- int λμ shortλ charμ μ¬μ© (λ λ§μ΄ λ€μ΄κ°)
: λ©ν° νλ‘μΈμ νλ‘κ·Έλλ°
- μνλ©΄ μ±λ₯ ν₯μ nλ°°, λͺ»νλ©΄ μ±λ₯ νλ½
- lock μ€μ΄κΈ° (lock μ체λ μ€λ²ν€λ, μΈλ§ν¬μ΄/컨λμ λ³μλ μμ€ν νΈμΆ)
- cache thrashing μ£Όμ (μΊμλ λΌμΈ λ¨μλ‘ μμ§μ)
: μλ² OS μ’ λ₯
- Unix κ³μ΄ (리λ μ€...) → κ°κ²© μ λ ΄ & μ μ§ λ³΄μ μ΄λ €μ
- μλμ° κ³μ΄ → λΉμΌλ° μ μ§ λ³΄μκ° λΉκ΅μ μ¬μ
: νλ‘κ·Έλ¨ μ΅μ
- κΌ νμν μΌλ§ → new/delete ν¬ν¨ μμ€ν νΈμΆ μ΅μν
- μ’μ μκ³ λ¦¬μ¦ μ¬μ© → O()
- λ©λͺ¨λ¦¬ λ³΅μ¬ μ€μ΄κΈ° → call by value λμ call by reference / copy constructor μ¬μ© ννΌ
- HW μν₯ κ³ λ € → μΊμ, νμ΄νλΌμΈ
- λ©ν°μ°λ λ νλ‘κ·Έλλ°
- HW μν₯ κ³ λ €
1) μμ€ν Call
// System Call
volatile long long tmp = 0;
DWORD WINAPI ThreadFunc(LPVOID lpParam) {
auto start = high_resolution_clock::now();
for (int j = 0; j < 10000000; ++j) {
tmp += j;
SwitchToThread(); // νμ¬ μ€λ λλ₯Ό μ보νκ³ λ€λ₯Έ μ€λΉλ μ€λ λμκ² CPUλ₯Ό μλ
}
auto duration = high_resolution_clock::now() - start;
cout << "Time " << duration_cast<milliseconds>(duration).count();
cout << " msec\n";
return 0;
}
int main() {
auto start = high_resolution_clock::now();
HANDLE hThread = CreateThread(NULL, 0, ThreadFunc, NULL, 0, NULL);
if (hThread == NULL) {
cerr << "Failed to create thread." << endl;
return 1;
}
WaitForSingleObject(hThread, INFINITE); // μ€λ λκ° μ’
λ£λ λκΉμ§ κΈ°λ€λ¦Ό
CloseHandle(hThread);
auto duration = high_resolution_clock::now() - start;
cout << "RESULT " << tmp << endl;
}
// No system call
int main()
{
volatile long long tmp = 0;
auto start = high_resolution_clock::now();
for (int j = 0; j < 10000000; ++j) {
tmp += j;
this_thread::yield(); // !!
}
auto duration = high_resolution_clock::now() - start;
cout << "Time " << duration_cast<milliseconds>(duration).count();
cout << " msec\n";
cout << "RESULT " << tmp << endl;
}
μμ€ν μ½μ΄ μλ κ²½μ° κ²°κ³Όκ° νμ°ν λ¦λλ€.
2) Cache Miss
constexpr int CACHE_LINE_SIZE = 40;
int main() {
for (int i = 0; i < 20; ++i) {
const int size = 1024 << i;
char* a = (char*)malloc(size);
unsigned int index = 0;
int tmp = 0;
auto start = high_resolution_clock::now();
for (int j = 0; j < 100000000; ++j) { // !!
tmp += a[index % size]; // !!
index += CACHE_LINE_SIZE * 11; // !!
}
auto dur = high_resolution_clock::now() - start;
cout << "Size : " << size / 1024 << "K, ";
cout << "Time " << duration_cast<milliseconds>(dur).count();
}
}
3) Pipelining stall
constexpr int T_SIZE = 100000000;
short rand_arr[T_SIZE];
int abs2(int x) // !!
{
int y = x >> 31;
return (y ^ x) - y;
}
int main(int argc, char** argv)
{
for (int i = 0; i < T_SIZE; ++i)
rand_arr[i] = rand() - 16384;
int sum = 0;
auto start_t = high_resolution_clock::now();
for (int i = 0; i < T_SIZE; ++i)
sum += abs(rand_arr[i]);
auto du = high_resolution_clock::now() - start_t;
cout << "[abs] Time " << duration_cast<milliseconds>(du).count() << " ms\n";
cout << "Result : " << sum << endl;
sum = 0;
start_t = high_resolution_clock::now();
for (int i = 0; i < T_SIZE; ++i)
sum += abs2(rand_arr[i]);
du = high_resolution_clock::now() - start_t;
cout << "[abs2] Time " << duration_cast<milliseconds>(du).count() << " ms\n";
cout << "Result : " << sum << endl;
}
: pipeline stall - μ»΄ν¨ν°μ νμ΄νλΌμΈμμ λ°μνλ μ§μ° νμ
: μμμλ λΆκΈ° μμΈ‘μ μ€ν¨λ‘ μ§μ°μ΄ μΌμ΄λ μ μμ (abs ν¨μ λ΄μμ)