라즈베리파이 메모리 성능 개선하기
CircleSync의 백엔드 서버는 쿠버네티스 클러스터로 구성된 4대의 라즈베리파이5에서 동작하는데 서비스를 정식으로 배포하기 전에 안정적으로 운영하기 위해서 각 노드들의 성능을 점검하고 업그레이드할 필요가 있었어요. 더군다나 CircleSync의 백엔드 서버는 gRPC로 통신하는 분산된 마이크로서비스로 구성되어 있고 쿠버네티스 워크로드에서 동작하지만 일부 마이크로서비스는 격리된 환경에서 테라폼 명령을 수행하기 위해 도커 컴포즈를 사용하여 생성한 컨테이너는 쿠버네티스 워크로드와 독립되어 쿠버네티스 DNS 질의로 통신이 불가능하기 때문에 직접 노드를 통해서 통신해야 했고 컨테이너는 노드의 커널을 공유하기 때문에 메모리 대역폭이 전체 시스템 성능에 직접적인 영향을 미치게 되어요.
특히 테라폼 작업과 같은 리소스 집약적인 태스크를 수행하는 도커 컨테이너와 쿠버네티스 워크로드가 동일한 노드에서 실행된다면 메모리 대역폭 병목 현상이 시스템 전반의 성능 이슈로 이어질 수 있다고 생각했습니다.
그렇다면 다른 사람들은 어떻게 라즈베리파이의 성능을 개선했을까요?
- Raspberry Pi 엔지니어들이 SDRAM 타이밍과 메모리 설정을 조정하여 기본 2.4GHz 클럭에서 10-20%의 속도 향상을 이룸
- 오버클러킹을 통해 3.2GHz에서 32%의 성능 향상을 달성
SDRAM 설정과 오버클러킹을 통해서 성능을 개선한 사례들이 보이는데 본문에서는 오버클러킹을 제외하고 SDRAM 설정을 통해서 어떻게 라즈베리파이의 성능을 올렸고 어떤 원리로 개선이 되었는가에 대해서 초점을 맞춥니다.
메모리의 대역폭을 높이는 방법에는 아래 세 가지가 있어요.
메모리의 대역폭을 높이는 방법
- 메모리의 버스 폭(width)를 늘리는 방법
- 메모리의 operating_clock을 높이는 방법
- 메모리의 interleaving을 적용하여 대역폭을 높이는 방법
SDRAM 설정 변경으로 메모리 대역폭을 개선하는 방법은 메모리의 interleaving을 적용하여 대역폭을 높이는 방법과 동일한 방법이에요.
Bank Interleaving의 목적
- 연속된 메모리 주소들을 다른 뱅크에 분산
- 여러 뱅크를 병렬로 접근 가능하게 함
- 메모리 액세스 레이턴시를 숨기고 실효 대역폭을 증가
즉, 메모리에 interleaving을 적용하여 대역폭을 높이는 방법은 순차적 메모리 접근이 많은 워크로드에서 더 높은 성능 향상을 기대할 수 있게 돼요.
interleaving 방식에는 크게 Bank Sequential Access
방식과 Bank Interleaving
방식이 있어요.
Bank Sequential Access
Bank Sequential Access 방식은 각 뱅크에서 row buffer를 통해 접근할 수 있는 데이터는 한번에 한 row만 가능하며 각 뱅크 내에서 다른 row로 이동할 때마다 precharge가 필요하게 됩니다. 아래 그림을 보며 동작 플로우를 따라가다보면 충분히 이해가 됩니다.
- 메모리 주소가 순차적으로 같은 뱅크에 할당
- Bank 동작:
Bank 0(row0)
: 0 ~ 3- precharge
Bank 0(row1)
: 4 ~ 7- precharge
Bank 1(row0)
: 8 ~ 11- precharge
Bank 1(row1)
: 12 ~ 15- precharge
Bank 2(row0)
: 16 ~ 19- precharge
Bank 2(row1)
: 20 ~ 23- precharge
Bank 3(row0)
: 24 ~ 27- precharge
Bank 3(row1)
: 28 ~ 31
Bank Sequential Access에서는 동일한 뱅크 내에서 다른 row로 이동할때마다 precharge가 필요하게 되므로 0 ~ 31번 데이터에 접근할 때 총 8번의 precharge 시간이 소요되었고 데이터 접근시 한 번에 하나의 뱅크만 활성화가 됩니다.
Bank Interleaving
Bank Interleaving 방식은 연속된 메모리 주소가 다른 뱅크에 분산 할당되며 가지고 있는 모든 뱅크에 병렬 실행이 가능해요.
- 연속된 메모리 주소가 다른 뱅크에 분산 할당
- Bank 동작:
- 병렬 - [row0 -
Bank 0 ~ 3
]: 0, 1, 2, 3, .... 15 동시 읽기 - Bank 0 ~ 3까지 한번에 precharge 발생
- 병렬 - [row1 -
Bank 0 ~ 3
]: 16, 17, 18, 19, ... 31 동시 읽기
- 병렬 - [row0 -
Bank 동작을 볼 때 총 4번의 precharge가 발생하였고 데이터 병렬 접근으로 Bank Sequential Access 방식보다 더 빠르게 더 많은 데이터를 읽었지만 짧은 시간에 더 많은 전력을 소비하여 명확하게 성능과 전력 소비가 트레이드 오프 되었습니다.
여기까지 Bank Interleaving으로 성능을 개선하는 원리에 대해서 알아보았어요. 라즈베리파이에서는 Bank Interleaving를 적용하기 위해서 /boot/firmware/config.txt
파일에 SDRAM_BANKROW=1
를 추가하는데 이 설정은 다음과 같은
의미를 가집니다.
- SDRAM(메모리)의 낮은 뱅크 주소 지정 방식으로 변경
- 메모리 접근 패턴을 최적화하여 특정 상황에서 성능을 개선
- 일부 라즈베리파이 모델에서 메모리 관련 안정성 문제 해결
설정을 적용하는 방법은 다음과 같아요.
$ sudo apt update
$ sudo apt full-upgrade
$ sudo rpi-eeprom-update
BOOTLOADER: up to date
CURRENT: Fri Feb 16 15:28:41 UTC 2024 (1708097321)
LATEST: Wed Dec 6 18:29:25 UTC 2023 (1701887365)
RELEASE: default (/lib/firmware/raspberrypi/bootloader-2712/default)
Use raspi-config to change the release.
# 파일 끝에 추가
$ sudo vi /boot/firmware/config.txt
SDRAM_BANKLOW=1
$ sudo -E rpi-eeprom-config --edit
SDRAM_BANKLOW=1
$ sudo reboot
$ vcgencmd bootloader_config
[all]
BOOT_UART=1
POWER_OFF_ON_HALT=0
BOOT_ORDER=0xf461
SDRAM_BANKLOW=1
$ sysbench memory run
Bank Interleaving과 전력 소비는 명확한 trade-off 관계에 있으므로 현재 운영하는 워크로드의 특성을 고려하여 적용하면 되겠네요!