TECH 으로 돌아가기
TECH HACKER NEWS 오늘 6분 읽기 23 READS

디스크와 네트워크를 100% 쥐어짜기: 하드웨어 한계까지 밀어붙인 실험

디스크와 네트워크를 100% 쥐어짜기: 하드웨어 한계까지 밀어붙인 실험

하드웨어를 100% 쥐어짜기, 생각보다 어렵습니다

요즘 서버 하드웨어는 정말 빨라요. NVMe SSD 하나가 초당 몇 기가바이트씩 읽고 쓰고, 네트워크 카드(NIC, Network Interface Card·서버를 네트워크에 연결해주는 부품)는 100기가비트, 그러니까 초당 12기가바이트가 넘는 데이터를 주고받을 수 있죠. 그런데 막상 코드를 짜서 돌려보면 이 하드웨어가 가진 성능의 절반도 못 쓰는 경우가 허다해요. 분명 디스크는 더 빨리 읽을 수 있는데 내 프로그램은 그 한계 근처에도 못 가는 거죠. 이번 글의 출처가 된 실험은 바로 그 한계까지 밀어붙여서, 디스크와 네트워크 대역폭을 동시에 꽉 채우는 데 성공한 이야기예요.

제목이 'Task Failed Successfully(작업이 성공적으로 실패했습니다)'인 게 재밌는데요. 어떤 작업을 빠르게 만들려다가 결국 하드웨어가 낼 수 있는 최대치에 부딪혀버린 상황을 이렇게 표현한 거예요. 더 이상 빨라지지 않는 이유가 내 코드가 느려서가 아니라 '디스크와 랜카드가 더는 못 버텨서'라면, 그건 엔지니어 입장에선 가장 기분 좋은 '실패'거든요.

왜 하드웨어 성능을 다 못 쓸까

보통 우리가 파일을 읽을 때 쓰는 read() 같은 함수는 한 번 호출할 때마다 운영체제 커널로 진입했다 나오는 비용(시스템 콜 오버헤드)이 들어요. 작은 조각을 수십만 번 읽으면 이 왕복 비용만으로도 CPU가 다 타버리죠. 또 데이터가 디스크 → 커널의 페이지 캐시 → 내 프로그램 메모리로 옮겨지면서 똑같은 데이터를 메모리에 여러 번 베껴 쓰는 일이 생겨요. 이걸 '메모리 카피' 비용이라고 하는데, 대역폭이 클수록 이 복사가 발목을 잡아요. 거기다 한 개의 스레드로만 처리하면 CPU 코어 하나만 일하고 나머지는 놀아서 하드웨어를 다 못 채우고요.

한계까지 밀어붙이는 기술들

그래서 고성능 스토리지 시스템들은 몇 가지 무기를 씁니다. 첫째는 io_uring 같은 비동기 I/O인데요, 이게 뭐냐면 요청을 하나씩 커널에 부탁하는 대신 '할 일 목록'을 큐에 잔뜩 쌓아두면 커널이 알아서 한꺼번에 처리해주는 방식이에요. 시스템 콜 왕복 횟수가 확 줄죠. 둘째는 다이렉트 I/O(O_DIRECT)예요. 커널 페이지 캐시를 거치지 않고 디스크에서 내 메모리로 곧장 데이터를 가져오는 건데, 중간 복사를 건너뛰니까 대역폭을 훨씬 잘 살려요. 셋째는 배칭과 정렬이에요. 데이터를 디스크가 좋아하는 크기(보통 4KB 배수)로 큼직하게, 그리고 메모리 주소를 잘 맞춰서 한 번에 많이 읽는 거죠. 넷째는 멀티 큐·멀티 스레드로, 여러 코어가 동시에 I/O를 날려서 NVMe가 가진 여러 개의 내부 큐를 전부 활용하는 거예요.

네트워크 쪽도 비슷해요. 작은 패킷을 잘게 보내면 헤더 오버헤드와 인터럽트 비용에 잡아먹히니까, 큰 덩어리로 묶어 보내고 가능하면 메모리 복사 없이 보내는 제로카피 기법을 씁니다. 이렇게 디스크와 네트워크 양쪽을 동시에 풀가동하면, 캐시 시스템이나 분산 스토리지처럼 '데이터를 받아 디스크에 쓰고 다시 네트워크로 내보내는' 파이프라인이 하드웨어 한계 속도로 돌아가게 되는 거예요.

한국 개발자에게

이런 저수준 최적화는 평소엔 신경 쓸 일이 별로 없지만, 캐시 서버, 로그 수집기, 데이터 파이프라인, 스토리지 엔진처럼 '데이터를 대량으로 흘려보내는' 시스템을 만드는 순간 바로 현실이 돼요. 예를 들어 같은 NVMe인데 왜 우리 서비스는 벤치마크의 30%밖에 안 나오나 싶을 때, 십중팔구 시스템 콜 횟수나 불필요한 메모리 복사, 단일 스레드 처리가 범인이거든요. 리눅스라면 fio로 디스크의 진짜 한계를 먼저 재보고, iostat·iftop으로 디스크와 네트워크 사용률을 같이 보면서 '지금 병목이 CPU냐, 디스크냐, 네트워크냐'를 구분하는 습관을 들이면 좋아요.

핵심 한 줄: 느린 게 내 코드 탓인지 하드웨어 한계인지 구분할 줄 아는 게 고성능 엔지니어링의 시작이에요. 여러분은 마지막으로 디스크나 네트워크를 진짜 100%까지 채워본 적 있으세요? 그때 병목은 어디였나요?


🔗 출처: Hacker News

SOURCE · HACKER NEWS
원문 전체 보기 → https://blog.mrcroxx.com/posts/task-failed-successfully-satu...
SHARE
처리 중...