์นดํ…Œ๊ณ ๋ฆฌ ์—†์Œ

OpenCV CUDA ์‹ค์‹œ๊ฐ„ ์˜์ƒ ์ฒ˜๋ฆฌ์˜ ๋ณ‘๋ชฉ, Pinned Memory์™€ ๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆผ์„ ํ™œ์šฉํ•œ ์ตœ์ ํ™” ๊ฐ€์ด๋“œ

๊ฒŒ์ž„๊ต์ˆ˜ 2026. 9. 1. 13:01
๋ฐ˜์‘ํ˜•

๐ŸŒ English Abstract

High-resolution real-time image processing using OpenCV CUDA often suffers from performance bottlenecks during Host-to-Device (H2D) data transfers over the PCIe bus. This article explores how to eliminate transfer latencies by leveraging Pinned Memory (Page-locked Memory) via cv::cuda::HostMem and overlapping memory copy with GPU execution using CUDA Asynchronous Streams (cv::cuda::Stream). It provides deep-dive troubleshooting strategies, practical C++ implementation patterns, and edge-case optimizations for embedded systems like NVIDIA Jetson.

1. ๊ฐœ์š”: GPU ์—ฐ์‚ฐ๋ณด๋‹ค ๋ฌด์„œ์šด ๋ฐ์ดํ„ฐ ์ „์†ก ๋ณ‘๋ชฉ (PCIe Bottleneck)

์‹ค์‹œ๊ฐ„ ์˜์ƒ ์ฒ˜๋ฆฌ ์‹œ์Šคํ…œ(Real-time Image Processing System) ๊ตฌ์ถ• ์‹œ, ๋งŽ์€ ์—”์ง€๋‹ˆ์–ด๋“ค์ด OpenCV CUDA ๋ชจ๋“ˆ์„ ๋„์ž…ํ•˜๋ฉด ์ฆ‰๊ฐ์ ์ธ ํ”„๋ ˆ์ž„๋ ˆ์ดํŠธ(FPS) ์ƒ์Šน์„ ๊ธฐ๋Œ€ํ•ฉ๋‹ˆ๋‹ค. ๊ทธ๋Ÿฌ๋‚˜ ์‹ค์ œ ๊ณ ํ•ด์ƒ๋„(4K ์ด์ƒ) ์นด๋ฉ”๋ผ ์ŠคํŠธ๋ฆผ์„ ์ฒ˜๋ฆฌํ•˜๋‹ค ๋ณด๋ฉด, GPU ์ปค๋„(Kernel) ์ˆ˜ํ–‰ ์‹œ๊ฐ„์€ ๋ถˆ๊ณผ ์ˆ˜ ๋ฐ€๋ฆฌ์ดˆ(ms)์— ๋ถˆ๊ณผํ•จ์—๋„ ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ํ”„๋ ˆ์ž„ ๋“œ๋กญ(Frame Drop)์ด ๋ฐœ์ƒํ•˜๋Š” ํ˜„์ƒ์„ ์ž์ฃผ ๋ชฉ๊ฒฉํ•˜๊ฒŒ ๋ฉ๋‹ˆ๋‹ค.

์ด๋Ÿฌํ•œ ๋ฌธ์ œ์˜ ๊ทผ๋ณธ ์›์ธ์€ GPU์˜ ์—ฐ์‚ฐ ๋Šฅ๋ ฅ ๋ถ€์กฑ์ด ์•„๋‹ˆ๋ผ, CPU์˜ ํ˜ธ์ŠคํŠธ ๋ฉ”๋ชจ๋ฆฌ(Host Memory)์—์„œ GPU์˜ ์žฅ์น˜ ๋ฉ”๋ชจ๋ฆฌ(Device Memory)๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•˜๋Š” ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ณ‘๋ชฉ ํ˜„์ƒ(Host-to-Device Bottleneck)์— ์žˆ์Šต๋‹ˆ๋‹ค. PCIe(Peripheral Component Interconnect Express) ๋ฒ„์Šค ๋Œ€์—ญํญ์˜ ํ•œ๊ณ„์™€ ๋™๊ธฐ์‹(Synchronous) ๋ฐ์ดํ„ฐ ๋ณต์‚ฌ ๋ฐฉ์‹์ด ๊ฒฐํ•ฉํ•˜๋ฉด, GPU๋Š” ์—ฐ์‚ฐ์„ ๋Œ€๊ธฐํ•˜๋ฉฐ ๋†€๊ฒŒ(Idle) ๋˜๊ณ  CPU ์—ญ์‹œ ๋ธ”๋กœํ‚น(Blocking) ์ƒํƒœ์— ๋น ์ง‘๋‹ˆ๋‹ค.

๋ณธ ๊ธ€์—์„œ๋Š” ์ด ๋ณ‘๋ชฉ์„ ๊ทผ๋ณธ์ ์œผ๋กœ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด Pinned Memory(Page-locked Memory)์˜ ๋ฉ”์ปค๋‹ˆ์ฆ˜์„ ํŒŒํ—ค์น˜๊ณ , CUDA ๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆผ(Asynchronous Stream)์„ ๊ฒฐํ•ฉํ•˜์—ฌ ์ „์†ก๊ณผ ์—ฐ์‚ฐ์„ ์™„์ „ํ•˜๊ฒŒ ์˜ค๋ฒ„๋žฉ(Overlap)ํ•˜๋Š” ๊ณ ์„ฑ๋Šฅ ํŒŒ์ดํ”„๋ผ์ธ ๊ตฌ์ถ•๋ฒ•์„ ๋‹ค๋ฃน๋‹ˆ๋‹ค.



2. ๋ฉ”์ปค๋‹ˆ์ฆ˜ ๋ถ„์„: Pageable Memory vs Pinned Memory

๊ธฐ๋ณธ์ ์œผ๋กœ ์šด์˜์ฒด์ œ(OS)๊ฐ€ ํ• ๋‹นํ•˜๋Š” ์ผ๋ฐ˜์ ์ธ ๋ฉ”๋ชจ๋ฆฌ๋Š” ํŽ˜์ด์ง€ ๊ฐ€๋Šฅ ๋ฉ”๋ชจ๋ฆฌ(Pageable Memory)์ž…๋‹ˆ๋‹ค. OS ๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ ๊ด€๋ฆฌ์ž(Virtual Memory Manager)๋Š” ํ•„์š”์— ๋”ฐ๋ผ ์ด ๋ฉ”๋ชจ๋ฆฌ์˜ ๋ฌผ๋ฆฌ ์ฃผ์†Œ๋ฅผ ๋ณ€๊ฒฝํ•˜๊ฑฐ๋‚˜ ๋””์Šคํฌ์˜ ์Šค์™‘ ์˜์—ญ(Swap Space)์œผ๋กœ ๋‚ด๋ณด๋‚ผ(Page-out) ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ cv::Mat ๋ฐ์ดํ„ฐ ์ „์†ก์˜ ๋น„ํšจ์œจ์„ฑ

์šด์˜์ฒด์ œ์˜ ๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ ํŠน์„ฑ ๋•Œ๋ฌธ์—, GPU DMA(Direct Memory Access) ์ปจํŠธ๋กค๋Ÿฌ๋Š” Pageable Memory์—์„œ ์ง์ ‘ ์žฅ์น˜ ๋ฉ”๋ชจ๋ฆฌ๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณต์‚ฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค. ๋”ฐ๋ผ์„œ ๊ธฐ๋ณธ cv::Mat ๊ฐ์ฒด๋ฅผ GPU๋กœ ์ „์†ก(upload)ํ•  ๋•Œ ๋‚ด๋ถ€์ ์œผ๋กœ ๋‹ค์Œ๊ณผ ๊ฐ™์€ 2๋‹จ๊ณ„ ๊ณผ์ •์ด ๊ฐ•์ œ๋กœ ์‹คํ–‰๋ฉ๋‹ˆ๋‹ค:

  1. ์ž„์‹œ Pinned Memory ํ• ๋‹น ๋ฐ ๋ณต์‚ฌ: CUDA ๋“œ๋ผ์ด๋ฒ„๊ฐ€ ํ˜ธ์ŠคํŠธ ์ธก์— ์Šคํ…Œ์ด์ง• ๋ฒ„ํผ(Staging Buffer) ์—ญํ• ์˜ Pinned Memory๋ฅผ ์ž„์‹œ ํ• ๋‹นํ•˜๊ณ , Pageable ๋ฉ”๋ชจ๋ฆฌ์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ด๊ณณ์œผ๋กœ ๋ณต์‚ฌํ•ฉ๋‹ˆ๋‹ค (CPU L3 ์บ์‹œ/๋ฉ”๋ชจ๋ฆฌ ๋Œ€์—ญํญ ์†Œ๋น„).
  2. PCIe DMA ์ „์†ก: ์Šคํ…Œ์ด์ง• ๋ฒ„ํผ์˜ ๋ฐ์ดํ„ฐ๋ฅผ DMA๋ฅผ ํ†ตํ•ด GPU VRAM(Device Memory)์œผ๋กœ ์ „์†กํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ณผ์ •์—์„œ ๋ฐœ์ƒํ•˜๋Š” ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ๋Œ€ ๋ฉ”๋ชจ๋ฆฌ ๋ณต์‚ฌ(Memcpy)์™€ ๋™๊ธฐํ™” ๋Œ€๊ธฐ ์‹œ๊ฐ„์€ ๊ณ ์ฃผ์‚ฌ์œจ ์˜์ƒ ์ฒ˜๋ฆฌ์—์„œ ์‹ฌ๊ฐํ•œ ์ง€์—ฐ์‹œ๊ฐ„(Latency)์„ ์ดˆ๋ž˜ํ•ฉ๋‹ˆ๋‹ค.

Pinned Memory (Page-locked Memory)์˜ ์›๋ฆฌ

Pinned Memory๋Š” OS์—๊ฒŒ "์ด ๋ฉ”๋ชจ๋ฆฌ ํŽ˜์ด์ง€๋Š” ์ ˆ๋Œ€๋กœ ๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ ์Šค์™‘์„ ํ•˜์ง€ ๋ง๊ณ  ๋ฌผ๋ฆฌ RAM ์œ„์น˜์— ๊ณ ์ •(Lock)ํ•ด๋ผ"๋ผ๊ณ  ๋ช…๋ นํ•˜์—ฌ ์ƒ์„ฑํ•œ ๋ฉ”๋ชจ๋ฆฌ์ž…๋‹ˆ๋‹ค. OpenCV์—์„œ๋Š” cv::cuda::HostMem ํด๋ž˜์Šค๋ฅผ ํ†ตํ•ด ์ด๋ฅผ ์ง€์›ํ•ฉ๋‹ˆ๋‹ค.

  • DMA ์ง์ฃผํ–‰: GPU DMA ์ปจํŠธ๋กค๋Ÿฌ๊ฐ€ ํ˜ธ์ŠคํŠธ RAM์˜ ๊ณ ์ •๋œ ๋ฌผ๋ฆฌ ์ฃผ์†Œ์— ์ง์ ‘ ์ ‘๊ทผํ•˜์—ฌ PCIe ๋ฒ„์Šค๋กœ ๋ฐ์ดํ„ฐ๋ฅผ ์ „์†กํ•ฉ๋‹ˆ๋‹ค. Intermediate Staging Buffer ๋ณต์‚ฌ๊ฐ€ ์ƒ๋žต๋ฉ๋‹ˆ๋‹ค.
  • ๋น„๋™๊ธฐ ์ „์†ก ๊ฐ€๋Šฅ: ๋ฉ”๋ชจ๋ฆฌ ์œ„์น˜๊ฐ€ ๊ณ ์ •๋˜์–ด ์žˆ์œผ๋ฏ€๋กœ CPU ์ž‘์—…๊ณผ GPU ๋ฐ์ดํ„ฐ ์ „์†ก ์ž‘์—…์„ ๋น„๋™๊ธฐ(Asynchronous)๋กœ ์™„๋ฒฝํžˆ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.


3. ์‹ค์ „ ์ตœ์ ํ™” ๊ตฌํ˜„: cv::cuda::HostMem๊ณผ cv::cuda::Stream ํŒŒ์ดํ”„๋ผ์ด๋‹

์‹ค์‹œ๊ฐ„ ํŒŒ์ดํ”„๋ผ์ธ์—์„œ ์„ฑ๋Šฅ์„ ๊ทน๋Œ€ํ™”ํ•˜๋ ค๋ฉด cv::cuda::HostMem ์‚ฌ์šฉ์— ๊ทธ์น˜์ง€ ์•Š๊ณ , CUDA ๋น„๋™๊ธฐ ์ŠคํŠธ๋ฆผ(Stream)์„ ํ™œ์šฉํ•ด ํ”„๋ ˆ์ž„ $N$์˜ ์ „์†ก, ํ”„๋ ˆ์ž„ $N-1$์˜ GPU ์—ฐ์‚ฐ, ํ”„๋ ˆ์ž„ $N-2$์˜ ๊ฒฐ๊ณผ ์ˆ˜์‹ (D2H)์„ ๋™์‹œ์— ์ง„ํ–‰ํ•˜๋Š” ๋”๋ธ”/ํŠธ๋ฆฌํ”Œ ๋ฒ„ํผ๋ง(Double/Triple Buffering) ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์ถ•ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋น„๋™๊ธฐ ํŒŒ์ดํ”„๋ผ์ธ C++ ์†Œ์Šค์ฝ”๋“œ ์˜ˆ์ œ

#include <opencv2/opencv.hpp>
#include <opencv2/cudaimgproc.hpp>
#include <opencv2/cudawarping.hpp>
#include <iostream>
#include <vector>

constexpr int STREAM_COUNT = 2; // Double Buffering

struct FrameBuffer {
    cv::cuda::HostMem host_pinned_in;   // Pinned Memory for Input
    cv::cuda::HostMem host_pinned_out;  // Pinned Memory for Output
    cv::cuda::GpuMat  gpu_in;
    cv::cuda::GpuMat  gpu_processed;
    cv::cuda::Stream  stream;
};

int main() {
    int width = 3840;
    int height = 2160;
    
    // 1. ๋ฒ„ํผ ๋ฐ ์ŠคํŠธ๋ฆผ ์ดˆ๊ธฐํ™” (์‚ฌ์ „ ํ• ๋‹น)
    std::vector<FrameBuffer> buffers(STREAM_COUNT);
    for (int i = 0; i < STREAM_COUNT; ++i) {
        // PAGE_LOCKED ์˜ต์…˜์„ ํ†ตํ•œ Pinned Memory ํ• ๋‹น
        buffers[i].host_pinned_in = cv::cuda::HostMem(height, width, CV_8UC3, cv::cuda::HostMem::AllocType::PAGE_LOCKED);
        buffers[i].host_pinned_out = cv::cuda::HostMem(height, width, CV_8UC1, cv::cuda::HostMem::AllocType::PAGE_LOCKED);
    }

    // ๋ฐ์ดํ„ฐ ์ˆ˜์ง‘ ๋ผ์ด๋ธŒ ๋ฃจํ”„ (๊ฐ€์ƒ)
    int frame_idx = 0;
    while (true) {
        int buf_idx = frame_idx % STREAM_COUNT;
        FrameBuffer& buf = buffers[buf_idx];

        // ์ด์ „ ํ”„๋ ˆ์ž„์˜ ์ŠคํŠธ๋ฆผ ์ž‘์—… ์™„๋ฃŒ ์—ฌ๋ถ€ ํ™•์ธ ๋ฐ ๋™๊ธฐํ™”
        buf.stream.waitForCompletion();

        // [CPU Step] ์นด๋ฉ”๋ผ ๋“ฑ ํ”„๋ ˆ์ž„ Grabber๋กœ๋ถ€ํ„ฐ Pinned Memory ํฌ์ธํ„ฐ๋กœ ์ง๋Œ€์ž…
        cv::Mat frame_view = buf.host_pinned_in.createMatHeader();
        // simulate_camera_capture(frame_view.data); // Zero-copy into Pinned Memory

        // [Non-blocking CUDA Steps]
        // 1. Host-to-Device ๋น„๋™๊ธฐ ์ „์†ก
        buf.gpu_in.upload(buf.host_pinned_in, buf.stream);

        // 2. GPU ์—ฐ์‚ฐ ์ปค๋„ ํ ๋“ฑ๋ก (๋น„๋™๊ธฐ)
        cv::cuda::cvtColor(buf.gpu_in, buf.gpu_processed, cv::COLOR_BGR2GRAY, 0, buf.stream);
        cv::cuda::gaussianBlur(buf.gpu_processed, buf.gpu_processed, cv::Size(5, 5), 1.5, 0, cv::BORDER_DEFAULT, buf.stream);

        // 3. Device-to-Host ๋น„๋™๊ธฐ ์ „์†ก
        buf.gpu_processed.download(buf.host_pinned_out, buf.stream);

        // CPU๋Š” ๊ธฐ๋‹ค๋ฆฌ์ง€ ์•Š๊ณ  ์ฆ‰์‹œ ๋‹ค์Œ ํ”„๋ ˆ์ž„ ๋ฃจํ”„๋กœ ์ง„์ž…ํ•˜์—ฌ ๋‹ค๋ฅธ ์ŠคํŠธ๋ฆผ ์ง€์‹œ
        frame_idx++;
        if (frame_idx > 1000) break; // ํ…Œ์ŠคํŠธ ์ข…๋ฃŒ ์กฐ๊ฑด
    }

    // ๋ชจ๋“  ์ŠคํŠธ๋ฆผ ์ž‘์—… ์™„์ˆ˜ ๋Œ€๊ธฐ
    for (auto& buf : buffers) {
        buf.stream.waitForCompletion();
    }

    return 0;
}

4. ์‹ค๋ฌด ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… ๋ฐ ์—ฃ์ง€ ์ผ€์ด์Šค (Troubleshooting & Edge Cases)

1) Pinned Memory ๊ณผ๋‹ค ํ• ๋‹น์œผ๋กœ ์ธํ•œ ์‹œ์Šคํ…œ ๋ฝ์—… (System OOM & Thrashing)

Pinned Memory๋Š” OS๊ฐ€ ๊ฐ€์ƒ ๋ฉ”๋ชจ๋ฆฌ๋กœ ํŽ˜์ด์ง€ ์•„์›ƒ์„ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์—†๋Š” Non-paged Pool ๋ฉ”๋ชจ๋ฆฌ ์˜์—ญ์„ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค. ์‹ค์‹œ๊ฐ„ ์„ฑ๋Šฅ์„ ๊ทน๋Œ€ํ™”ํ•˜๊ณ ์ž ๋ชจ๋“  ํ”„๋ ˆ์ž„ ๋ฒ„ํผ๋ฅผ Pinned Memory๋กœ ๊ฑฐ๋Œ€ํ•˜๊ฒŒ ํ• ๋‹น(์˜ˆ: ์ˆ˜์‹ญ ๊ธฐ๊ฐ€๋ฐ”์ดํŠธ)ํ•˜๋ฉด, OS ์ž์ฒด์™€ ๋‹ค๋ฅธ ํ”„๋กœ์„ธ์Šค๊ฐ€ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋Š” ์Šค์™€ํ•‘ ๊ฐ€๋Šฅํ•œ ๋ฌผ๋ฆฌ RAM ์˜์—ญ์ด ๊ทน๋‹จ์ ์œผ๋กœ ์ค„์–ด๋“ญ๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ์ฑ…: ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ์ ์— ring-buffer ํ˜•ํƒœ์˜ cv::cuda::HostMem์„ ์ตœ์†Œํ•œ์˜ ๊ฐœ์ˆ˜(์˜ˆ: 2~4๊ฐœ)๋งŒ ๋ฏธ๋ฆฌ ํ”„๋ฆฌ์–ดํฌ๋กœ์ผ€์ด์…˜(Pre-allocation)ํ•˜์—ฌ ์žฌ์‚ฌ์šฉํ•˜์‹ญ์‹œ์˜ค. ๋™์  ํ• ๋‹น/ํ•ด์ œ(malloc/free)๋ฅผ ๋ฃจํ”„ ๋‚ด๋ถ€์—์„œ ๋ฐ˜๋ณตํ•˜๋ฉด cudaHostAlloc ์ž์ฒด์˜ ๋ฌด๊ฑฐ์šด ์‹œ์Šคํ…œ ์ฝœ ์ปจํ…์ŠคํŠธ ์Šค์œ„์นญ ๋น„์šฉ ๋•Œ๋ฌธ์— ์˜คํžˆ๋ ค ์„ฑ๋Šฅ์ด ์•…ํ™”๋ฉ๋‹ˆ๋‹ค.

2) ์ž„๋ฒ ๋””๋“œ ํ”Œ๋žซํผ(NVIDIA Jetson)์—์„œ์˜ ์ œ๋กœ์นดํ”ผ(Zero-Copy) ๋ฉ”๋ชจ๋ฆฌ ์ฐฉ๊ฐ

NVIDIA Jetson Xavier / Orin ๋“ฑ๊ณผ ๊ฐ™์€ ์ž„๋ฒ ๋””๋“œ SoC ํ™˜๊ฒฝ์€ CPU์™€ GPU๊ฐ€ ๋™์ผํ•œ ๋ฌผ๋ฆฌ DRAM์„ ๊ณต์œ ํ•˜๋Š” ํ†ตํ•ฉ ๋ฉ”๋ชจ๋ฆฌ ๊ตฌ์กฐ(Unified Memory Architecture, UMA)๋ฅผ ๊ฐ€์ง‘๋‹ˆ๋‹ค. discrete GPU(๋ฐ์Šคํฌํ†ฑ/์„œ๋ฒ„์šฉ NVLink, PCIe ์นด๋“œ) ํ™˜๊ฒฝ๊ณผ๋Š” ๋ฉ”๋ชจ๋ฆฌ ์ ‘๊ทผ ๋ฉ”์ปค๋‹ˆ์ฆ˜์ด ๋‹ค๋ฆ…๋‹ˆ๋‹ค.

Jetson ํ™˜๊ฒฝ์—์„œ๋Š” AllocType::SHARED ์˜ต์…˜์ด๋‚˜ Unified Memory๋ฅผ ํ†ตํ•ด upload()/download() ํ˜ธ์ถœ ์ž์ฒด๋ฅผ ์•„์˜ˆ ์ƒ๋žตํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. Discrete GPU ์ „์šฉ ์ตœ์ ํ™” ๊ธฐ๋ฒ•(PCIe ๋Œ€์—ญํญ ๊ทน๋Œ€ํ™”์šฉ Pinned Memory)์„ Jetson์— ๊ทธ๋Œ€๋กœ ์ ์šฉํ•˜๋ฉด ๋ถˆํ•„์š”ํ•œ ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ ํ”Œ๋Ÿฌ์‹œ(Cache Flush) ๋ฐ ๋™๊ธฐํ™” ์˜ค๋ฒ„ํ—ค๋“œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์œผ๋ฏ€๋กœ target hardware์— ๋”ฐ๋ฅธ ๋ถ„๊ธฐ ์ฒ˜๋ฆฌ๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

3) ๋ฉ€ํ‹ฐ์Šค๋ ˆ๋“œ ๋ฉ€ํ‹ฐ์ŠคํŠธ๋ฆผ ๊ฒฝ์Ÿ ์ƒํƒœ (Data Race & Synchronization Failures)

ํ”„๋ ˆ์ž„์„ ์ˆ˜์‹ ํ•˜๋Š” ์ƒ์‚ฐ์ž ์Šค๋ ˆ๋“œ(Producer Thread)์™€ GPU๋กœ ์ด์†ก ๋ฐ ์—ฐ์‚ฐ์„ ์ง€์‹œํ•˜๋Š” ์†Œ๋น„์ž ์Šค๋ ˆ๋“œ(Consumer Thread)๊ฐ€ ๋ถ„๋ฆฌ๋˜์–ด ์žˆ์„ ๋•Œ, Pinned ๋ฉ”๋ชจ๋ฆฌ์˜ ํฌ์ธํ„ฐ ์ ‘๊ทผ ์‹œ์  ๋™๊ธฐํ™”๊ฐ€ ์ œ๋Œ€๋กœ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์œผ๋ฉด **ํ™”๋ฉด ๊นจ์ง(Tearing)**์ด๋‚˜ **๋ฉ”๋ชจ๋ฆฌ ์˜ค์—ผ(Corruption)**์ด ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•ด๊ฒฐ์ฑ…: cv::cuda::Stream::query()๋ฅผ ํ™œ์šฉํ•œ ๋น„๋™๊ธฐ ํด๋ง ์ƒํƒœ ์ฒดํฌ๋‚˜ cv::cuda::Event๋ฅผ ์ด์šฉํ•ด GPU ์ „์†ก ์ž‘์—…์ด ์™„์ „ํžˆ ๋๋‚œ ํ›„ ์Šค๋ ˆ๋“œ๊ฐ€ ๋ฒ„ํผ์— ๋ฎ์–ด์“ฐ๋„๋ก ๋น„๋™๊ธฐ ์ด๋ฒคํŠธ๋ฅผ ๋™๊ธฐํ™” ํ”Œ๋ž˜๊ทธ๋กœ ํ™œ์šฉํ•˜์„ธ์š”.

 

๐Ÿ’ก ์š”์•ฝ: PCIe ๋ณ‘๋ชฉ ํ•ด๊ฒฐ 3๋‹จ๊ณ„ ์ฒดํฌ๋ฆฌ์ŠคํŠธ
  • ์ผ๋ฐ˜ cv::Mat์„ ๋ฃจํ”„ ๋‚ด๋ถ€์—์„œ ์ง์ ‘ upload() ํ•˜์ง€ ๋ง๊ณ , cv::cuda::HostMem์„ ์‚ฌ์šฉ ์ค‘์ธ๊ฐ€?
  • cv::cuda::Stream ๊ฐ์ฒด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ CPU-GPU ์ „์†ก ๋ฐ ์—ฐ์‚ฐ์„ ๋น„๋™๊ธฐํ™” ํ•˜์˜€๋Š”๊ฐ€?
  • ๋ฉ”๋ชจ๋ฆฌ ํ• ๋‹น(Allocation)๊ณผ ํ•ด์ œ(Deallocation)๊ฐ€ ํ•ซ๋ฃจํ”„(Hot Loop) ๋ฐ–์—์„œ ์‚ฌ์ „ ์™„๋ฃŒ๋˜์—ˆ๋Š”๊ฐ€?


5. ์„ฑ๋Šฅ ์ธก์ • ๋ถ„์„ (Benchmarking)

์•„๋ž˜ ํ‘œ๋Š” 4K (3840x2160, RGB 3์ฑ„๋„) ํ•ด์ƒ๋„ ์˜์ƒ์„ ๊ธฐ์ค€์œผ๋กœ, ๋™๊ธฐ์‹ ์ผ๋ฐ˜ ๋ฉ”๋ชจ๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ๊ณผ ๋น„๋™๊ธฐ Pinned Memory ์ŠคํŠธ๋ฆผ ํŒŒ์ดํ”„๋ผ์ธ์˜ ๋ ˆ์ดํ„ด์‹œ ๋ฐ ํ”„๋ ˆ์ž„ ์ฒ˜๋ฆฌ ์†๋„๋ฅผ ๋น„๊ตํ•œ ์˜ˆ์‹œ ๊ฒฐ๊ณผ์ž…๋‹ˆ๋‹ค.

์ ์šฉ ๋ฐฉ์‹ (Strategy) H2D ์ „์†ก ์ง€์—ฐ (Latency) GPU ์ฒ˜๋ฆฌ ๋ฐ D2H ์ „์ฒด FPS (Frame Rate)
1. ๋™๊ธฐ์‹ + Pageable (๊ธฐ๋ณธ) 18.5 ms (Blocking) 4.2 ms ~44 FPS
2. ๋™๊ธฐ์‹ + Pinned Memory 8.1 ms (Blocking) 4.2 ms ~81 FPS
3. ๋น„๋™๊ธฐ Stream + Pinned (์ตœ์ ํ™”) 0 ms (Overlapped) 4.2 ms (Overlapped) 230+ FPS

NVIDIA Nsight Systems ํƒ€์ž„๋ผ์ธ ํ”„๋กœํŒŒ์ผ๋Ÿฌ๋กœ ๋ถ„์„ ์‹œ, ์ตœ์ ํ™” ์ ์šฉ ํ›„์—๋Š” ์ „์†ก ํƒ€์ž„๋ผ์ธ(MemCpy H2D)๊ณผ ์ปค๋„ ์‹คํ–‰ ํƒ€์ž„๋ผ์ธ(Kernel Execution)์ด ์™„๋ฒฝํžˆ ์ˆ˜์ง์œผ๋กœ ์ค‘์ฒฉ(Overlap)๋˜์–ด PCIe ์ „์†ก ์ง€์—ฐ์ด ์ „์ฒด ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰ ์‹œ๊ฐ„์—์„œ ์™„์ „ํžˆ ์€ํ(Hide)๋˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

6. ๊ฒฐ๋ก 

OpenCV CUDA ๋ชจ๋“ˆ์„ ํ™œ์šฉํ•œ ๊ณ ์„ฑ๋Šฅ ์˜์ƒ ์ฒ˜๋ฆฌ ์‹œ์Šคํ…œ์—์„œ CPU-GPU ๊ฐ„ ๋ฐ์ดํ„ฐ ์ „์†ก์€ ์‹œ์Šคํ…œ ์ „์ฒด์˜ ์„ฑ๋Šฅ์„ ๊ฒฐ์ •์ง“๋Š” ํ•ต์‹ฌ ์ œ์–ด ์š”์†Œ์ž…๋‹ˆ๋‹ค. cv::Mat์˜ ๋‹จ์ˆœ upload() ๊ตฌ์กฐ์—์„œ ๋ฒ—์–ด๋‚˜ cv::cuda::HostMem์„ ํ†ตํ•œ Pinned Memory ํ™œ์šฉ๊ณผ cv::cuda::Stream ๊ธฐ๋ฐ˜์˜ ๋น„๋™๊ธฐ ํŒŒ์ดํ”„๋ผ์ด๋‹์„ ๋„์ž…ํ•˜๋Š” ๊ฒƒ๋งŒ์œผ๋กœ๋„ ๋ณต์žกํ•œ ์ปค๋„ ์•Œ๊ณ ๋ฆฌ์ฆ˜ ์ˆ˜์ • ์—†์ด ์ฒ˜๋ฆฌ๋Ÿ‰์„ 2~4๋ฐฐ ์ด์ƒ ๊ทน์ ์œผ๋กœ ํ–ฅ์ƒ์‹œํ‚ฌ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ ์ดˆ๊ธฐ๋ถ€ํ„ฐ ๋ฉ”๋ชจ๋ฆฌ ํ”„๋ฆฌ์–ดํฌ๋กœ์ผ€์ด์…˜๊ณผ ๋ฉ€ํ‹ฐ ์ŠคํŠธ๋ฆผ ๊ตฌ์กฐ๋ฅผ ๋ฐ˜์˜ํ•˜์—ฌ ์‹ค์‹œ๊ฐ„ ์˜์ƒ ์ฒ˜๋ฆฌ ํŒŒ์ดํ”„๋ผ์ธ์˜ ์ˆจ์€ ์„ฑ๋Šฅ์„ 100% ์ด๋Œ์–ด๋‚ด์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค.

๋ฐ˜์‘ํ˜•