본문 바로가기

dev_AI_framework

각 모듈별 독립 구현 변경

현재 각 기능, ops 를 추가 구현하면서 바인딩 코드가 계속 늘어나고, 유지 및 보수에 불리해짐을 느낌, 각 ops 별 독립 모듈로써 작동하도록 변경 필요 

 

1) 디렉터리/레이아웃 정리

각 op 디렉터리(예: backends/cuda/ops/gemm) 안에 모든 의존 자원을 국소화한다.

backends/cuda/ops/<op>/
 ┣ include/             # 이 op만 쓰는 내부 헤더(필요시)
 ┣ kernels/             # CUDA 커널 소스들(.cu)
 ┣ api.hpp              # 이 op의 C++ public API(런처 선언 등)
 ┣ launcher.cu          # 텐서/attrs → 커널 파라미터 매핑/호출
 ┣ (backward.cu)        # 있으면
 ┣ (common.hpp)         # 공유 유틸(있으면)
 ┗ (traits.hpp)         # 커널 컴파일 특성(있으면)
 

gemm처럼 커널이 전용일 때, 공유 폴더 kernels/regemm_epilogue는 제거하고, backends/cuda/ops/gemm/kernels/로 이관(중복·다중정의 방지).


2) 공통 헤더 의존성 → “경량 shim”으로 치환

기존에는 #include "ai/tensor.hpp", #include "ai/dispatch.hpp" 같은 코어 헤더에 의존했는데, 독립 모듈에서는 최소 타입만 필요한 경량 shim으로 갈아끼운다.

  • 방법 A(추천): backends/cuda/ops/_shim/ 폴더에 아주 작은 대체 헤더 두 개 정의
    • tensor_shim.hpp : dtype, layout, device, Tensor(desc+data) 정도
    • op_schema_shim.hpp : 이 op에 필요한 attrs만(예: GemmAttrs{act, leaky_slope, trans_a,b})
    • dispatch_shim.hpp : using StreamHandle = void*; enum class Status {...};
  • 각 op의 api.hpp/launcher.cu/backward.cu 등에서 코어 헤더 대신 shim 헤더를 include:
    • (예) #include "ops_shim/tensor_shim.hpp" (상대 경로는 CMake include로 해결)

이렇게 하면 코어 라이브러리 없이도 컴파일/링크가 독립된다. (CUDA 런타임/ cuBLAS만 링크)


3) 런처 코드 수정 포인트

  • include 교체: ai/* → shim 헤더
  • 텐서/속성/Status/StreamHandle 타입은 shim 네임스페이스로 변경(또는 namespace ai {} 유지해도 되지만 타입 실체는 shim)
  • 디버그 로그/외부 레지스트리 의존 제거
  • 커널 호출은 op 내부 kernels/*.cu에 있는 함수만 부른다.
  • 필요한 변환 유틸(infer_ld, bias_kind 추론 등)은 common.hpp 같은 op-로컬 유틸로 모듈화

4) 바인딩(파이썬) 모듈을 op 단위로 분리

  • 새 타깃: src/bindings/<op>_pybind.cpp → _ops_<op>로 빌드
  • 바인딩은 numpy API 또는 (필요 시) 얇은 텐서 포장 중 택1
    • 빠르게 쓰려면 우리가 만든 forward_numpy(...), backward_numpy(...) 식으로 numpy만 받는 래퍼 제공
    • 내부에서 host→device 복사/실행/결과 device→host 복귀(이미 gemm 테스트에서 검증)
  • 에러 매핑: Status → std::runtime_error 식으로 간단 변환

이미 _ops_gemm로 검증 끝. 다른 op도 동일 패턴으로 복제하면 된다.


5) CMake: 각 op를 완전 독립 타깃으로

최상위 CMakeLists.txt에 아래 재사용 가능한 함수/매크로를 추가해 두면, ops 추가가 1~2줄로 끝남.

# 공통: 독립 op 모듈을 만드는 헬퍼
function(add_standalone_op OP_NAME)
  # 예: OP_NAME=gemm → 타깃명 _ops_gemm, 소스 경로 구성
  set(OP_DIR ${CMAKE_SOURCE_DIR}/backends/cuda/ops/${OP_NAME})

  pybind11_add_module(_ops_${OP_NAME} MODULE
    ${CMAKE_SOURCE_DIR}/src/bindings/${OP_NAME}_pybind.cpp
    ${OP_DIR}/launcher.cu
    ${OP_DIR}/kernels/*.cu     # 필요에 따라 glob or 나열
    ${OP_DIR}/backward.cu      # 있으면
  )

  target_include_directories(_ops_${OP_NAME}
    PRIVATE
      ${OP_DIR}                         # api.hpp, common.hpp
      ${OP_DIR}/include                 # 이 op 전용 내부 include
      ${CMAKE_SOURCE_DIR}/backends/cuda/ops/_shim  # 경량 shim
  )

  target_link_libraries(_ops_${OP_NAME}
    PRIVATE CUDA::cudart CUDA::cublas
  )

  set_target_properties(_ops_${OP_NAME} PROPERTIES
    CUDA_SEPARABLE_COMPILATION ON
    LIBRARY_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/python/graph_executor_v2/ops"
  )
  if (WIN32)
    set_target_properties(_ops_${OP_NAME} PROPERTIES
      RUNTIME_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/python/graph_executor_v2/ops"
      ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_SOURCE_DIR}/python/graph_executor_v2/ops"
    )
  endif()
endfunction()

# 필요 op들을 간단히 나열
add_standalone_op(gemm)
# add_standalone_op(rmsnorm)
# add_standalone_op(softmax) ...
 

이렇게 하면 코어/백엔드 타깃 불필요.
_ops_<op>가 CUDA 런타임/BLAS만 링크하고 독립적으로 떨어짐.


6) 빌드 & 실행

 
# 설정
cmake -S . -B build -G "Ninja" -DCMAKE_BUILD_TYPE=Release ^
      -DCMAKE_CUDA_ARCHITECTURES=86 ^
      -DPython3_EXECUTABLE="C:\Path\to\python.exe" ^
      -Dpybind11_DIR="...\site-packages\pybind11\share\cmake\pybind11"

# 특정 op만 빌드
cmake --build build --target _ops_gemm -j

실행 시:

  • Windows면 CUDA DLL 경로 os.add_dll_directory()로 보장
  • python/graph_executor_v2/ops/_ops_gemm*.pyd 로드 확인

7) 마이그레이션 작업 순서(요약 체크리스트)

  1. 커널 이관
    • 공유 kernels/regemm_epilogue → backends/cuda/ops/gemm/kernels/로 이동
    • 중복 정의 제거(하나만 컴파일)
  2. shim 도입
    • backends/cuda/ops/_shim/{tensor_shim.hpp, op_schema_shim.hpp, dispatch_shim.hpp} 작성
    • 런처/커널/바인딩에서 ai헤더 대신 shim include
  3. 런처 정리
    • ai:: → shim 타입으로 치환
    • 공용 유틸(common.hpp)로 infer_ld/bias_kind 등 캡슐화
    • 코어/레지스트리 호출 제거
  4. 바인딩 추가
    • src/bindings/<op>_pybind.cpp 작성(넘파이 API or 얇은 텐서 API)
    • Status→예외 변환, 문서 문자열, all
  5. CMake 타깃 추가
    • add_standalone_op(<op>)로 간단 등록
    • 의존성은 CUDA 런타임/BLAS만
  6. 테스트
    • 간단 forward/backward shape/값 검증
    • (선택) 수치미분 케이스
    • dumpbin /DEPENDENTS(Win)로 외부 의존성에 ai_core가 없는지 확인

8) 자주 부딪히는 이슈 & 해결

  • 다중정의(nvlink error): 커널을 두 군데서 빌드하지 않도록 소스 중복 제거.
  • 런타임 DLL 문제(Win): os.add_dll_directory(CUDA\bin) 확보.
  • ABI 충돌: shim 헤더는 C++17 고정·extern "C" 필요 없음(전부 템플릿/inline이면 안전).
  • 성능/타일 파라미터: 각 op config.h에서 독립 관리(빌드 옵션으로 override 가능).