2P by GN⁺ 17시간전 | ★ favorite | 댓글 1개
  • MacThrottle은 Mac이 과열로 인해 성능을 제한할 때 이를 메뉴 막대에서 시각적으로 알려주는 SwiftUI 기반 앱 - 오픈소스
  • macOS의 ProcessInfo.thermalState API와 powermetrics 명령어를 비교하며, 시스템의 실제 열 상태를 정확히 감지하기 위한 방법을 탐색
  • 최종적으로 thermald가 Darwin의 notifyd 시스템에 게시하는 알림을 활용해 루트 권한 없이 열 상태를 읽는 방식 구현
  • 앱은 온도·팬 속도 그래프, 상태별 색상 아이콘, macOS 알림 기능을 포함하며, 로그인 시 자동 실행도 지원
  • Apple Silicon Mac의 열 관리 상태를 실시간으로 파악할 수 있는 도구로, 개발자와 파워유저에게 유용한 진단 수단 제공

Mac의 열 스로틀링 문제 인식

  • M2 MacBook Air에서 외부 4K 120Hz 디스플레이 사용 시 성능 저하와 반응 지연이 발생
    • 팬이 없어 소음을 감지할 수 없지만, CPU 사용률이 100%인 상태에서 전력 사용량이 감소함
  • iStat Menus와 MX Power Gadget을 통해 CPU 주파수와 전력 하락을 확인하며 열 스로틀링을 진단
  • M4 Max MacBook Pro에서도 동일 현상 발생, 14인치 모델의 열 설계 한계로 인한 문제로 언급
  • Apple Silicon의 전력 효율은 여전히 높지만, 열 상태를 직접 감지할 방법을 찾고자 함

macOS에서 열 상태를 프로그래밍적으로 확인하기

  • macOS는 여러 방식으로 열 상태를 노출하지만 일관성이 부족
  • Apple이 권장하는 방법은 FoundationProcessInfo.thermalState 사용
    • 출력 예시: nominal, fair, serious, critical
  • 루트 권한이 필요한 powermetrics -s thermal 명령어도 동일한 정보를 제공하지만,
    두 방식의 상태 구분 단위가 다름
    • 예: fairpowermetricsmoderateheavy 두 상태를 모두 포함
  • 실제 스로틀링 시점은 powermetrics에서 heavy로 표시되지만, ProcessInfo에서는 구분 불가

thermald와 Darwin 알림 시스템 활용

  • powermetrics의 데이터는 thermald 데몬에서 가져오며,
    thermald는 현재 열 압력 상태를 notifyd 시스템 이벤트로 게시
  • notifyutil -g com.apple.system.thermalpressurelevel 명령으로 상태 확인 가능
  • OSThermalNotification.h 헤더에서 정의된 열 압력 수준:
    • nominal, moderate, heavy, trapping, sleeping
  • Swift 코드로 notify_register_checknotify_get_state를 호출해
    루트 권한 없이 실시간 열 상태를 읽는 기능 구현

MacThrottle 앱 개발

  • SwiftUI와 MenuBarExtra를 사용해 메뉴 막대 전용 앱 제작
    • 온도계 아이콘 색상으로 상태 표시 (녹색→적색)
    • Info.plistLSUIElementtrue로 설정해 Dock 아이콘 비활성화

초기 접근: powermetrics 루트 헬퍼

  • 초기에 루트 권한이 필요한 powermetrics를 사용하기 위해
    LaunchDaemon과 bash 스크립트로 헬퍼 프로세스 구성
    • /usr/local/bin/mac-throttle-thermal-monitor가 10초마다 상태를 /tmp 파일에 기록
    • 앱은 해당 파일을 주기적으로 읽어 표시

개선: thermald IPC 알림 사용

  • notifyd 이벤트를 직접 구독하는 방식으로 전환
    • 루트 권한 불필요, 코드 단순화

온도 및 팬 속도 표시

  • CPU/GPU 온도와 팬 속도를 그래프로 표시
  • 초기에는 IOKit 비공개 API 사용 시 온도가 실제보다 낮게 표시됨 (~80°C)
  • 오픈소스 Stats 프로젝트를 참고해 SMC 인터페이스로 전환
    • SoC 세대별로 다른 키(Tp0D, Tf0E 등)를 사용해야 함
  • SMC가 작동하지 않을 경우 IOKit으로 폴백

메뉴 막대 그래프 구현

  • 그래프는 3가지 정보를 동시에 표시
    • 배경 색상: 열 상태 (녹색~적색)
    • 실선: CPU 온도
    • 점선: 팬 속도 비율
  • 2초 간격으로 데이터 수집, 10분 단위 히스토리 유지
  • onContinuousHover로 툴팁 제공,
    .drawingGroup을 추가해 GPU 렌더링으로 120Hz 디스플레이에서도 부드럽게 표시

macOS 알림 및 자동 실행

  • 열 상태 변화 시 알림 전송 기능 추가
    • 특정 상태 전환이나 복구 시 알림 가능
  • SMAppService API로 로그인 시 자동 실행 설정 지원
    • register() / unregister() / status 메서드로 제어

배포 및 사용

  • Apple Developer 계정이 없어 공식 공증(notarization) 불가
    • GitHub 릴리스에서 설치 시 Privacy and Security에서 수동 승인 필요
    • 일부 Mac에서는 Xcode로 직접 빌드해야 실행 가능
  • 설치 및 빌드 방법은 GitHub README에 명시

결론

  • MacThrottle은 Apple Silicon Mac의 열 스로틀링 상태를 실시간 감시할 수 있는 경량 도구
  • 루트 권한 없이 동작하며, 시각적 피드백·알림·그래프 기능을 통해
    개발자와 고성능 작업 사용자에게 시스템 열 상태 인식을 제공
Hacker News 의견들
  • 2019년형 MacBook Pro i9을 썼는데, 열 스로틀링 감지 함수는 이렇게 간단히 쓸 수 있을 것 같음

    function isThermalThrottling() {
      return true;
    }
    

    농담이지만, 고가의 i9 CPU를 샀는데 i7보다 성능이 떨어지는 게 꽤 실망스러웠음

    • 나도 같은 모델을 쓰는데, 오른쪽 포트로 충전해야만 문제를 해결할 수 있었음
      이유는 모르겠지만 그렇게 하니 스로틀링이 사라졌음
      그래도 macOSLogic 기반 워크플로우가 익숙해서 계속 쓰고 있음
      리눅스로 옮길 수도 있겠지만, 지금은 꽤 쓸 만한 머신임
    • 나도 2019 i9 모델을 썼는데, VRM 모듈에 써멀 패드를 붙이니 완전히 새 컴퓨터처럼 변했음
      두 개의 외부 모니터와 Adobe Creative Suite를 쓸 때마다 스로틀링이 심했는데, 이 패드로 해결됨
      단점은 하판이 뜨거워져 무릎 위에 두기 어렵다는 점이지만, 전혀 후회하지 않음
      지금은 M3 MacBook Air(24GB RAM) 로 바꿨고 매우 만족 중임
      아직 2019 모델을 쓰는 사람이라면 꼭 VRM 써멀 패드 개조를 고려해보길 추천함
    • 사실 i9 자체가 노트북용으로 부적합한 CPU였다고 생각함
      Dell에서도 i9을 i7으로 바꾸자마자 훨씬 나아졌음
      “큰 숫자 = 더 좋은 CPU”라는 마케팅에 속은 셈임
    • 나도 같은 문제를 겪었음, 특히 외부 모니터 두 대를 연결했을 때 심했음
      이후 M1 Max로 바꾸자 완전히 다른 세상이었음
      지금은 M3 Max로 업그레이드했고, Apple Silicon은 오래 버틸 것 같음
    • 그 노트북은 내가 써본 최악의 컴퓨터였음
      부팅하자마자 팬이 돌고, Thunderbolt 장치 연결 시 커널 패닉도 자주 발생했음
      지금 쓰는 M1 Max MBP는 완벽하게 안정적임
  • 프로젝트가 꽤 괜찮아 보임
    다만 macOS에서 개발이 점점 어려워지고 있는데, 스로틀링을 감지해도 실제로 무엇을 할 수 있을지는 의문임
    팬 속도 조정도 안 되고, 언더볼팅도 불가능하지 않나?

    • 나 같은 경우 iStat Menus로 팬 커브를 직접 조정했음
      기본 커브가 너무 느려서 스로틀링이 먼저 발생했거든
      Apple Silicon에서는 High Power Mode를 쓰면 팬이 더 빠르게 돌아감
      지금은 커스텀 커브를 안 쓰지만, 14" M4 Max에서는 꽤 시끄러움
      MacBook Air는 팬이 없으니 그냥 열을 식히는 수밖에 없음
    • 나는 Macs Fan Control을 써서 CPU 온도에 따라 팬 RPM을 조절함
      기본 설정으로는 90도 이상 올라가서, 더 보수적으로 세팅했음
      GitHub 링크
    • 이런 앱이 꼭 필요했는데 이제 생겼음
      가끔 프로세스가 폭주해서 스로틀링이 생기는데, 그걸 알아차리기 전까지 배터리가 반쯤 닳아 있음
    • 결국 할 수 있는 건 앱을 종료하거나 잠시 쉬는 것뿐임
      백그라운드에서 돌아가는 앱이 많을 때 이런 문제가 자주 생김
  • Homebrew에 넣으면 무료로 코드 서명과 공증을 받을 수 있음
    그렇게 배포되면 정말 좋겠음

    • 좋은 정보임. 혹시 Windows용 무료 서명 방법도 있는지 궁금함
    • 오, 몰랐음. 개발자 설정에 따라 달라지는 줄 알았는데 찾아봐야겠음
  • 내 가설은 CPU 문제가 아니라 USB 컨트롤러가 열을 포화시키는 것
    CPU/GPU가 아니라 본체가 과열돼서 열 방출이 막히고 결국 스로틀링이 생기는 구조 같음
    다른 어댑터나 모니터로 실험해볼 필요가 있음

    • 나도 2019 i9을 쓰는데, 충전 포트를 바꾸자 스로틀링이 사라졌음
      네 말이 맞는 것 같음
    • 완전히 이해한 건 아니지만, 내 M2 Air도 비슷한 증상이 있음
      4K 144Hz 모니터에 연결해 Zoom이나 영상 스트림을 여러 개 켜면 발열이 심해짐
      USB 컨트롤러 때문이라기보단 단순히 부하가 커서 그런 듯함
    • 이런 현상을 thermal soaking이라고 부름
  • 사이트가 트래픽 폭주로 죽은 듯함
    저장소는 angristan/MacThrottle

    • 수정 완료했음. Cloudflare Workers가 fail closed 모드로 설정돼 있어서 트래픽을 막고 있었음
  • iStat Menus에서 CPU 100%인데 전력 사용량이 낮으면 스로틀링이라 생각할 수 있지만,
    저전력 USB-C 충전기에 연결된 경우에도 같은 현상이 생김
    충전기 전력을 감지하는 기능을 추가하면 좋을 듯함

    • 나도 M1 MacBook Air로 D&D 세션을 하다가 같은 문제를 겪었음
      충전 중이라 더워져서 스로틀링이 심했는데, 세션 전에 미리 충전하니 해결됨
    • 예전에 전원 어댑터 출력이 부족해서 게임을 하다 배터리가 닳아 꺼진 적이 있었음
      이후 세대에서 더 큰 전원 어댑터가 나온 이유를 이해했음
    • 그렇다면 왜 코어 온도만으로는 스로틀링을 판단할 수 없는지 궁금함
      온도가 바로 제어 변수 아닌가?
    • iStat Menus에서 충전기 전력을 보여주긴 하지만, 왜 이런 현상이 생기는지는 여전히 의문임
  • CPU 사용률과 시스템 전력량을 메뉴바에 표시해두면 이상 징후를 바로 알 수 있음
    exelban/stats

    • 나도 그렇게 해서 스로틀링을 처음 의심했음
      CPU 사용률은 높은데 전력량이 줄어드는 걸 보고 눈치챘음
  • 다음 MacBook Air M5에는 Vapor Chamber 냉각이 들어갔으면 좋겠음
    지금은 Apple이 소음 최소화를 열 방출보다 우선시하는 것 같음
    그래서 팬 최소 속도를 강제로 높여둠

    • Vapor Chamber는 순간적인 열 분산에는 좋지만, 결국 열은 알루미늄 바디로 전달됨
      바디가 환경과 열평형에 도달하면 방출량이 한계에 부딪힘
      팬이 있다면 구리판과 바람으로 충분히 해결 가능함
      결국 에너지 보존의 문제임
  • thermal pressure 알림 버그가 있는 것 같음
    혹시 앱에서 그런 문제를 겪었는지 궁금함
    관련 이슈

    • 나도 ProcessInfo.processInfo.thermalState를 쓸 때 상태가 갱신되지 않는 걸 봤음
      하지만 지금 쓰는 thermald 알림 방식에서는 그런 문제가 없음
  • @_silgen_name으로 Darwin API를 직접 선언했는지 궁금함
    import Darwin으로는 접근이 안 되나?

    • 실제로 import Darwin만으로는 해당 API가 노출되지 않는 것 같음