본문 바로가기

dev_AI_framework

CUDA Backend: Slice & Concat 연산 문서

1. 개요

  • slice 와 concat 은 수치 연산(Numerical Computation) 이 아닌, 메모리 조작(Memory Movement) 연산에 속한다.
  • 두 연산 모두 복잡한 수학적 계산 없이 데이터 블록 재배치/복사 만 수행하므로, 별도의 kernels.cu 모듈이 필요하지 않고 launcher.cu 에서 바로 구현 가능하다.
  • 바인딩(Python ↔ C++)에서는 GPU 실행 부분만 GIL 해제하여 안전하고 효율적인 연동을 제공한다.

2. Slice 연산

📌 역할

  • N차원 배열에서 start/stop/step 파라미터에 따라 서브 텐서를 잘라내는 연산.
  • PyTorch, NumPy 의 X[:, 1:4:2] 같은 슬라이싱을 GPU에서 수행.

📌 내부 구조

  1. 입력 검증
    • start/stop/step 길이가 ndim 과 일치해야 함.
    • step > 0 조건 및 범위 체크 수행.
  2. 출력 shape 계산
    • 각 축에 대해 (stop - start + step - 1) / step 길이 산출.
    • 0 크기인 경우 빠르게 리턴.
  3. fast-path 최적화
    • start=0, stop=원래 크기, step=1 인 경우 → 단순 전체 복사 → cudaMemcpyAsync 로 처리.
  4. 일반 경로
    • y_div, x_stride, start, step 을 GPU 메모리에 업로드.
    • 단일 커널(slice_kernel) 실행:
      • 각 쓰레드가 출력 인덱스를 계산 → 원본 인덱스로 매핑 → 복사.
  5. 동기화 & 리소스 해제
    • cudaStreamSynchronize 후, 임시 디바이스 메모리 해제.

📌 바인딩(Py API)

 
Y = ge.slice(X, start=[...], stop=[...], step=[...])
  • Python 배열을 GPU로 업로드.
  • 내부적으로 ai::ops::slice_run → SliceCudaLaunch 호출.
  • 연산만 GIL 해제(py::gil_scoped_release).
  • 결과를 다시 NumPy 배열로 반환.

3. Concat 연산

📌 역할

  • 여러 입력 텐서를 특정 축(axis) 기준으로 이어붙이는 연산.
  • 예: (2,3,4) 텐서 여러 개를 axis=1 기준으로 합치면 (2,6,4).

📌 내부 구조

  1. 입력 검증
    • 모든 입력의 ndim 동일.
    • concat 축 외 나머지 차원 일치.
    • dtype=F32, layout=RowMajor, device=CUDA 확인.
  2. 출력 shape 계산
    • concat 축 길이 = 모든 입력 텐서 해당 축 길이 합.
  3. fast-path
    • 사실상 필요 없음. 모든 경우 memcpy 반복으로 처리 가능.
  4. 구현 방식
    • 두 가지 가능:
      • (A) 단순 cudaMemcpyAsync 루프
        • 입력마다 outer 슬라이스 단위로 복사.
      • (B) 단일 concat_kernel 실행 (outer/inner 인덱스 기반).
    • 현재 구현은 (A) 루프 기반 memcpy로 충분히 동작.
  5. 동기화 & 리소스 해제
    • 모든 복사 후 cudaStreamSynchronize.
    • 임시 디바이스 버퍼 해제.

📌 바인딩(Py API)

 
C = ge.concat([A, B, C], axis=1)
  • Python 리스트 입력을 GPU로 업로드.
  • 내부적으로 ai::ops::concat_run → ConcatCudaLaunch 호출.
  • 연산만 GIL 해제.
  • 결과를 다시 NumPy 배열로 반환.

4. 공통 설계 원칙

  • 메모리 중심 연산
    • slice / concat 은 모두 메모리 복사/재배치가 핵심.
    • 따라서 kernels.cu 파일을 두지 않고, launcher.cu 안에서 커널 또는 memcpy 루프를 직접 구현.
  • fast-path 지원
    • trivial 한 경우(slice: 전체 복사, concat: axis=마지막)는 memcpy로 대체해 성능 최적화.
  • 자원 관리
    • 임시 디바이스 버퍼(d_stride, d_start, d_step, d_srcs, d_offsets)는 모두 cudaStreamSynchronize 후 안전하게 cudaFree.
  • Python 바인딩
    • py::call_guard<py::gil_scoped_release>() 를 함수 전체에 쓰지 않는다.
    • 대신, ai::ops::xxx_run 호출 부분만 py::gil_scoped_release 로 감싼다.
    • NumPy 데이터 접근(mutable_data(), shape)는 반드시 GIL 보유 상태에서 수행.

 

✅ 요약:

  • Slice: 인덱스 변환 기반 복사, fast-path=전체 memcpy.
  • Concat: 여러 입력을 outer-slice 단위 D2D memcpy로 이어붙임.
  • 두 연산 모두 kernels.cu 별도 불필요, launcher.cu + api.cpp + 바인딩으로 완결.
  • Python 바인딩은 ops 호출 구간만 GIL 해제.