본문 바로가기

dev_AI_framework

AI Framework 구현을 위한 최적의 구조 생각 ( 확장 및 보완 중심? ) - C++/CUDA Core + 얇은 pybind11 Adapter 아키텍처 가이드

목적: 핵심 연산은 C++/CUDA로, 파이썬 바인딩은 포인터/스트림만 전달하는 얇은 어댑터로 구성하여, 제로-카피·고성능·모듈 재사용성을 달성한다. 이 문서는 디렉터리 구조, 인터페이스 규약, 빌드/배포, 파이썬 상호운용(CuPy/PyTorch/NumPy), 테스트/디버깅, 자주 겪는 이슈까지 한 번에 정리한다.

 

핵심 철학 (TL;DR)

  • 경계 최소화: Python ↔ Core 사이에 포인터(void*)/크기/shape·stride/스트림만 오간다.
  • 소유권 비침투: 바인딩은 메모리를 소유하지 않는다(해제 책임은 호출자).
  • 동기화는 선택: 기본 비동기, 필요 시 sync_after 옵션으로 동기화.
  • GIL 해제: 커널 실행 구간은 항상 GIL을 해제한다.
  • 플러그형 코어: Core는 언어 비의존+안정 ABI. pybind는 매우 얇게 유지.

 

개발 워크 플로

  • 기능 설계
    • 시그니처 확정 : 버퍼 포인터, shape/stride, dtype, cudaStream_t
    • 동기화 정책 : 기본 비동기, 필요 시 sync_after 플래그
  • 코어 (C++/CUDA) 구현
    • .hpp 헤더 추가
    • .cu 커널 작성
  • 코어 라이브러리 빌드
    • CMake 에 소스 추가
    • 정적 또는 공유 빌드
  • 바인딩 추가
    • 바인딩 내 함수 추가
    • 포인터/스트림을 캐스팅해서 코어 함수 호출
  • 파이썬 모듈 빌드
  • 파이썬 유틸

 

더보기

동기화 synchronization 개념

 

GPU 연산은 기본적으로 비동기 asynchronous 로 동작

  • CPU -> GPU : CPU 가 커널을 실행시키면 GPU 에 작업 명령만 던져주고 바로 리턴 ( CPU 는 다른 동작 )
  • 즉, CPU 코드가 끝나도 GPU 연산은 끝나지 않았을 수 있음
  • GPU 는 수천 개의 스레드를 병렬 실행시켜, CPU 와 다르게 파이프라인이 길고 준비 시간 존재
  • CPU 를 대기 시키면 성능 저하 - 필요시 결과를 동기화해서 확인하는 방식으로 작동

동기화 방법

  • cudaStreamSynchronize(stream) 특정 스트림의 작업이 다 끝날 때까지 CPU 를 블록
  • cudaDeviceSynchronize() : GPU 전체의 모든 작업이 끝날 때까지 대기
  • cudaMemcpy (host <-> device) : 기본적으로 동기화 동작을 내포
// 비동기 커널 실행
my_kernel<<<grid, block, 0, stream>>>(...);

// CPU는 바로 리턴 → 아래 코드 바로 실행됨
printf("GPU가 아직 안 끝났어도 이건 찍힘!\n");

// 결과를 확인하려면 반드시 동기화 필요
cudaStreamSynchronize(stream);

 그래서 바인딩 함수에서 sync_after=True 옵션을 주면, 내부에서 cudaStreamSynchronize 를 호출해서 CPU, GPU 가 맞춰진 상태로 리턴

False 의 경우 사용자가 sync 를 책임져야 함

 

 

정적, 공유 라이브러리 개념

c++/CUDA 코드는 라이브러리 형태로 묶을 수 있고, static, shared 두 가지로 나뉜다.

정적 라이브러리는 빌드할 때 라이브러리 안의 코드가 그대로 실행 파일에 복사, 합쳐짐

공유 라이브러리는 실행 파일에는 함수 심볼 이름만 들어가고 실제 구현은 런타임에서 불러옴