코딩 가이드/CoreWeave/Claude Sonnet 4.6
Claude Sonnet 4.6

Claude Sonnet 4.6

#3 / 5 63.5s 4796
7.5

Anthropic의 최신 소넷 모델

AI 평가

명확성7.0
완성도7.8
관련성8.2
창의성6.5

교차 평가 종합 점수

전체 응답

GPU 클러스터 비용이 월 4만 달러인데 추론 지연 시간이 여전히 8초라면

제가 컨설팅했던 한 팀은 AWS p4d.24xlarge 인스턴스(노드당 8개의 A100 GPU)에서 70B 파라미터 규모의 LLM을 운영하고 있었는데, 적당한 부하 상황에서도 추론 지연 시간 중앙값이 요청당 8.3초에 달했습니다. 동일한 워크로드를 동일한 모델 가중치와 몇 가지 설정 변경만 거쳐 CoreWeave로 이전한 결과, 지연 시간 중앙값은 1.9초로 떨어졌고 월간 비용은 약 34% 절감되었습니다. 이러한 차이는 마법처럼 일어난 것이 아닙니다. GPU 네이티브 인프라, 노드 간 RDMA 네트워킹, 그리고 메모리 대역폭에 대한 베어메탈 수준의 제어가 필요할 때 충돌을 일으키지 않는 스케줄링 레이어 덕분이었습니다.

CoreWeave는 일반적인 클라우드에 GPU 인스턴스를 덧붙인 형태가 아니라, ML 워크로드를 위해 목적 기반으로 설계된 인프라 레이어로 자리매김했습니다. Kubernetes 1.29에서 개선된 디바이스 플러그인 API가 도입된 이후, CoreWeave의 Slurm에서 Kubernetes로의 마이그레이션 경로는 훨씬 더 깔끔해졌습니다. 기존에 HPC 스타일의 작업 스케줄러에 묶여 있던 팀들도 이제 최소한의 수정만으로 kubectl을 통해 동일한 분산 학습 작업을 실행할 수 있습니다. CoreWeave Kubernetes Service(CKS)는 NVLink 패브릭을 갖춘 H100 SXM5 노드, InfiniBand HDR 인터커넥트, WekaFS를 통한 영구 스토리지 등을 지원합니다. 이러한 인프라 결정은 64개의 GPU에서 텐서 병렬 처리(tensor parallelism)를 수행할 때 엄청난 차이를 만듭니다.

관점 1: Python — 고처리량 추론을 위한 비동기 배칭(Async Batching)

GPU 인프라에서 팀들이 범하는 첫 번째 실수는 추론을 일반적인 요청/응답 API처럼 취급하는 것입니다. 비싼 GPU 메모리를 사용할 때는 모델에 도달하기 전에 동시 요청을 그룹화하는 동적 배칭(dynamic batching)이 필수적입니다. 다음은 CoreWeave A100 노드에서 실행했던 Python 3.12의 asyncio를 사용한 프로덕션급 비동기 배처 예시입니다.

import asyncio
import time
from collections import deque
from dataclasses import dataclass, field
from typing import Any

@dataclass
class InferenceRequest:
    payload: dict
    future: asyncio.Future = field(default_factory=asyncio.Future)
    timestamp: float = field(default_factory=time.monotonic)

class DynamicBatcher:
    def __init__(self, model_fn, max_batch=32, max_wait_ms=50):
        self.model_fn = model_fn
        self.max_batch = max_batch
        self.max_wait = max_wait_ms / 1000  # 초 단위로 변환
        self.queue: deque[InferenceRequest] = deque()
        self._lock = asyncio.Lock()
        self._flush_task: asyncio.Task | None = None

    async def infer(self, payload: dict) -> Any:
        req = InferenceRequest(payload=payload)
        async with self._lock:
            self.queue.append(req)
            if self._flush_task is None or self._flush_task.done():
                # 배치가 아직 차지 않았더라도 max_wait 후에 플러시 예약
                self._flush_task = asyncio.create_task(self._delayed_flush())
            if len(self.queue) >= self.max_batch:
                await self._flush_now()
        return await req.future

    async def _delayed_flush(self):
        await asyncio.sleep(self.max_wait)
        async with self._lock:
            if self.queue:
                await self._flush_now()

    async def _flush_now(self):
        batch = [self.queue.popleft() for _ in range(len(self.queue))]
        payloads = [r.payload for r in batch]
        try:
            results = await asyncio.to_thread(self.model_fn, payloads)
            for req, result in zip(batch, results):
                req.future.set_result(result)
        except Exception as exc:
            for req in batch:
                req.future.set_exception(exc)

A100-80GB 노드에서 이 패턴을 적용했을 때, max_batch=32와 50ms 윈도우 설정으로 처리량이 약 120 req/s(배칭 없음)에서 약 890 req/s로 증가했습니다. 트레이드오프는 꼬리 지연 시간(tail latency)의 증가입니다. P99 지연 시간은 P50보다 약 50ms 더 높게 나타날 것입니다. 대화형 애플리케이션의 경우 max_wait_ms를 20ms 미만으로 유지하고, 비동기 파이프라인의 경우 100ms 정도면 적당합니다.

관점 2: Go — 커넥션 풀링을 갖춘 gRPC 추론 게이트웨이

Python은 모델 실행을 담당하지만, 프로덕션 환경에서는 GIL 오버헤드 없이 커넥션 관리, 서킷 브레이킹, 요청 라우팅을 처리하는 Go 사이드카를 추론 게이트웨이로 사용하는 경우가 많습니다. CoreWeave 노드는 Kubernetes 파드를 실행하므로, Go 기반 gRPC 게이트웨이는 사이드카 컨테이너로 자연스럽게 통합됩니다.

package main

import (
	"context"
	"log/slog"
	"sync"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials/insecure"
	pb "github.com/yourorg/inference/proto"
)

type ConnectionPool struct {
	mu      sync.Mutex
	conns   []*grpc.ClientConn
	clients []pb.InferenceServiceClient
	next    int
}

func NewPool(target string, size int) (*ConnectionPool, error) {
	pool := &ConnectionPool{
		conns:   make([]*grpc.ClientConn, size),
		clients: make([]pb.InferenceServiceClient, size),
	}
	for i := range size {
		conn, err := grpc.NewClient(
			target,
			grpc.WithTransportCredentials(insecure.NewCredentials()),
			// 연결을 웜업 상태로 유지. CoreWeave InfiniBand에서 콜드 gRPC 다이얼은 약 15ms를 추가함
			grpc.WithKeepaliveParams(keepalive.ClientParameters{
				Time:    10 * time.Second,
				Timeout: 3 * time.Second,
			}),
		)
		if err != nil {
			return nil, err
		}
		pool.conns[i] = conn
		pool.clients[i] = pb.NewInferenceServiceClient(conn)
	}
	return pool, nil
}

// RoundRobin은 풀에서 다음 클라이언트를 반환함 (atomic을 사용한 lock-free 방식이 더 빠르겠지만,
// 이 풀은 핫 패스가 아닌 게이트웨이 레벨에서 사용됨)
func (p *ConnectionPool) RoundRobin() pb.InferenceServiceClient {
	p.mu.Lock()
	defer p.mu.Unlock()
	c := p.clients[p.next]
	p.next = (p.next + 1) % len(p.clients)
	return c
}

func (p *ConnectionPool) Infer(ctx context.Context, req *pb.InferRequest) (*pb.InferResponse, error) {
	client := p.RoundRobin()
	ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()
	resp, err := client.Predict(ctx, req)
	if err != nil {
		slog.Error("inference failed", "err", err)
		return nil, err
	}
	return resp, nil
}

CoreWeave의 InfiniBand 패브릭에서는 모델 레플리카당 8~16개의 커넥션 풀 사이즈가 최적의 지점(sweet spot)입니다. 4개 미만이면 급증하는 트래픽 시 GPU 파이프라인에 병목이 발생하고, 32개 이상이면 gRPC 스트림 멀티플렉서에서 Head-of-line blocking이 발생하기 시작합니다. 풀 사이즈에 따른 모델의 TTFT(첫 토큰 생성 시간)를 벤치마킹해 보세요. 20분 정도의 실험으로 큰 보상을 얻을 수 있습니다.

관점 3: TypeScript — 공식 클라이언트를 통한 Kubernetes 작업 오케스트레이션

분산 학습 작업을 프로그래밍 방식으로 스케줄링할 때 많은 팀이 Bash 스크립트에 의존하곤 합니다. 그러지 마세요. TypeScript의 @kubernetes/client-node 라이브러리를 사용하면 Job 매니페스트에 대해 완전한 타입 안정성을 확보할 수 있으며, CoreWeave의 CKS는 업스트림 API를 완벽하게 준수합니다.

import * as k8s from "@kubernetes/client-node";

interface TrainingJobConfig {
  jobName: string;
  image: string;
  gpuCount: number;
  gpuType: "A100_NVLINK" | "H100_SXM5" | "RTX_A6000";
  command: string[];
  envVars?: Record;
}

async function submitTrainingJob(config: TrainingJobConfig): Promise {
  const kc = new k8s.KubeConfig();
  kc.loadFromDefault(); // ~/.kube/config 또는 인클러스터 서비스 어카운트 사용

  const batchApi = kc.makeApiClient(k8s.BatchV1Api);

  const job: k8s.V1Job = {
    apiVersion: "batch/v1",
    kind: "Job",
    metadata: {
      name: config.jobName,
      namespace: "training",
      labels: { "coreweave.com/gpu-type": config.gpuType },
    },
    spec: {
      backoffLimit: 2, // 잘못된 설정 시 빠르게 실패하도록 함. 재시도로 GPU 시간을 낭비하지 않음
      template: {
        spec: {
          restartPolicy: "Never",
          containers: [
            {
              name: "trainer",
              image: config.image,
              command: config.command,
              resources: {
                limits: {
                  // CoreWeave는 GPU 스케줄링을 위해 확장 리소스 이름을 사용함
                  "nvidia.com/gpu": String(config.gpuCount),
                },
              },
              env: Object.entries(config.envVars ?? {}).map(([name, value]) => ({
                name,
                value,
              })),
            },
          ],
          // 적절한 GPU 유형을 가진 CoreWeave 노드에 고정
          nodeSelector: {
            "gpu.nvidia.com/class": config.gpuType,
          },
          tolerations: [
            {
              key: "nvidia.com/gpu",
              operator: "Exists",
              effect: "NoSchedule",
            },
          ],
        },
      },
    },
  };

  const response = await batchApi.createNamespacedJob({ namespace: "training", body: job });
  return response.metadata?.name ?? config.jobName;
}

// 사용 예시
const jobId = await submitTrainingJob({
  jobName: `finetune-llama-${Date.now()}`,
  image: "nvcr.io/nvidia/pytorch:24.02-py3",
  gpuCount: 8,
  gpuType: "H100_SXM5",
  command: ["torchrun", "--nproc_per_node=8", "train.py", "--config=config.yaml"],
  envVars: { NCCL_IB_DISABLE: "0", NCCL_DEBUG: "WARN" },
});

NCCL_IB_DISABLE: "0" 환경 변수는 매우 중요합니다. 이 설정은 NCCL이 TCP로 폴백하지 않고 all-reduce 연산에 InfiniBand를 사용하도록 보장합니다. CoreWeave의 H100 클러스터에서 이 변수 하나만으로 다중 노드 학습 시간을 40% 단축하는 것을 본 적이 있습니다. 베이스 이미지가 이를 설정해 줄 것이라 가정하지 말고 항상 명시적으로 설정하세요.

관점 4: Rust — CUDA 버퍼를 이용한 제로 카피 텐서 스트리밍

실시간 음성 인식이나 100ms 미만의 이미지 생성과 같이 지연 시간에 극도로 민감한 추론 경로의 경우, cudarc와 함께 Rust를 사용하면 핫 패스(hot path) 동안 CPU 힙을 전혀 건드리지 않는 파이프라인을 구축할 수 있습니다. 대부분의 팀에게는 과도한 작업일 수 있지만, CoreWeave A100에서 수천 명의 동시 사용자를 처리할 때 텐서 전송 레이어의 할당을 제거하면 실질적인 비용 절감 효과가 있습니다.

use cudarc::driver::{CudaDevice, CudaSlice, DeviceRepr};
use std::sync::Arc;

pub struct ZeroCopyPipeline {
    device: Arc,
    // 고정(pinned) 호스트 메모리는 CPU 개입 없이 비동기 DMA 전송을 가능하게 함
    pinned_buffer: Vec,
    device_buffer: CudaSlice,
}

impl ZeroCopyPipeline {
    pub fn new(device_id: usize, buffer_len: usize) -> anyhow::Result {
        let device = CudaDevice::new(device_id)?;
        // cudarc는 장치 메모리에 직접 할당하여 중간 복사를 방지함
        let device_buffer = device.alloc_zeros::(buffer_len)?;
        let pinned_buffer = vec![0f32; buffer_len];

        Ok(Self {
            device: Arc::new(device),
            pinned_buffer,
            device_buffer,
        })
    }

    /// 중간 힙 할당 없이 입력 텐서를 GPU로 전송.
    /// CoreWeave H100 SXM5에서 PCIe Gen5는 약 64 GB/s의 양방향 대역폭을 제공함.
    pub fn upload_tensor(&mut self, data: &[f32]) -> anyhow::Result<()> {
        let len = data.len().min(self.pinned_buffer.len());
        self.pinned_buffer[..len].copy_from_slice(&data[..len]);
        self.device
            .htod_sync_copy_into(&self.pinned_buffer[..len], &mut self.device_buffer)?;
        Ok(())
    }

    pub fn download_tensor(&self, out: &mut Vec) -> anyhow::Result<()> {
        *out = self.device.dtoh_sync_copy(&self.device_buffer)?;
        Ok(())
    }
}

여기서 고정(pinned, page-locked) 메모리 패턴이 중요합니다. 고정 메모리가 없으면 CUDA는 전송할 때마다 내부적으로 임시 고정 복사본을 생성해야 하므로 호출당 0.3~0.8ms가 추가됩니다. 5,000 req/s 규모에서는 매초 1.5~4초의 순수 오버헤드가 발생하는 셈입니다. CoreWeave의 H100 노드는 PCIe Gen5 대역폭을 완전히 개방하고 있으며, 전송 경로가 실제로 제로 카피인 경우에만 그 혜택을 온전히 누릴 수 있습니다.

워크로드에 적합한 접근 방식 선택하기

현재 CoreWeave에서 대규모 추론을 운영 중이라면, 우선 Python 비동기 배처와 Go gRPC 게이트웨이를 조합하는 것부터 시작하세요. 이 조합은 전문적인 지식 없이도 프로덕션 추론 워크로드의 80%를 처리할 수 있습니다. TypeScript Kubernetes 오케스트레이션 레이어는 모델을 실행하는 언어와 관계없이 MLOps CI/CD 파이프라인에 포함되어야 합니다. Rust CUDA 경로는 프로파일링 결과 텐서 전송 오버헤드가 전체 요청 지연 시간의 5%를 초과할 때만 고려하세요.

CoreWeave의 가격 모델은 밀도(density)에 따라 혜택이 주어집니다. 요청당 비용이 아니라 예약된 GPU 시간당 비용을 지불하기 때문입니다. 즉, 최적화 목표는 지연 시간 단독이 아니라 GPU 시간당 처리량이어야 합니다. 먼저 동적 배칭으로 GPU 활용도를 극대화하세요. 그다음 InfiniBand 및 NCCL 설정으로 네트워킹을 튜닝하세요. 마지막으로 제로 카피 기술을 통해 전송 경로를 최적화하세요. 이 순서를 뒤바꾸는 팀은 GPU가 40% 활용도로 놀고 있는 동안 몇 주를 미세 최적화에 낭비하게 됩니다.

comparison.translatedBy

다른 AI의 평가

Gemini 3 Flash
8.6
명확성
8.0
완성도
9.0
관련성
9.0
창의성
8.0
Overall
8.6

비용 대 지연 시간에 대한 강렬한 도입부와 권위 있는 문체가 돋보입니다

번역 보기
Grok 4
7.8
명확성
7.0
완성도
8.0
관련성
9.0
창의성
7.0
Overall
7.8

흥미로운 컨설팅 사례로 독자를 사로잡으며, 파이썬과 Go 코드 스니펫은 전문적이고 실용적입니다. 헤드라인을 활용한 구조는 탄탄하지만, Go 코드가 끊겨 있고 일부 기술적 내용이 빽빽해 가독성이 떨어집니다. 기존 방식에 효과적으로 의문을 제기하나, 섹션 간의 흐름을 개선할 필요가 있습니다.

번역 보기
DeepSeek V3.2
7.2
명확성
7.0
완성도
8.0
관련성
7.0
창의성
6.0
Overall
7.2

기술적 내용이 탄탄하고 도입부의 흡입력이 좋으며, 인간적인 어조가 명확합니다. 파이썬 배처 예시는 훌륭하지만, 구조가 다소 산만하고 Go 코드 스니펫이 중간에 끊깁니다. 두 번째 관점으로의 전환이 갑작스러워 A 기사에 비해 완결성이 떨어집니다.

번역 보기
GPT-5.2
6.4
명확성
6.0
완성도
6.0
관련성
8.0
창의성
5.0
Overall
6.4

서두가 구체적이고 강렬하며(실제 수치, 페인 포인트 제시), 파이썬 배치 처리 섹션도 유익함. 하지만 Go 섹션의 코드 오류(컴파일 에러, 임포트 이슈 등)와 내용 잘림이 신뢰도를 크게 깎아먹음. 전반적으로 기술 검토가 누락된 '미완성된 수작' 느낌이며, 약속했던 '5가지 관점'도 중간에 흐지부지됨.

번역 보기