구글이 리눅스 커널 패치를 통해 TCP 통신성능을 40%까지 향상할 수 있었다는 것을 알게 됐습니다. 어떻게 했길래 40%라는 수치가 나오는지 궁금해서 알아보았고 아주 단순하고 그냥 지나칠 수 있는 기본 지식을 실제로 적용하는 것이 성능에 크게 영향을 미칠 수 있다는 사실이 놀라워 글로 작성해 봅니다.
원본 아티클: Linux 6.8 Network Optimizations Can Boost TCP Performance For Many Concurrent Connections By ~ 40%
유튜브 영상: Google Patches Linux kernel with 40% TCP performance - Hussein Nasser
구글은 어떻게 성능을 향상했는가?
구글은 리눅스 커널의 TCP 성능을 향상하기 위해 핵심 네트워킹 구조체의 분석 및 재조직해 캐시라인 소비를 최적화했습니다. 이전 구조체들의 멤버 변수는 추가된 시간 순서대로 작성되어있기도 하고 논리적으로 비슷한 변수들끼리 모여있기도 하며 캐시라인에 따라 모여있기도 했었습니다. 구글은 이런 변수들을 캐시라인 소비를 최소화하는데 초점을 맞춰 재배치했고 결과적으로 동시에 연결된 네트워크 커넥션이 많을 경우 40%가량 성능을 향상했습니다.(인텔에서는 큰 효과가 있진 않았습니다.) 즉 구조체에서 자주 함께 사용되는 멤버 변수를 코드상에 함께 위치시켜 메모리상에서도 연속되도록 했고 이를 통해 캐시 적중률을 높였습니다. 높아진 캐시 적중률은 동시에 연결된 커넥션이 많을 때 더 높은 성능 차이를 보입니다.
이참에 다시 알아보는 Data Structure Alignment
CPU는 워드(word) 단위로 메모리에서 데이터를 읽고 씁니다. 워드는 32-bit 아키텍처의 경우 32bits(4 bytes), 64-bit 아키텍쳐의 경우 64bits(8bytes)입니다. 만약 4bytes 데이터가 워드에 딱 맞게 저장되어 있다면 데이터를 읽기 위해 한 개의 워드만 읽으면 됩니다. 하지만 워드의 경계에 걸쳐 저장되어 있다면 두 개의 워드를 읽어야 하는 비효율이 발생합니다.
Data Structure Alignment는 CPU가 효율적으로 메모리 접근할 수 있도록 데이터를 정렬하는 규칙 혹은 방식입니다. 일반적으로 이 규칙은 "n(n은 2의 거듭제곱) 바이트 데이터는 k*n 메모리 주소에 저장한다."입니다. 즉, 데이터의 크기에 배수인 메모리 주소에 데이터를 저장하는 방식입니다.
컴파일러는 이러한 규칙에 맞게 구조체를 저장하기 위해 데이터 사이에 패딩(padding)을 추가합니다. 따라서 구조체의 실제 메모리 크기가 예상과 다를 수 있습니다. 구조체의 멤버 변수의 순서에 따라 구조체의 크기가 달라지고 캐시 라인 적재도 달라져 성능에도 영향을 미칩니다.
컴파일러가 적용하는 규칙은 다음과 같습니다.
- 구조체의 크기는 가장 큰 멤버 타입 크기의 배수다.
- 멤버 변수는 해당 타입 크기의 배수 메모리 주소에 할당된다.
예시를 통해 규칙을 살펴보겠습니다.
#include <iostream>
using namespace std;
struct s0 {
char c1a;
int i4;
char c1b;
};
struct s1 {
int i4;
char c1a;
char c1b;
};
int main() {
cout << sizeof(s0) << '\n'; // 12 (bytes)
cout << sizeof(s1) << '\n'; // 8 (bytes)
}
같은 내용의 두 개의 구조체 `s0`와 `s1`을 만들었지만 둘의 크기는 각각 12 bytes와 8 bytes로 차이 납니다. 컴파일러는 위 규칙에 맞도록 저장하기 위해서는 데이터 사이에 패딩을 넣습니다. 따라서 실제 저장되는 모습은 아래와 같을 것입니다.
마무리
저는 구조체에서 변수의 위치를 주로 논리적인 묶음으로 배치했습니다. 이러한 방식은 가독성에서는 이점이 있을 수 있으나 성능이 중요한 프로그램에서는 좋은 선택이 아닐 수 있습니다. 단순히 구조체를 재배치하는 것으로 40%의 성능 향상을 기대할 수 있다는 사실에 놀랐고 사소한 것이 모여 큰 효과를 낼 수 있다는 것을 항상 생각하며 작은 것도 집요하게 고려해야겠다는 다짐을 하게 됐습니다.
'Computer Science > etc.' 카테고리의 다른 글
모듈러(Modular)와 C, Java, Python, Javascript의 나머지 연산자 (0) | 2023.09.08 |
---|---|
의존성 주입(Dependency Injection) (0) | 2023.08.08 |
[Design Pattern] 싱글톤 패턴(Singleton Pattern) (0) | 2023.08.02 |