
Core ML과 Core AI의 차이점
요약
Core ML과 차세대 Core AI의 핵심적인 차이점을 기술적으로 분석합니다. LLM의 생성 루프(Generation Loop)를 앱 측 코드에서 실행하는지, 아니면 OS 런타임에서 직접 실행하는지에 따른 성능 차이를 다룹니다.
핵심 포인트
- Core AI는 LLM 생성 루프를 OS 런타임에서 직접 처리하여 성능을 최적화함
- 루프 주체 변화만으로 동일 모델 기준 실측 약 3.5배의 속도 향상 가능
- KV 캐시, 샘플링, IR, 디스패치 등 LLM 추론의 핵심 메커니즘 설명
- Core ML은 iOS 26 이하 버전 지원을 위해 계속 유지됨
Core AI는 Core ML의 후계입니다(WWDC 2026, iOS/macOS 27~).
역할은 거의 동일하며, "PyTorch 모델을 변환하여 iPhone이나 Mac의 ANE/GPU/CPU에서 구동하기" 위한 것입니다.
본질적인 차이는 단 하나입니다.
LLM의 생성 루프(Generation Loop)를 당신의 코드(앱 측 CPU)가 돌리는가, 아니면 OS의 런타임(Runtime)이 돌리는가.
그리고 이 차이만으로, 동일한 모델이 실측 3.5배 빨라집니다.
사용법에 대한 답을 미리 적어둡니다:
| 당신의 상황 |
|---||
| iOS 26 이전을 지원해야 한다 | Core ML 일택 (Core AI는 OS 27+)
| ... | 기존의 .mlpackage 자산 |
| 계속 유지된다 | 서두를 이유가 없다 |
여기서부터는 이 결론——특히 "루프를 누가 돌리는가"가 왜 3.5배의 차이를 만드는가——를 이해하기 위한 글입니다.
이 글에서 전제로 하는 용어는 다음 4가지뿐입니다. 알고 있다면 건너뛰셔도 됩니다.
KV 캐시 (KV Cache). LLM은 1토큰씩 문장을 생성합니다. 이때 과거의 모든 토큰에 대해 계산한 중간 결과(Attention의 Key와 Value)를 매번 처음부터 다시 계산하면, 문장이 길어질수록 절망적으로 느려집니다. 그래서 한 번 계산한 부분은 저장해 두었다가 재사용합니다. 이 "작업 메모"가 KV 캐시이며, 추론과 추론 사이에서 유지되는 **상태 (State)**의 정체입니다.
샘플링 (Sampling). LLM의 1회 추론이 내놓는 것은 다음 토큰 그 자체가 아니라 "다음에 올 토큰의 확률 분포"입니다 ("고양이" 80% · "강아지" 15% · · · 와 같은 표). 거기서 실제로 1개를 선택하는 조작이 샘플링입니다. 기존에는 이것이 모델 외부——앱 측 코드——의 일이었습니다. 이 부분도 나중에 중요하게 작용합니다.
IR (중간 표현, Intermediate Representation). 변환 도구가 모델을 저장할 때 사용하는 컴파일러용 공통 언어입니다. PyTorch 코드도 GPU 기계어도 아닌 중간 표현으로, .mlpackage나 .aimodel의 내용은 실질적으로 이것입니다. 컴파일러는 IR을 읽고 최적화합니다. 즉——IR에 적혀 있지 않은 것은 컴파일러에게 보이지 않는다. 이 문장이 나중에 중요하게 작용합니다.
디스패치 (Dispatch). CPU가 GPU에 "이 계산 커널을 1개 실행해줘"라고 요청하는 조작입니다. 1회마다 고정된 수수료(마이크로초 단위)가 발생합니다. 계산량이 크면 오차 범위 내이지만, 계산량이 작으면 수수료가 본체보다 더 커지게 됩니다.
Core ML과 Core AI의 비교에서 자주 보이는 설명들은 조사해 보면 둘 다 차이점으로 성립하지 않습니다.
"Core ML은 상태(KV 캐시)를 가질 수 없다" — 거짓입니다. iOS 18의 MLState를 통해 추론 사이에 상태를 유지할 수 있습니다. Apple 스스로가 Core ML로 LLM을 구동하는 예시를 보여주고 있습니다. 루프는 돌아갑니다.
"Core AI라면 자작 모델을 Foundation Models에 끼워 넣을 수 있다" — 이 또한 차이점이 아닙니다. WWDC26 세션 339의 LanguageModel은 공개 프로토콜이며, MLX(MLXLanguageModel)도 Hugging Face(AnyLanguageModel)도 이를 구현하고 있습니다. Core AI에 있는 것은 공식 어댑터의 "편리함"이지, 능력이 아닙니다.
그렇다면 무엇이 다른가.
루프의 내부를 보면 알 수 있습니다.
LLM의 생성은 1토큰마다 동일한 그래프를 돌리는 루프입니다. 당신의 코드가 이 루프를 돌릴 때, 1회전의 내부 구성은 다음과 같습니다.
매 토큰마다:
① 당신의 코드 → 프레임워크에 predict를 1회 요청
② 런타임이 그래프의 수백 개의 op를 GPU로 dispatch
...
이 중 모델의 본질적인 작업은 ②의 일부뿐입니다. 뉴럴 네트워크의 추론은 궁극적으로 "가중치(모델이 학습한 거대한 수의 표)를 메모리에서 읽고, 입력과 곱하고, 더하는" 반복이며, 그것이 일어나는 곳은 ②의 내부뿐입니다. ①③④는 루프를 관리하기 위한 처리 비용——이하 "관리 비용"이라 부름——이며, 모델의 지능에는 1밀리미터도 기여하지 않습니다. 1회라면 오차 수준이지만, LLM은 이를 초당 수십~수백 회 돌립니다.
관리 비용이 얼마나 영향을 미치는지 실측 데이터가 있습니다. LLM 디코딩 속도의 물리적인 천장은 메모리에서 데이터를 읽어오는 속도입니다 (매 토큰마다 모델의 가중치(weights)를 전부 읽어야 하므로, "가중치 총량 ÷ 읽기 속도"보다 빠를 수는 없습니다). 그런데 수중에 있는 Mac에서 저수준 API(low-level API)를 통해 이 루프를 직접 돌려보면——구조는 Core ML의 predict() 루프와 같습니다——**읽기 속도의 이론적 상한선의 약 11%밖에 사용하지 못했으며, 1토큰당 약 1000회의 디스패치(dispatch)**가 발생했습니다. 천장에 도달하기도 훨씬 전에 관리 비용 때문에 한계에 부딪히는 것입니다.
그리고 바로 이 지점이 Core ML + MLState의 구조적인 한계입니다. 상태(state)는 가질 수 있습니다. 하지만 루프는 당신의 코드입니다. 즉, Core ML에서는 1토큰을 만들 때마다 반드시 GPU에서 당신의 코드(CPU 측)로 제어권이 돌아옵니다. ①③④의 관리 비용은 매 회차 반드시 지불해야 합니다. 이 "매 토큰마다 CPU로 돌아오는" 구조 자체는 MLState에서도 변하지 않습니다.
Core AI의 런타임에는 LLM 디코딩 전용 파이프라인 엔진이 탑재되어 있습니다. "런타임이 루프를 내포한다"는 것을 GPU의 관점에서 구체적으로 살펴보겠습니다.
먼저, 당신의 코드가 루프를 돌릴 때 GPU가 보는 세상은 이렇습니다: 수백 개의 작은 작업이 도착함 → 전부 수행함 → 멈춰서 다음 요청을 기다림. CPU가 결과를 읽고, 샘플링(sampling)하고, 다음 입력을 만들어 재요청할 때까지 GPU는 그저 기다립니다. 1토큰마다 신호를 기다리는 셈입니다. 앞서 말한 "읽기 속도의 11%"의 정체가 바로 이것이며, GPU는 실력의 10%밖에 내지 못하고 있습니다.
파이프라인 엔진은 이 신호 대기 문제를 세 가지 방법으로 해결합니다.
1. 한 회차의 형태를 처음부터 알고 있습니다. 디코딩의 한 회차는 매번 완전히 동일한 형태의 그래프입니다 (입력 토큰 1개 · 상태 · 출력). 엔진은 이를 전제로 수백 개의 연산(op)을 소수의 커널(kernel)로 융합(fusion)하며, 이전 회차가 GPU에서 실행되는 동안 다음 회차의 명령 준비를 미리 마쳐둡니다.
2. 회차와 회차를 GPU 측에서 연결합니다. "토큰 N의 출력이 토큰 N+1의 입력이 된다"는 전달 과정을 CPU로 돌려보내지 않고, GPU 상의 작업 연쇄로서 연결합니다. 샘플링을 포함하여 엔진 내부에서 처리되므로, GPU의 큐(queue)에는 항상 다음 작업이 쌓여 있습니다. 신호 대기가 사라집니다.
3. CPU는 관객이 됩니다. 당신의 코드는 처음에 "생성해"라고 한 번만 말하면 됩니다. 그 후에는 완성된 토큰이 비동기 스트림(asynchronous stream)으로 흘러나오는 것을 받기만 하면 됩니다. 매 토큰마다 발생하는 루프는 더 이상 당신의 코드 어디에도 존재하지 않습니다.
비유하자면, 자체 루프는 "제품을 1개 만들 때마다 라인을 멈추고, 사무실로 운반해 검수하고, 다음 제품의 지시서를 작성한 뒤에야 라인을 재가동하는" 공장입니다. 엔진은 "처음에 지시서를 전달하면 라인은 계속 돌아가고, 완성품이 컨베이어 벨트를 타고 흘러나오는" 공장입니다. 라인(GPU)의 성능은 같더라도 생산량(throughput)이 다릅니다.
효과는 동일한 가중치로 측정할 수 있습니다. 자체 측정(qwen3.5, 동일한 변환 모델 사용) 결과, 자체 루프 58.5 tok/s → 엔진 204 tok/s로 3.5배 향상. 커스텀 커널을 단 한 줄도 작성하지 않았습니다. 변한 것은 가중치도, 양자화(quantization)도 아닌, 바로 루프의 소유권뿐입니다.
이것이 Core ML과 Core AI의 진정한 차이점입니다. 그리고 흥미로운 점은 여기서부터인데, Core AI의 다른 신기능들은 전부 "런타임이 루프를 소유하기 위한 부품"으로서 인과관계로 연결됩니다.
① 상태가 IR의 어휘(vocabulary)라는 점. 앞서 말한 문장을 기억하세요. IR에 적혀 있지 않은 것은 컴파일러에게 보이지 않습니다. MLState는 바로 그런 존재로, 런타임 입장에서 보면 내부를 알 수 없는 **외부 버퍼(external buffer)**였습니다. 외부에서 전달되는 불투명한 상자를 엔진이 미리 예측하여 스케줄링할 수는 없습니다. Core AI의 .aimodel은 "이 텐서(tensor)는 그래프가 인플레이스(in-place)로 수정할 상태이다"라고 IR 레벨에서 선언합니다. 상태의 읽기/쓰기가 그래프의 일부이기 때문에, 엔진은 루프 전체를 꿰뚫어 보고 파이프라인을 구성할 수 있습니다. 상태의 일급 시민화(first-class citizenization)는 그 자체가 목적이 아니라 루프 소유를 위한 전제 조건입니다.
② 그래프가 사전에 고정되어 있다는 점. 엔진의 기술(1)——다음 회차를 미리 준비하는 것——은 그래프가 실행되기 전에 완전히 컴파일되어 있기 때문에 가능해집니다. Core AI는 AOT 컴파일(xcrun coreai-build compile...
→ .aimodelc)
및 기기 상의 첫 최적화를 제어하는 API(AIModel.specialize() / AIModelCache)를 공개했습니다. Core ML에서는 불투명했던 "기기 상에서 언제 컴파일되는가"가 개발자의 손에 넘어왔습니다. AOT(Ahead-Of-Time)가 완료된 번들은 웜 로드(warm load) 시간이 0.0초입니다.
③ 반복(iteration)당 바이트 하한(byte floor)을 낮추는 연산. 루프 관리 비용이 사라지면, 다음 한계는 드디어 물리적인 영역입니다: 매 반복마다 가중치(weights)를 메모리에서 전부 읽어오는 것 말입니다. OS 27의 Core AI는 FP4/FP8 TensorOps 및 FlashAttention을 해제하여(WWDC26 세션 330), 이 하한선 자체를 낮추려 하고 있습니다. Core ML의 런타임 연산이 실질적으로 fp16 하한이었던 것과 대조적입니다.
변환기(coreai-torch)가 SDPA, RoPE, RMSNorm을 복합 연산(composite ops)으로서 구조 자체로 내려보내는 것도 같은 맥락입니다. 엔진이 루프를 최적화하려면, 그래프 안에 "이것은 attention이다"라는 의미가 남아 있어야 하기 때문입니다.
Core ML은 루프를 "돌릴 수" 있습니다. Core AI는 루프를 "소유"합니다. 초당 수백 번씩 돌아가는 루프에서는, 반복당 관리 비용을 누가 제거할 수 있는지가 전부이며, 이는 당신의 코드로는 구조적으로 제거할 수 없습니다. 그래서 런타임 자체를 다시 만들었습니다. 그것이 Core AI입니다.
AI 자동 생성 콘텐츠
본 콘텐츠는 Qiita AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.
원문 바로가기