PCIe 공동 처리
Neat PCIe 호스트 API를 사용하면 호스트 시스템의 애플리케이션이 텐서 또는 이미지를 연결된 Modalix PCIe 카드에 전송하고 추론 결과를 받을 수 있습니다. 호스트 시스템이 애플리케이션 I/O 및 오케스트레이션을 담당하고 카드가 컴파일된 모델과 구성된 전처리 또는 후처리를 실행할 때 사용합니다.
이는 DevKit에서 직접 실행되는 Neat Library와는 별개의 API입니다. 공개 유형은 simaai::neat::pcie C++ 네임스페이스와 pyneatpcie Python 패키지에 있습니다.
호스트 시스템에 core/pciehost를 설치합니다. Neat SDK 컨테이너 내부나 Modalix PCIe 카드에 설치하지 마십시오. 이 페이지를 사용하기 전에 PCIe 호스트를 설치합니다. 지침을 따르십시오.
공동 처리 방식
하나의 pcie::Model은 하나의 물리적 PCIe 큐에서 실행되는 하나의 컴파일된 모델을 나타냅니다.
- 생성자는 로컬 모델 아카이브를 읽고 해당 입력 및 출력 계약을 노출합니다.
build()는 아카이브를 PCIe 가상 네트워크를 통해 카드에 업로드하고, 카드 측 파이프라인을 시작하 고, 준비될 때까지 기다립니다.run()또는push()는 PCIe를 통해 입력 페이로드를 보냅니다.- 카드는 사전 처리, 추론 및 구성된 후처리를 실행합니다.
run()또는pull()는 출력 텐서를 호스트로 반환합니다.close()는 카드 측 파이프라인을 중지하고 큐를 해제합니다.
모델 아카이브는 build() 중에 전송됩니다. 추론 페이로드 및 결과는 PCIe 데이터 전송을 사용합니다.
연결 구성
ConnectionOptions는 이 모델에서 사용하는 카드와 큐를 식별합니다.
| 필드 | 기본값 | 목적 |
|---|---|---|
card_host | 비어 있음 | 명시적 SSH/SCP 주소입니다. 비어 있는 경우 카드 N은 10.0.N.2를 사용합니다. |
card_id | 0 | 호스트 PCIe 플러그인에 전달되는 카드 번호입니다. |
user | sima | 카드 측 SSH 및 SCP에 대한 사용자입니다. |
queue | 0 | 0에서 3까지의 공동 처리 큐입니다. |
max_inflight | 10 | 결과를 기다리는 최대 허용 입력 수입니다. |
큐 0의 10.0.0.2에 있는 하나의 카드에 대해 기본값을 사용합니다. 카드가 다른 관리 주소를 사용하는 경우 card_host를 명시적 으로 설정합니다.
#include <simaai/neat/pcie/Model.h>
namespace pcie = simaai::neat::pcie;
pcie::ConnectionOptions connection;
connection.card_host = "10.0.0.2";
connection.card_id = 0;
connection.queue = 0;
connection.max_inflight = 10;
모델 검사 및 빌드
구성은 로컬에서 이루어지며, 카드를 시작하지 않습니다. 입력을 할당하기 전에 info()를 검사한 다음, 공동 처리 세션을 시작하기 위해 build()를 한 번 호출합니다.
pcie::Model model("model.tar.gz", {}, connection);
const pcie::ModelInfo info = model.info();
for (const auto& input : info.inputs) {
std::cout << input.name << " requires " << input.size_bytes << " bytes\n";
}
model.build(/*readiness_timeout_ms=*/180000);
input_specs()와 output_specs()는 각각 동일한 목록을 반환합니다.
running()은 성공적인 빌드 후에 true로 변경되고, close() 후에 false로 돌아갑니다.
동기 추론 실행
가장 간단한 요청/응답 흐름을 위해 run()을 사용합니다. 먼저 모델을 빌드하고, 애플리케이션 오류로 인해 무한정 대기하지 않도록 유한한 시간 제한을 설정합니다.
다음 예제는 보고된 입력 데이터 유형이 FP32인 모델에 대한 입력을 생성합니다.
const auto& input_spec = info.inputs.front();
if (input_spec.dtype != "FP32") {
throw std::runtime_error("this example requires an FP32 model input");
}
std::vector<float> values(input_spec.size_bytes / sizeof(float), 0.0f);
pcie::Tensor input = pcie::Tensor::from_vector(
std::move(values), input_spec.shape, input_spec.name);
pcie::TensorList outputs = model.run(input, /*timeout_ms=*/30000);
model.close();
다중 입력 모델의 경우, 논리적 입력마다 하나의 Tensor를 순서대로 전달하고, info().inputs에서 보고하는 경로 이름을 사용합니다.
run() 시간 초과 시 대기는 중단되지만, 이미 카드가 수락한 입력은 취소되지 않습니다. 시간 초과가 발생한 후에는 pull()을 사용하여 해당 대기 중인 결과를 처리하거나, 새 요청 시퀀스를 시작하기 전에 close()를 호출합니다.
푸시 및 풀을 사용하는 파이프라인 요청
입력 준비와 추론이 동시에 진행되어야 하는 경우 push() 및 pull()을 사용합니다. max_inflight는 아직 반환되지 않은 수락된 작업의 양을 제한합니다. 결과를 즉시 가져와서 프로듀서가 계속 작업을 진행할 수 있도록 합니다.
std::size_t pushed = 0;
std::size_t pulled = 0;
while (pulled < inputs.size()) {
while (pushed < inputs.size() && pushed - pulled < 10) {
model.push(inputs[pushed++]);
}
auto outputs = model.pull(/*timeout_ms=*/30000);
if (!outputs) {
throw std::runtime_error("PCIe inference timed out");
}
consume(*outputs);
++pulled;
}
push()는 max_inflight가 가득 찰 때까지 대기하므로, 결과를 가져오지 않고 구성된 윈도우보다 많은 데이터를 제출하지 마십시오. pull()은 이 모델에 사용할 수 있는 다음 결과를 반환합니다. run()을 호출하기 전에 push()로 제출된 모든 결과를 처리하십시오.
이미지 전송 및 전처리 구성
디코딩된 이미지 데이터를 전송할 때 preprocess.kind를 Image로 설정합니다. 카드 측의 Neat 파이프라인은 지원되는 객체 감지 출력의 크기를 조정하고, 색상을 변환하고, 정규화하고, 디코딩할 수 있습니다.
이 예제에서는 BGR 이미지를 전송하고, 모델 아카이브에서 추론된 모델 입력에 맞게 이미지의 비율을 조정한 다음, 디코딩된 YOLOv8 BBOX 페이로드를 포함하는 텐서를 반환합니다.
#include <opencv2/imgcodecs.hpp>
pcie::ModelOptions options;
options.preprocess.kind = pcie::InputKind::Image;
options.preprocess.color_convert.input_format = pcie::ColorFormat::BGR;
options.preprocess.resize.enable = pcie::AutoFlag::On;
options.preprocess.resize.mode = pcie::ResizeMode::Letterbox;
options.decode_type = pcie::BoxDecodeType::YoloV8;
options.score_threshold = 0.25f;
options.nms_iou_threshold = 0.45f;
options.top_k = 100;
pcie::Model detector("yolo_v8n_mpk.tar.gz", options, connection);
detector.build();
cv::Mat image = cv::imread("image.jpg", cv::IMREAD_COLOR);
pcie::TensorList detections = detector.run(image, /*timeout_ms=*/30000);
detector.close();
input_max_width, input_max_height 또는 input_max_depth를 설정하지 마세요.
입력값이 없는 모델의 경우, 애플리케이션에서 명시적인 입력 제한이 필요한 경우가 아니라면 설정할 필요가 없습니다. Neat은 모델 아카이브에서 모델 측의 크기 조정 대상을 추론할 수 있습니다.
안정적으로 닫기
모델이 더 이상 필요하지 않을 때와 큐를 재사용하기 전에 close()를 호출하세요. 여러 번 호출해도 안전합니다.
model = pcie.Model("model.tar.gz", connection=connection)
model.build()
outputs = model.run([input_tensor], timeout_ms=30000)
model.close()
또는 컨텍스트 관리자를 사용하여 모델을 자동으로 닫을 수 있습니다.
with pcie.Model("model.tar.gz", connection=connection) as model:
model.build()
outputs = model.run([input_tensor], timeout_ms=30000)
컨텍스트 관리자는 블록이 종료될 때, 예외가 발생할 때를 포함하여 close()를 호출합니다. with 블록 내에 다른 명시적인 close()를 추가하지 마십시오.
C++ 호스트 애플리케이션 빌드
개발 패키지는 SimaPCIeHost CMake 패키지를 제공합니다.
cmake_minimum_required(VERSION 3.16)
project(pcie_model LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
find_package(SimaPCIeHost REQUIRED CONFIG)
add_executable(pcie_model main.cpp)
target_link_libraries(pcie_model PRIVATE SimaPCIeHost::sima_neat_pcie_host)
해당 호스트 머신에서 이 애플리케이션을 네이티브 방식으로 빌드합니다.
C++ 이미지 예제에서도 OpenCV를 사용합니다. 해당 애플리케이션 대상에 헤더 파일과 라이브러리를 추가합니다.
find_package(OpenCV REQUIRED COMPONENTS core imgcodecs)
target_include_directories(pcie_model PRIVATE ${OpenCV_INCLUDE_DIRS})
target_link_libraries(pcie_model PRIVATE ${OpenCV_LIBS})
현재 범위 및 제한 사항
- 하나의
pcie::Model은 하나의 PCIe 큐를 소유합니다. 큐 범위는0에서3까지입니다. - Modalix EV74는 최대 네 개의 동시 코프로세싱 파이프라인을 지원합니다.
- 동일한 큐에 두 개의 활성 모델을 할당하지 마십시오.
- 카드에 설치된 호스트 패키지와 Neat Library는 호환되는 버전이어야 합니다.
- 첫 번째 페이로드가 제출된 후 입력 미디어 유형과 형식을 안정적으로 유지하십시오. 활성 전송 용량보다 큰 후속 페이로드는 거부됩니다.
- PCIe 호스트 API는 Neat 모델의 전처리 및 객체 디코딩 옵션의 제한된 하위 집합을 지원합니다.
- 이 코프로세싱 API는 호스트 측
Graph,Node또는Run구성을 노출하지 않습니다. 네이티브 애플리케이션 그래프를 위해 DevKit에서 일반 Neat Library를 사용하십시오.
설치 및 연결 확인을 위해 PCIe 호스트를 설치합니다.로 돌아가십시오.