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에서 수행.
📌 내부 구조
- 입력 검증
- start/stop/step 길이가 ndim 과 일치해야 함.
- step > 0 조건 및 범위 체크 수행.
- 출력 shape 계산
- 각 축에 대해 (stop - start + step - 1) / step 길이 산출.
- 0 크기인 경우 빠르게 리턴.
- fast-path 최적화
- start=0, stop=원래 크기, step=1 인 경우 → 단순 전체 복사 → cudaMemcpyAsync 로 처리.
- 일반 경로
- y_div, x_stride, start, step 을 GPU 메모리에 업로드.
- 단일 커널(slice_kernel) 실행:
- 각 쓰레드가 출력 인덱스를 계산 → 원본 인덱스로 매핑 → 복사.
- 동기화 & 리소스 해제
- 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).
📌 내부 구조
- 입력 검증
- 모든 입력의 ndim 동일.
- concat 축 외 나머지 차원 일치.
- dtype=F32, layout=RowMajor, device=CUDA 확인.
- 출력 shape 계산
- concat 축 길이 = 모든 입력 텐서 해당 축 길이 합.
- fast-path
- 사실상 필요 없음. 모든 경우 memcpy 반복으로 처리 가능.
- 구현 방식
- 두 가지 가능:
- (A) 단순 cudaMemcpyAsync 루프
- 입력마다 outer 슬라이스 단위로 복사.
- (B) 단일 concat_kernel 실행 (outer/inner 인덱스 기반).
- (A) 단순 cudaMemcpyAsync 루프
- 현재 구현은 (A) 루프 기반 memcpy로 충분히 동작.
- 두 가지 가능:
- 동기화 & 리소스 해제
- 모든 복사 후 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 해제.
'dev_AI_framework' 카테고리의 다른 글
| 파이프 라인 수정... - IR → 패스 → 커널선택(디스패치) → 스케줄/메모리플랜 → 실행으로 이어지는 컴파일러형 학습 파이프라인 (0) | 2025.09.29 |
|---|---|
| 어느 단위 레벨까지 구현해야 할까 에 대한 고민 - 기본 레벨 연산들은 끝도 없 (0) | 2025.09.24 |
| 행렬 곱, bias 문제 내용 (0) | 2025.09.23 |
| Conv2D (NCHW, group=1) 구현/연산 (0) | 2025.09.23 |
| LLM 과 접목, 최적화 커널 구성과 빌드, 모듈 생성의 과정을 자동화 (0) | 2025.09.23 |