πŸ€“ Study/Game Server

[02μ£Όμ°¨] ν™˜κ²½

GAMEMING 2024. 4. 19. 02:15
728x90

 

- 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;
}

 

system call
no system call

 

 μ‹œμŠ€ν…œ 콜이 μžˆλŠ” 경우 κ²°κ³Όκ°€ ν™•μ—°νžˆ λŠ¦λŠ”λ‹€.

 

 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

 

 

 : pipeline stall - μ»΄ν“¨ν„°μ˜ νŒŒμ΄ν”„λΌμΈμ—μ„œ λ°œμƒν•˜λŠ” μ§€μ—° ν˜„μƒ

 : μœ„μ—μ„œλŠ” λΆ„κΈ° 예츑의 μ‹€νŒ¨λ‘œ 지연이 일어날 수 있음 (abs ν•¨μˆ˜ λ‚΄μ—μ„œ)