Dirty threshold와 writeback 주기를 구분해서 읽기

Memory에 수정된 file data가 많으면 writeback이 필요합니다. 하지만 “얼마나 많이 수정되었을 때 쓰는가”와 “얼마나 오래 지난 data를 주기적으로 쓰는가”는 다른 정책입니다. 두 설정의 단위를 섞으면 의도와 전혀 다른 값을 적용할 수 있습니다.

Linux v6.12를 기준으로 dirty 관련 sysctl과 cache·통계 관련 설정을 살펴보겠습니다. 여기서는 동작의 의미를 설명하며 실제 시스템의 값을 변경하지 않습니다.

Background threshold와 writer throttling은 다릅니다

dirty_background_bytes와 dirty_background_ratio는 background writeback을 고려할 기준입니다. dirty_bytes와 dirty_ratio는 더 높은 dirty 상태에서 writer를 throttling하는 기준과 연결됩니다. 두 쌍이 모두 단순히 같은 writeback 시점을 설정하는 것은 아닙니다.

bytes는 고정 크기, ratio는 dirtyable memory에 대한 비율입니다. Dirtyable memory는 설치된 RAM 전체와 같지 않습니다. Free memory, file pages와 reserve 등의 계산이 관여하고, writeback domain과 backing device의 상태도 실제 throttling에 영향을 줍니다.

각 쌍에서 bytes와 ratio는 동시에 독립적인 두 limit을 만드는 방식이 아닙니다. 한쪽을 설정하면 대응하는 다른 쪽은 0으로 바뀝니다. 설정 파일에 둘을 연달아 적었을 때 적용 순서가 의미를 가질 수 있습니다.

Dirty threshold를 낮춘다고 쓰기 지연이 언제나 줄어드는 것은 아닙니다. 더 이른 writeback으로 작은 I/O가 늘거나, 낮은 foreground 기준 때문에 writer가 더 자주 지연될 수 있습니다. 느린 장치에서 threshold를 크게 하면 일시적인 burst는 더 흡수할 수 있어도, 이후 긴 stall이나 crash 때 잃을 수 있는 변경분의 규모가 달라질 수 있습니다.

centisecs는 100분의 1초입니다

dirty_expire_centisecs는 dirty data를 periodic writeback 대상으로 볼 나이와 연결됩니다. dirty_writeback_centisecs는 periodic writeback을 깨우는 간격과 연결됩니다. 나이가 어리다고 어떤 상황에서도 writeback되지 않는 것은 아니고, 나이가 지났다고 정확히 그 순간 저장이 완료되는 것도 아닙니다.

단위는 centiseconds이므로 100이 1초입니다. Linux v6.12의 일반적인 초기값인 3,000은 30초, 500은 5초입니다. 30,000을 30초로 읽거나 5,000을 5초로 읽으면 10배가 어긋납니다. 실제 배포판 설정은 별도로 확인해야 합니다.

dirty_writeback_centisecs를 0으로 두는 경우의 periodic 동작도 명시적인 정책 변화입니다. 이것을 모든 writeback과 동기화가 영구적으로 금지된다는 뜻으로 읽어서는 안 됩니다. Threshold에 의한 동작과 application이 요청하는 동기화는 별개의 경로입니다.

drop_caches는 평상시 memory 확보 요령이 아닙니다

drop_caches는 test를 위해 cache 상태를 바꿀 때 사용하는 인터페이스입니다. 값 1은 page cache, 값 2는 reclaimable slab 쪽 동작에 연결되고 3은 둘을 함께 요청합니다. Dirty data를 버려서 저장을 생략하는 명령이 아닙니다. In-use object나 여러 제약 때문에 요청한 cache가 언제나 전량 사라지는 것도 아닙니다.

Cold-cache test를 수행하려면 남아 있는 dirty data와 동기화 여부가 결과에 영향을 줄 수 있습니다. 반대로 평상시에 cache를 반복해서 지우면 다시 읽을 I/O와 cache를 재구성할 비용이 생깁니다. buff/cache를 작게 보이게 만드는 것 자체가 성능 개선의 목표는 아닙니다.

값 4의 bit는 kernel log 메시지 억제와 관련됩니다. 이것을 추가 cache 종류를 회수하는 option으로 읽으면 안 됩니다. 숫자가 bitmask라는 사실과 각 bit의 의미를 구분해야 합니다.

PCP 설정은 cache와 global free list의 균형입니다

percpu_pagelist_high_fraction은 PCP의 high 수준을 정하는 데 관여하는 입력입니다. 일반적으로 분모 형태의 값이므로 숫자를 올리면 CPU cache도 같은 비율로 커진다는 뜻이 아닙니다. 0은 kernel의 자동 조정 경로를 사용한다는 의미로 읽어야 합니다.

PCP를 유지하면 공유 free list에 접근하는 비용을 줄일 수 있지만, memory가 CPU별 cache에 분산되는 양도 달라집니다. CPU 수와 zone 크기, allocation 패턴과 동적인 batch 조정을 함께 보아야 합니다. 오래된 version의 한 가지 나눗셈 공식을 현재 모든 구현의 고정 규칙으로 쓰면 안 됩니다.

Linux v6.12의 zone_highsize()에서는 수동 fraction이 있으면 우선 zone managed pages / fraction을 계산하고, 이를 그 zone의 node에 있는 online CPUs에 나눕니다. CPU가 없는 memory node라면 전체 online CPUs를 사용하고 CPU hotplug 조건도 반영합니다. 마지막에는 적어도 batch * 4가 되도록 제한합니다. 기본 fraction 0은 low watermark 기반의 최소 high와 별도의 최대 high를 구성하며 동적 조정에 사용합니다. 따라서 모든 node의 PCP에 동일한 RAM / 전체 CPU 수가 적용된다고 설명하면 안 됩니다. Linux v6.12 mm/page_alloc.c

통계 갱신 간격도 관찰 결과에 영향을 줍니다

stat_interval은 per-CPU VM 통계를 global 상태로 모으는 periodic 동작의 간격에 관여합니다. stat_refresh는 관련 통계 refresh를 요청할 때 사용합니다. 이 경로는 통계의 갱신에 관한 것으로, process의 모든 page table을 고정하고 조사하는 smaps와 같은 작업은 아닙니다.

stat_interval의 userspace 단위는 seconds이며 Linux v6.12의 기본값은 1초입니다. 앞의 dirty 설정의 centiseconds와 섞지 않아야 합니다. 또한 stat_refresh는 root의 read나 write 모두가 per-CPU 통계 flush를 유발하는 trigger입니다. 일반적인 상태 파일처럼 읽기만 했으니 갱신 작업을 요청하지 않았다고 가정하면 안 됩니다.

통계를 더 자주 모으면 관찰의 지연은 달라질 수 있지만 갱신 비용도 생깁니다. Counter가 어떤 범위와 단위를 나타내는지 모른 채 refresh만 반복해서는 잘못된 해석이 해결되지 않습니다. 여러 파일을 읽는 사이 allocation과 reclaim이 계속된다는 점도 남습니다.

설정을 바꾸기 전에 실제로 필요한 것이 writeback의 양 조절인지, 주기 조절인지, cold-cache test인지, 통계 관찰의 지연 문제인지부터 나누어 보시면 됩니다. 이름이 비슷한 항목을 한 묶음의 “memory 최적화값”으로 다루지 않는 것이 중요합니다.

참고 자료