thesis-3d GPU 레이아웃 엔진 C++ 재설계 — Python CuPy 의존성 제거와 CUDA 바이너리 안정화 계획
초록
현재 thesis-3d의 GPU 가속 레이아웃 계산은 Python CuPy로 구현되어 있다. CuPy는 런타임에 libcurand.so 등 CUDA 라이브러리를 동적 로드하기 때문에 CUDA 툴킷 버전 변경 시 ImportError로 전체 서비스가 fallback으로 강등된다. 실제로 이번 세션에서 venv python3로 교체 시 libcurand.so 미발견으로 정확히 이 문제가 발생했다. 본 논문은 layout_engine C++ CUDA 바이너리로 재설계해 라이브러리 의존성을 컴파일 시점에 고정하고 런타임 안정성을 확보하는 설계 계획을 제시한다.
thesis-3d GPU 레이아웃 엔진 C++ 재설계
— Python CuPy 의존성 제거와 CUDA 바이너리 안정화 계획
저자: EROS
일자: 2026-07-08
버전: v1
분류: architecture · gpu · cpp · stability · thesis-3d
1. 현재 문제 — Python CuPy의 구조적 취약성
1.1 실제 발생한 장애
[오류 재현]
venv python3로 viz_server 실행 시:
File "cupy_backends/cuda/libs/__init__.py"
ImportError: Failure finding "libcurand.so": No such file
→ _CUPY_OK = False → numpy-cpu fallback 강제 적용
→ GPU 있어도 CPU로 계산, 성능 저하
1.2 원인 구조
Python CuPy 동작 방식:
import cupy
└─ 런타임에 dlopen("libcurand.so") 시도
├─ 성공 → GPU 가속
└─ 실패 → ImportError (전체 모듈 로드 실패)
CuPy는 CUDA 라이브러리를 런타임에 동적 로드한다. 이는: - CUDA 툴킷 버전 업그레이드 → so 파일명 변경 → ImportError - venv vs 시스템 python3 간 라이브러리 경로 불일치 - Ubuntu 버전 업그레이드 → 패키지 경로 변경
위 세 가지 시나리오 모두 서비스 배포 없이 갑자기 fallback으로 전락하는 원인이 된다.
2. 목표 환경
| 항목 | 값 |
|---|---|
| 호스트 | hb5u (Ubuntu 25.10 Questing Quokka) |
| GPU | NVIDIA GeForce RTX 5060 Laptop GPU |
| VRAM | 8,151 MiB |
| Compute Capability | 12.0 (Blackwell) |
| CUDA 드라이버 | 595.71.05 / CUDA 13.2 |
| nvcc | 미설치 (런타임만 존재) |
3. 설계 — layout_engine C++ CUDA 바이너리
3.1 아키텍처
viz_server.py (Python)
└─ subprocess.run(["./layout_engine"], input=json_bytes)
│ stdin: {"nodes":[...], "edges":[...], "iterations":80}
└─ layout_engine (C++ 바이너리)
├─ JSON 파싱 (nlohmann/json, header-only)
├─ CUDA 커널 실행 (Fruchterman-Reingold 3D)
└─ stdout: {"nodes":[...with x,y,z...], "ms":37.2}
인터페이스: JSON stdin → JSON stdout - 외부 네트워크 없음, 파일 I/O 없음 - viz_server.py와 완전 분리 → Python 버전·패키지 무관
3.2 의존성 설계 — libcurand 완전 제거
기존 CuPy가 libcurand.so에 의존한 이유: 배열 초기화에 cp.random.randn() 사용.
해결: CUDA 커널 내 Xorshift 난수 생성으로 대체:
__device__ float xorshift_float(uint32_t& state) {
state ^= state << 13;
state ^= state >> 17;
state ^= state << 5;
return (state & 0x7FFFFF) / float(0x800000) * 2.0f - 1.0f;
}
__global__ void init_positions(float* pos, int n, uint32_t seed) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i >= n) return;
uint32_t s = seed ^ (i * 2654435761u);
pos[i*3+0] = xorshift_float(s);
pos[i*3+1] = xorshift_float(s);
pos[i*3+2] = xorshift_float(s);
}
런타임 CUDA 의존성: libcuda.so 하나만 (드라이버 설치 시 항상 존재).
3.3 CUDA 커널 설계 — 반발력
__global__ void repulsion_kernel(
const float* __restrict__ pos,
float* __restrict__ delta,
int n, float k2
) {
int i = blockIdx.x * blockDim.x + threadIdx.x;
if (i >= n) return;
float dx = 0, dy = 0, dz = 0;
float xi = pos[i*3], yi = pos[i*3+1], zi = pos[i*3+2];
for (int j = 0; j < n; ++j) {
if (i == j) continue;
float rx = xi - pos[j*3];
float ry = yi - pos[j*3+1];
float rz = zi - pos[j*3+2];
float dist2 = fmaxf(rx*rx + ry*ry + rz*rz, 1e-4f);
float inv = k2 / dist2;
dx += rx * inv; dy += ry * inv; dz += rz * inv;
}
delta[i*3] = dx; delta[i*3+1] = dy; delta[i*3+2] = dz;
}
n=120 기준 블록 구성: <<<(n+127)/128, 128>>>
3.4 빌드 명세
# CUDA 툴킷 설치 (nvcc)
apt-get install cuda-toolkit-13-2 # 또는 pip install cuda-python
# 컴파일
nvcc -O3 -arch=sm_120 \
-Xcompiler "-O3 -static-libgcc -static-libstdc++" \
layout_engine.cu -o layout_engine
# 의존성 확인
ldd layout_engine # libcuda.so.1 만 보여야 함
-arch=sm_120: RTX 5060 Laptop (Blackwell, compute 12.0) 최적화.
3.5 viz_server.py 연동 (변경 최소)
import subprocess, json
BINARY = Path(__file__).parent / "layout_engine"
_BINARY_OK = BINARY.exists()
def _force_layout_binary(nodes, edges, iterations=80):
payload = json.dumps({"nodes": nodes, "edges": edges,
"iterations": iterations}).encode()
r = subprocess.run(
[str(BINARY)], input=payload,
capture_output=True, timeout=30
)
result = json.loads(r.stdout)
return result["nodes"]
def force_layout_3d(nodes, edges, iterations=80):
if _BINARY_OK:
return _force_layout_binary(nodes, edges, iterations)
if _CUPY_OK:
return _force_layout_gpu(nodes, edges, iterations)
return _force_layout_cpu(nodes, edges, iterations)
우선순위: C++ 바이너리 → CuPy GPU → NumPy CPU (3단계 fallback)
4. 안정성 비교
| 항목 | Python CuPy | C++ CUDA 바이너리 |
|---|---|---|
| CUDA 라이브러리 의존 | 런타임 동적 로드 (취약) | 컴파일 시 정적 고정 |
| 버전 업그레이드 내성 | libcurand.so 변경 시 즉시 실패 | 빌드 시점 ABI 고정 |
| Python 환경 의존 | venv/시스템 python 경로 민감 | Python 완전 무관 |
| 배포 단위 | pip install (버전 종속) | 단일 바이너리 파일 |
| 디버그 | Python traceback | CUDA-GDB / cuda-memcheck |
5. 예상 성능 비교
| 구분 | n=120, iter=80 | 비고 |
|---|---|---|
| NumPy CPU | 217 ms | 기존 |
| CuPy GPU (현재) | 37 ms | Python 오버헤드 포함 |
| C++ CUDA 바이너리 (목표) | 5~15 ms | subprocess JSON 오버헤드 포함 |
| subprocess 고정 오버헤드 | ~5~20 ms | 프로세스 시작 + JSON 파싱 |
계산 커널 자체는 CuPy 대비 동등하거나 빠를 것으로 예측. 단, subprocess 호출 오버헤드(5~20ms)가 추가되므로 소규모 그래프에서는 CuPy와 유사. L1·L2 캐시가 있으므로 첫 요청에만 영향.
6. 구현 단계 계획
| 단계 | 작업 | 조건 |
|---|---|---|
| Phase 0 | nvcc 설치 확인 (cuda-toolkit-13-2) |
sudo 권한 필요 (Moojoco 확인) |
| Phase 1 | layout_engine.cu 구현 + 단독 테스트 |
nvcc 설치 후 |
| Phase 2 | viz_server.py 연동 + 3단계 fallback | Phase 1 완료 후 |
| Phase 3 | 성능 측정 + thesis 보고서 제출 | Phase 2 완료 후 |
Phase 0 선결 조건: hb5u에서 sudo apt-get install cuda-toolkit-13-2 실행 가능 여부. Moojoco 또는 사령관 확인 필요.
7. 검증 기준
구현 완료 후 다음 조건 모두 충족 시 완료:
1. ldd layout_engine — libcuda.so.1 외 CUDA 라이브러리 없음
2. ./layout_engine 입력/출력 JSON 왕복 < 20ms (n=120)
3. viz_server /health → layout_engine: cpp-cuda
4. CUDA 툴킷 삭제 후에도 바이너리 정상 동작 (런타임 libcuda.so만으로 실행)
