본문 바로가기
C.W.K.
Stream
Lesson 04 of 06 · published

GPU가 그중 얼마를 쓸 수 있나

~14 min · memory, working-set, wired-limit, iogpu, mlx, measured

Level 0스펙 시트 훑는 사람
0 XP0/91 lessons0/19 achievements
0/100 XP to next level100 XP to go0% complete
"GPU는 풀을 소유하지 않아. 몫을 허락받는 거고, 그 몫은 네가 읽을 수 있는 숫자야."

풀 하나, 심판 하나

GPU엔 자기 메모리가 없으니, GPU가 쓰는 모든 바이트는 기계의 나머지가 못 쓰는 바이트야. 그래서 macOS는 장치마다 권장 최대 작업 집합을 공개해. 애플 말로는 "이 GPU 장치가 런타임 성능에 영향 없이 할당할 수 있는 메모리 바이트 수의 근사치". 그 권장치가 물리 메모리의 몇 퍼센트인지는 공식으로 문서화돼 있지 않고, 제일 자주 인용되는 수치인 '약 75%'는 곡선 위의 한 점으로 밝혀져. 코드 블록이 함대 맥마다 그 숫자를 읽어.

별칭물리권장 작업 집합비율MLX 기본 메모리 한도iogpu.wired_limit_mb증거
air24 GiB17.8 GiB74.0%22.8 GiB (95%)0실측 2026-09-15
pro2023128 GiB107.5 GiB84.0%0실측
music192 GiB161.3 GiB84.0%0실측
office512 GiB464.0 GiB90.6%486.4 GiB (95%)0실측

모양이 어떤 경험칙보다 분명해. 운영체제는 작은 풀에 비해선 크고 큰 풀에 비해선 작은 예약분을 남겨. Air에서 6.2 GiB, 128 GB MacBook Pro에서 20.5, 192 GB Studio 둘에서 30.7, office에서 48. 그게 고정 공식인지 커널 안의 표인지는 사용자 공간에서 알 수 없어. 사용자 공간이 알 수 있는 건 숫자고, 계획은 그 숫자에 맞춰야 해. 이 시리즈의 이웃 퀘스트는 512 GB M3 Ultra의 '실질 천장이 380 GB에 가깝다'고 인용해. macOS 26.6.2에서 장치 자체는 464 GiB를 보고하고, 이 퀘스트는 읽은 걸 보고해. 들어가는지 결정(앞 레슨)과 디코드 예측은 스티커가 아니라 이 칸을 써.

손잡이 둘, 어느 것도 울타리는 아니야

iogpu.wired_limit_mb는 GPU가 풀에서 얼마나 wire할 수 있는지, 그러니까 페이지 아웃되지 않게 고정할 수 있는지 상한을 두는 커널 sysctl이야. 모든 함대 맥에서 0이고, 커널이 정한다는 뜻이야. (루트로) 값을 주면 GPU 프로세스가 더 많이 wire할 수 있고, 문서화된 데는 mlx-lm README뿐인데 거긴 "모델 크기(메가바이트)보다 크고 기계 메모리 크기보다 작은" 값을 권해. MLX 자체 set_memory_limit은 프로세스 쪽 손잡이고, 읽어본 두 기계 모두에서 기본값이 물리 메모리의 95%야. 운영체제 권장치보다 위. 어느 손잡이도 남한테 울타리를 안 쳐. wired 한도를 올리면 기계의 나머지한테서 메모리를 가져가고, 권장을 무시하는 프로세스는 디스플레이한테서 가져가. GPU 트랙은 이걸 규율이라 불렀고, 이 레슨은 산수라 불러. 몫은 권장이지 강제가 아니고, 네가 고른 런타임이 권장을 지킬지 정해.

이게 함대에 사주는 것

이 집의 추론 허브는 임베딩 모델 하나, 리랭커 둘, 채팅 모델 하나를 512 GB 풀 하나 위에서 권장치 아래로 잡은 메모리 가드와 함께 나란히 돌리고, 사고 없이 그래 왔어. 같은 숫자가 Air가 호스트가 아니라 증인인 이유도 설명해. GPU 몫 17.8 GiB에 16 GB 모델이면 자기 컨텍스트에 남는 게 없고, 운영체제는 짧은 실행 하나를 되게 하려고 다른 메모리 1.7 GB를 스왑으로 밀어냈어.

Code

gpu_share.py — 닿을 수 있는 모든 맥의 GPU 권장 몫·python
#!/usr/bin/env python3
"""Read physical memory, the GPU's recommended working set and the wired-limit
sysctl on each ssh alias. The fraction is not a constant; look at the reserve."""
import subprocess
import sys

ALIASES = sys.argv[1:] or ["air", "pro2023", "music", "office"]
PY = ("~/miniconda3/envs/silicon-lab/bin/python -c "
      "'import mlx.core as mx; i=mx.device_info(); print(i[\"memory_size\"], i[\"max_recommended_working_set_size\"])'")

print(f"{'alias':8} {'physical GiB':>12} {'recommended GiB':>15} {'fraction':>9} {'reserve GiB':>12} {'wired_limit_mb':>14}")
for alias in ALIASES:
    out = subprocess.run(["ssh", alias, PY + "; sysctl -n iogpu.wired_limit_mb"], capture_output=True, text=True).stdout.split()
    phys, rec, wired = int(out[0]), int(out[1]), out[2]
    print(f"{alias:8} {phys/2**30:12.0f} {rec/2**30:15.1f} {rec/phys:9.1%} {(phys-rec)/2**30:12.1f} {wired:>14}")

# 2026-09-15, macOS 26.6.2:
# air        24    17.8   74.0%    6.2   0
# pro2023   128   107.5   84.0%   20.5   0
# music     192   161.3   84.0%   30.7   0
# office    512   464.0   90.6%   48.0   0
MLX 없이 셸에서 같은 숫자·bash
sysctl iogpu.wired_limit_mb          # 0 = the kernel decides the GPU's wired ceiling
sysctl hw.memsize                    # physical bytes

# recommendedMaxWorkingSetSize is a Metal device property; MLX exposes it as
# mx.device_info()['max_recommended_working_set_size']. In Swift:
#   MTLCreateSystemDefaultDevice()!.recommendedMaxWorkingSetSize

# mlx-lm README: to let the GPU wire more of the pool (root, moves the fence, adds none)
# sudo sysctl iogpu.wired_limit_mb=N   # N > model MB, N < machine memory MB

External links

Exercise

네 맥에서 gpu_share.py(아니면 셸 블록)를 돌리고 권장 작업 집합, 그 비율, 예약분(GiB)을 카드에 추가해. 그다음 앞 레슨의 피크 표에서 네 맥이 권장 안에서 붙들 수 있는 제일 큰 모델-컨텍스트 짝과, wired 한도를 올려야 하는 첫 번째 짝을 말해. 올리는 게 기계의 나머지한테 뭘 치르게 하는지 한 문장으로 써.
Hint
예약분은 디스플레이, 커널, 그리고 다른 모든 프로세스가 나눠 쓰는 거야. wired 한도를 거기까지 올려도 메모리가 생기진 않아. 부족분을 모델에서 윈도우 서버로 옮길 뿐이고, 그건 네가 제일 굶기고 싶지 않은 프로세스야.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.