triton_cpp v1.2.1
Header-only C++ wrapper for NVIDIA Triton Inference Server clients
Loading...
Searching...
No Matches
triton_interface.hpp
Go to the documentation of this file.
1// Copyright Institute for Automotive Engineering (ika), RWTH Aachen University
2// SPDX-License-Identifier: Apache-2.0
3
4#pragma once
5
6#include <optional>
7#include <variant>
8
9#include <Eigen/Dense>
10#include <eigen3/unsupported/Eigen/CXX11/Tensor>
11
12#if defined(TRITON_CPP_ENABLE_CUDA_SHM) && !defined(TRITON_ENABLE_GPU)
13#define TRITON_ENABLE_GPU
14#endif
15
16#include <common.h>
17#include <grpc_client.h>
18#include <grpc_service.pb.h>
19#include <ipc.h>
20#include <model_config.pb.h>
21
23#include "triton_cpp/shm.hpp"
24#include "triton_cpp/types.hpp"
25#include "triton_cpp/utils.hpp"
26
27namespace triton_cpp {
28
37 public:
52 TritonInterface(const std::string& model_name,
53 const std::string& model_version,
54 const std::string& server_url,
55 bool shm,
56 bool variable_input_size = false,
57 bool retry_connection = false,
58 double client_timeout_s = 0.0,
59 bool cuda_input_shm = false)
60 : options_{model_name}, shm_{shm}, variable_input_size_{variable_input_size}, cuda_input_shm_requested_{cuda_input_shm} {
61 if ((shm || cuda_input_shm) && variable_input_size) {
62 throw std::invalid_argument("Variable input size and shared memory cannot be combined");
63 }
64 options_.model_version_ = model_version;
65 if (client_timeout_s < 0.0) {
66 throw std::invalid_argument("client_timeout_s must be >= 0.0");
67 }
68 options_.client_timeout_ = static_cast<decltype(options_.client_timeout_)>(client_timeout_s * 1e6);
69 triton::client::Error err;
70 err = triton::client::InferenceServerGrpcClient::Create(&triton_client_, server_url, false);
71 while (retry_connection && !err.IsOk()) {
72 std::cerr << "Failed to create Triton client: " << err.Message() << ". Retrying..." << std::endl;
73 std::this_thread::sleep_for(std::chrono::seconds(1));
74 err = triton::client::InferenceServerGrpcClient::Create(&triton_client_, server_url, false);
75 }
76 if (!err.IsOk()) {
77 throw std::runtime_error("Failed to create Triton client: " + err.Message());
78 }
79 inference::ModelConfigResponse model_config;
80 err = triton_client_->ModelConfig(&model_config, model_name, model_version);
81 while (retry_connection && !err.IsOk()) {
82 std::cerr << "Failed to get model config from Triton server: " << err.Message() << ". Retrying..." << std::endl;
83 std::this_thread::sleep_for(std::chrono::seconds(1));
84 err = triton_client_->ModelConfig(&model_config, model_name, model_version);
85 }
86 if (!err.IsOk()) {
87 throw std::runtime_error("Failed to get model config from Triton server: " + err.Message());
88 }
89
90 std::tie(input_metadata_, output_metadata_) = build_model_info(model_config);
91
92 if (cuda_input_shm_requested_) {
93#if defined(TRITON_CPP_ENABLE_CUDA_SHM)
94 inference::ServerMetadataResponse server_metadata;
95 const auto metadata_status = triton_client_->ServerMetadata(&server_metadata);
96 if (!metadata_status.IsOk()) {
97 throw std::runtime_error(
98 "CUDA input shared memory was requested, but Triton server metadata could not be "
99 "queried: " +
100 metadata_status.Message());
101 } else {
102 const bool server_supports_cuda_shm = std::find(server_metadata.extensions().begin(), server_metadata.extensions().end(),
103 "cuda_shared_memory") != server_metadata.extensions().end();
104 std::string reason;
105 if (!server_supports_cuda_shm) {
106 throw std::runtime_error(
107 "CUDA input shared memory was requested, but the Triton server does not advertise "
108 "cuda_shared_memory support");
109 } else if (!LocalCudaSharedMemorySupported(&reason)) {
110 throw std::runtime_error(
111 "CUDA input shared memory was requested, but local CUDA shared memory is not "
112 "available: " +
113 reason);
114 } else {
115 cuda_input_shm_enabled_ = true;
116 }
117 }
118#else
119 throw std::runtime_error(
120 "CUDA input shared memory was requested, but triton_cpp was built without CUDA SHM "
121 "support");
122#endif
123 }
124 }
125
128
137 ~TritonInterface() { releaseSharedMemoryRegistrations(); };
139
148 void initInOutputs(std::optional<std::map<std::string, std::vector<int64_t>>> special_output_shapes,
149 std::optional<std::map<std::string, std::vector<int64_t>>> special_input_shapes = std::nullopt) {
150 releaseSharedMemoryRegistrations();
151 input_shm_.reset();
152#if defined(TRITON_CPP_ENABLE_CUDA_SHM)
153 input_cuda_shm_.reset();
154#endif
155 output_shm_.reset();
156 inputs_.clear();
157 outputs_.clear();
158 if (special_input_shapes.has_value()) {
159 for (const auto& [name, shape] : special_input_shapes.value()) {
160 auto it = input_metadata_.find(name);
161 if (it == input_metadata_.end()) {
162 throw std::invalid_argument("Input name not found in model metadata: " + name);
163 }
164 const auto datatype = it->second.datatype;
165 input_metadata_.erase(it);
166 input_metadata_.emplace(name, InputOutputMetaData{shape, datatype});
167 }
168 }
169
170 if (special_output_shapes.has_value()) {
171 for (const auto& [name, shape] : special_output_shapes.value()) {
172 auto it = output_metadata_.find(name);
173 if (it == output_metadata_.end()) {
174 throw std::invalid_argument("Output name not found in model metadata: " + name);
175 }
176 const auto datatype = it->second.datatype;
177 output_metadata_.erase(it);
178 output_metadata_.emplace(name, InputOutputMetaData{shape, datatype});
179 }
180 }
181 if (!variable_input_size_) {
182 if (cuda_input_shm_enabled_) {
183 try {
184 setup_cuda_shm_inputs(input_metadata_);
185 } catch (const std::exception& e) {
186 throw std::runtime_error("CUDA input shared memory was requested, but initialization failed: " + std::string(e.what()));
187 }
188 } else if (shm_) {
189 setup_shm_inputs(input_metadata_);
190 } else {
191 setup_standard_inputs(input_metadata_);
192 }
193 }
194
195 if (shm_) {
196 setup_shm_outputs(output_metadata_);
197 } else {
198 setup_standard_outputs(output_metadata_);
199 }
200
201 // Also store the raw pointers to in/outputs in vectors, as required by the Triton client
202 // We don't have to delete these anywhere, as they are managed by the respective shared pointers
203 if (!variable_input_size_) {
204 raw_inputs_.clear();
205 for (const auto& input : inputs_) {
206 raw_inputs_.push_back(input.second.input.get());
207 }
208 }
209
210 raw_outputs_.clear();
211 for (const auto& output : outputs_) {
212 raw_outputs_.push_back(output.second.get());
213 }
214 }
215
222 void infer() {
223 if (variable_input_size_) {
224 raw_inputs_.clear();
225 for (const auto& input : inputs_) {
226 raw_inputs_.push_back(input.second.input.get());
227 }
228 }
229 triton::client::InferResult* raw_results{nullptr};
230 auto status = triton_client_->Infer(&raw_results, options_, raw_inputs_, raw_outputs_);
231 if (!status.IsOk()) {
232 throw(std::runtime_error("ModelInfer failed: " + status.Message()));
233 }
234 results_.reset(raw_results);
235 };
236
246 template <typename T>
247 VectorType<T> getInputTensor(const std::string& name, int64_t rows) {
248 if (variable_input_size_) {
249 CreateInputTensor(name, rows);
250 }
251 auto& input = inputs_[name];
252 if (!input.isHostMappable()) {
253 throw std::invalid_argument("Input tensor '" + name + "' is backed by CUDA shared memory and is not host-mappable");
254 }
255 if (rows * sizeof(T) != input.data_raw_size) {
256 std::stringstream ss;
257 ss << name << ": rows: " << rows << " sizeof(T): " << sizeof(T) << " input.data_raw_size: " << input.data_raw_size
258 << std::endl;
259 throw std::invalid_argument("Invalid input tensor size: " + ss.str());
260 }
261 return VectorType<T>{reinterpret_cast<T*>(input.data_raw), rows};
262 }
263
274 template <typename T>
275 MatrixType<T> getInputTensor(const std::string& name, int64_t rows, int64_t cols) {
276 if (variable_input_size_) {
277 CreateInputTensor(name, rows, cols);
278 }
279 auto& input = inputs_[name];
280 if (!input.isHostMappable()) {
281 throw std::invalid_argument("Input tensor '" + name + "' is backed by CUDA shared memory and is not host-mappable");
282 }
283 if (rows * cols * sizeof(T) != input.data_raw_size) {
284 std::stringstream ss;
285 ss << name << ": rows: " << rows << " cols: " << cols << " sizeof(T): " << sizeof(T)
286 << " input.data_raw_size: " << input.data_raw_size << std::endl;
287 throw std::invalid_argument("Invalid input tensor size: " + ss.str());
288 }
289 return MatrixType<T>{reinterpret_cast<T*>(input.data_raw), rows, cols};
290 }
291
304 template <typename T, typename... DimType>
305 TensorType<T, sizeof...(DimType) + 3> getInputTensor(
306 const std::string& name, int64_t dim0, int64_t dim1, int64_t dim2, DimType... dims) {
307 if (variable_input_size_) {
308 CreateInputTensor(name, dim0, dim1, dim2, dims...);
309 }
310 auto& input = inputs_[name];
311 if (!input.isHostMappable()) {
312 throw std::invalid_argument("Input tensor '" + name + "' is backed by CUDA shared memory and is not host-mappable");
313 }
314 if (((dim0 * dim1 * dim2) * ... * dims) * sizeof(T) != input.data_raw_size) {
315 std::stringstream ss;
316 // Print the further dims, separated by commas
317 ss << name << ": dims: " << dim0 << ", " << dim1 << ", " << dim2;
318 ((ss << ", " << dims), ...);
319 ss << " sizeof(T): " << sizeof(T) << " input.data_raw_size: " << input.data_raw_size << std::endl;
320 throw std::invalid_argument("Invalid input tensor size: " + ss.str());
321 }
322 return TensorType<T, sizeof...(dims) + 3>{reinterpret_cast<T*>(input.data_raw), dim0, dim1, dim2, dims...};
323 }
324
333 std::pair<uint8_t*, std::size_t> getInputTensor(const std::string& name) {
334 if (variable_input_size_ && inputs_.count(name) == 0) {
335 throw(std::invalid_argument("Variable input size is enabled, but no size was provided for input " + name));
336 }
337 auto& input = inputs_[name];
338 if (!input.isHostMappable()) {
339 throw std::invalid_argument("Input tensor '" + name + "' is backed by CUDA shared memory and is not host-mappable");
340 }
341 return {input.data_raw, input.data_raw_size};
342 }
343
349 bool usesCudaInputSharedMemory() const { return cuda_input_shm_enabled_; }
350
358 std::pair<uint8_t*, std::size_t> getInputTensorDevice(const std::string& name) {
359 auto& input = inputs_.at(name);
360 if (!input.isDeviceBacked()) {
361 throw std::invalid_argument("Input tensor '" + name + "' is not backed by CUDA shared memory");
362 }
363 return {input.device_data_raw, input.data_raw_size};
364 }
365
376 void copyInputTensorToDevice(const std::string& name, const void* host_data, std::size_t bytes) {
377#if defined(TRITON_CPP_ENABLE_CUDA_SHM)
378 auto& input = inputs_.at(name);
379 if (!input.isDeviceBacked()) {
380 throw std::invalid_argument("Input tensor '" + name + "' is not backed by CUDA shared memory");
381 }
382 if (bytes != input.data_raw_size) {
383 throw std::invalid_argument("Input tensor '" + name + "' byte size mismatch for host-to-device copy");
384 }
385 throw_on_cuda_error(cudaMemcpy(input.device_data_raw, host_data, bytes, cudaMemcpyHostToDevice), "cudaMemcpy");
386#else
387 // Suppress unused-parameter warnings when CUDA SHM support is not compiled in.
388 (void)name;
389 (void)host_data;
390 (void)bytes;
391 throw std::invalid_argument("triton_cpp was built without CUDA SHM support");
392#endif
393 }
394
404 template <typename T>
405 VectorType<const T> getOutputTensor(const std::string& name, int64_t rows) const {
406 auto [raw_data_buf, raw_data_size] = getOutputTensor(name);
407 if (raw_data_size != rows * sizeof(T)) {
408 std::stringstream ss;
409 ss << name << ": rows: " << rows << " sizeof(T): " << sizeof(T) << " raw_data_size: " << raw_data_size << std::endl;
410 throw std::invalid_argument("Invalid output tensor size: " + ss.str());
411 }
412 return VectorType<const T>{reinterpret_cast<const T*>(raw_data_buf), rows};
413 }
414
425 template <typename T>
426 MatrixType<const T> getOutputTensor(const std::string& name, int64_t rows, int64_t cols) const {
427 auto [raw_data_buf, raw_data_size] = getOutputTensor(name);
428 if (raw_data_size != rows * cols * sizeof(T)) {
429 std::stringstream ss;
430 ss << name << ": rows: " << rows << " cols: " << cols << " sizeof(T): " << sizeof(T) << " raw_data_size: " << raw_data_size
431 << std::endl;
432 throw std::invalid_argument("Invalid output tensor size: " + ss.str());
433 }
434 return MatrixType<const T>{reinterpret_cast<const T*>(raw_data_buf), rows, cols};
435 }
436
449 template <typename T, typename... DimType>
450 TensorType<const T, sizeof...(DimType) + 3> getOutputTensor(
451 const std::string& name, int64_t dim0, int64_t dim1, int64_t dim2, DimType... dims) const {
452 auto [raw_data_buf, raw_data_size] = getOutputTensor(name);
453 if (raw_data_size != ((dim0 * dim1 * dim2) * ... * dims) * sizeof(T)) {
454 std::stringstream ss;
455 ss << name << ": dims: " << dim0 << ", " << dim1 << ", " << dim2;
456 ((ss << ", " << dims), ...);
457 ss << " sizeof(T): " << sizeof(T) << " raw_data_size: " << raw_data_size << std::endl;
458 throw std::invalid_argument("Invalid output tensor size: " + ss.str());
459 }
460 return TensorType<const T, sizeof...(dims) + 3>{reinterpret_cast<const T*>(raw_data_buf), dim0, dim1, dim2, dims...};
461 }
462
469 std::pair<const uint8_t*, std::size_t> getOutputTensor(const std::string& name) const {
470 const uint8_t* raw_data_buf{nullptr};
471 std::size_t raw_data_size;
472 if (shm_) {
473 auto shm = outputs_.at(name);
474 std::size_t offset;
475 std::string shm_name;
476 shm->SharedMemoryInfo(&shm_name, &raw_data_size, &offset);
477 raw_data_buf = output_shm_->getAddress() + offset;
478 } else {
479 results_->RawData(name, &raw_data_buf, &raw_data_size);
480 }
481 return {raw_data_buf, raw_data_size};
482 }
483
489 std::string getModelInfo() const { return model_info_; }
490
496 std::size_t nInputs() const { return input_metadata_.size(); }
497
503 std::size_t nOutputs() const { return output_metadata_.size(); }
504
512 std::vector<int64_t> getInputShape(const std::string& name) const {
513 auto it = input_metadata_.find(name);
514 if (it == input_metadata_.end()) {
515 throw std::invalid_argument("Input name not found in model metadata: " + name);
516 }
517 return it->second.shape;
518 }
519
527 std::vector<int64_t> getOutputShape(const std::string& name) const {
528 auto it = output_metadata_.find(name);
529 if (it == output_metadata_.end()) {
530 throw std::invalid_argument("Output name not found in model metadata: " + name);
531 }
532 return it->second.shape;
533 }
534
535 private:
536 void releaseSharedMemoryRegistrations() {
537 if (triton_client_ == nullptr) {
538 return;
539 }
540
541 if (cuda_input_shm_enabled_) {
542 const auto status = triton_client_->UnregisterCudaSharedMemory(INPUT_SHM_NAME);
543 if (!status.IsOk()) {
544 std::cerr << "Failed to unregister Triton CUDA shared memory region '" << INPUT_SHM_NAME << "': " << status.Message()
545 << std::endl;
546 }
547 } else if (shm_) {
548 const auto status = triton_client_->UnregisterSystemSharedMemory(INPUT_SHM_NAME);
549 if (!status.IsOk()) {
550 std::cerr << "Failed to unregister Triton system shared memory region '" << INPUT_SHM_NAME << "': " << status.Message()
551 << std::endl;
552 }
553 }
554
555 if (shm_) {
556 const auto status = triton_client_->UnregisterSystemSharedMemory(OUTPUT_SHM_NAME);
557 if (!status.IsOk()) {
558 std::cerr << "Failed to unregister Triton system shared memory region '" << OUTPUT_SHM_NAME << "': " << status.Message()
559 << std::endl;
560 }
561 }
562 }
563
564 std::pair<std::map<std::string, InputOutputMetaData>, std::map<std::string, InputOutputMetaData>> build_model_info(
565 const inference::ModelConfigResponse& model_config) {
566 std::pair<std::map<std::string, InputOutputMetaData>, std::map<std::string, InputOutputMetaData>> metadata;
567 std::stringstream model_info_builder;
568 int n_inputs = model_config.config().input_size();
569 for (int i{0}; i < n_inputs; ++i) {
570 auto input = model_config.config().input(i);
571 std::vector<int64_t> shape{};
572 if (model_config.config().max_batch_size() != 0) {
573 // Triton omits the batch dimension from the reported tensor dims, but InferInput expects it.
574 shape.push_back(1);
575 }
576
577 for (int j{0}; j < input.dims_size(); ++j) {
578 shape.push_back(input.dims(j));
579 }
580 metadata.first.emplace(input.name(), InputOutputMetaData{shape, input.data_type()});
581 model_info_builder << "input name: " << input.name() << '\n';
582 model_info_builder << "input datatype: " << inference::DataType_Name(input.data_type()) << '\n';
583 model_info_builder << "input dims: " << (input.dims_size() + (model_config.config().max_batch_size() != 0))
584 << ", shape: " << shape << "\n\n";
585 }
586 model_info_builder << "-------------------\n";
587
588 int n_outputs = model_config.config().output_size();
589 for (int i{0}; i < n_outputs; ++i) {
590 auto output = model_config.config().output(i);
591 std::vector<int64_t> shape;
592 for (int j{0}; j < output.dims_size(); ++j) {
593 shape.push_back(output.dims(j));
594 }
595 metadata.second.emplace(output.name(), InputOutputMetaData{shape, output.data_type()});
596 model_info_builder << "output name: " << output.name() << '\n';
597 model_info_builder << "output datatype: " << inference::DataType_Name(output.data_type()) << '\n';
598 model_info_builder << "output dims: " << output.dims_size() << ", shape: " << shape << "\n\n";
599 }
600 model_info_builder << "-------------------\n";
601 model_info_ = model_info_builder.str();
602 return metadata;
603 }
604
605 template <typename... DimType>
606 void CreateInputTensor(const std::string& name, DimType... dims) {
607 if (input_metadata_.find(name) == input_metadata_.end()) {
608 throw std::invalid_argument("No input named " + name + " is known.");
609 }
610 auto& meta = input_metadata_.at(name);
611 InputOutputMetaData modified_meta = InputOutputMetaData{std::vector<int64_t>{dims...}, meta.datatype};
612 triton::client::InferInput* input_ptr{nullptr};
613 triton::client::InferInput::Create(&input_ptr, name, std::vector<int64_t>{dims...},
614 inference::DataType_Name(modified_meta.datatype).substr(5));
615 inputs_[name] = {std::shared_ptr<triton::client::InferInput>(input_ptr), std::vector<uint8_t>(modified_meta.bytesize, 0)};
616 inputs_[name].input->AppendRaw(inputs_[name].data.data(), inputs_[name].data.size());
617 }
618
619 void setup_standard_inputs(const std::map<std::string, InputOutputMetaData>& metadata) {
620 for (const auto& [name, meta] : metadata) {
621 triton::client::InferInput* input_ptr{nullptr};
622 triton::client::InferInput::Create(&input_ptr, name, meta.shape, inference::DataType_Name(meta.datatype).substr(5));
623 inputs_[name] = {std::shared_ptr<triton::client::InferInput>(input_ptr), std::vector<uint8_t>(meta.bytesize, 0)};
624 inputs_[name].input->AppendRaw(inputs_[name].data.data(), inputs_[name].data.size());
625 }
626 }
627
628 std::size_t computeSharedMemorySize(const std::map<std::string, InputOutputMetaData>& metadata) const {
629 // Offsets are aligned by datatype with a minimum alignment of 8 bytes.
630 std::size_t total_size = 0;
631 for (const auto& [_, meta] : metadata) {
632 total_size = alignUp(total_size, getSharedMemoryAlignment(meta.datatype));
633 total_size += static_cast<std::size_t>(meta.bytesize);
634 }
635 return total_size;
636 }
637
638 void setup_shm_inputs(const std::map<std::string, InputOutputMetaData>& metadata) {
639 const auto shm_size = static_cast<std::int64_t>(computeSharedMemorySize(metadata));
640 input_shm_ = std::make_unique<SharedMemoryRegion>(INPUT_SHM_KEY, shm_size);
641 const uint8_t* input_shm_begin = input_shm_->getAddress();
642 std::size_t current_offset = 0;
643
644 fail_on_error(triton_client_->RegisterSystemSharedMemory(INPUT_SHM_NAME, input_shm_->getKey(), shm_size),
645 "RegisterSystemSharedMemory");
646 for (const auto& [name, meta] : metadata) {
647 std::size_t current_shm_size = meta.bytesize;
648 current_offset = alignUp(current_offset, getSharedMemoryAlignment(meta.datatype));
649 uint8_t* current_input_shm = input_shm_->getAddress() + current_offset;
650 triton::client::InferInput* input_ptr{nullptr};
651 triton::client::InferInput::Create(&input_ptr, name, meta.shape, inference::DataType_Name(meta.datatype).substr(5));
652 inputs_[name] = {std::shared_ptr<triton::client::InferInput>(input_ptr), current_input_shm, current_shm_size};
653 inputs_[name].input->SetSharedMemory(INPUT_SHM_NAME, current_shm_size, current_input_shm - input_shm_begin);
654 current_offset += current_shm_size;
655 }
656 }
657
658 void setup_cuda_shm_inputs(const std::map<std::string, InputOutputMetaData>& metadata) {
659#if defined(TRITON_CPP_ENABLE_CUDA_SHM)
660 const auto shm_size = static_cast<std::int64_t>(computeSharedMemorySize(metadata));
661 input_cuda_shm_ = std::make_unique<CudaSharedMemoryRegion>(INPUT_SHM_NAME, shm_size);
662 const uint8_t* input_shm_begin = input_cuda_shm_->getDeviceAddress();
663 std::size_t current_offset = 0;
664
665 fail_on_error(triton_client_->RegisterCudaSharedMemory(INPUT_SHM_NAME, input_cuda_shm_->getIpcHandle(),
666 input_cuda_shm_->getDeviceId(), shm_size),
667 "RegisterCudaSharedMemory");
668
669 for (const auto& [name, meta] : metadata) {
670 std::size_t current_shm_size = meta.bytesize;
671 current_offset = alignUp(current_offset, getSharedMemoryAlignment(meta.datatype));
672 uint8_t* current_input_shm = input_cuda_shm_->getDeviceAddress() + current_offset;
673 triton::client::InferInput* input_ptr{nullptr};
674 triton::client::InferInput::Create(&input_ptr, name, meta.shape, inference::DataType_Name(meta.datatype).substr(5));
675 inputs_[name] = {std::shared_ptr<triton::client::InferInput>(input_ptr), nullptr, current_input_shm, current_shm_size};
676 inputs_[name].input->SetSharedMemory(INPUT_SHM_NAME, current_shm_size, current_input_shm - input_shm_begin);
677 current_offset += current_shm_size;
678 }
679#else
680 (void)metadata;
681 throw std::invalid_argument("triton_cpp was built without CUDA SHM support");
682#endif
683 }
684
685 void setup_standard_outputs(const std::map<std::string, InputOutputMetaData>& metadata) {
686 for (const auto& [name, meta] : metadata) {
687 triton::client::InferRequestedOutput* output_ptr{nullptr};
688 triton::client::InferRequestedOutput::Create(&output_ptr, name);
689 outputs_[name] = (std::shared_ptr<triton::client::InferRequestedOutput>(output_ptr));
690 }
691 }
692
693 void setup_shm_outputs(const std::map<std::string, InputOutputMetaData>& metadata) {
694 const auto shm_size = static_cast<std::int64_t>(computeSharedMemorySize(metadata));
695 output_shm_ = std::make_unique<SharedMemoryRegion>(OUTPUT_SHM_KEY, shm_size);
696 const uint8_t* output_shm_begin = output_shm_->getAddress();
697 std::size_t current_offset = 0;
698
699 fail_on_error(triton_client_->RegisterSystemSharedMemory(OUTPUT_SHM_NAME, output_shm_->getKey(), shm_size),
700 "RegisterSystemSharedMemory");
701 for (const auto& [name, meta] : metadata) {
702 std::size_t current_shm_size = meta.bytesize;
703 current_offset = alignUp(current_offset, getSharedMemoryAlignment(meta.datatype));
704 uint8_t* current_output_shm = output_shm_->getAddress() + current_offset;
705 triton::client::InferRequestedOutput* output_ptr{nullptr};
706 triton::client::InferRequestedOutput::Create(&output_ptr, name);
707 outputs_[name] = (std::shared_ptr<triton::client::InferRequestedOutput>(output_ptr));
708 outputs_[name]->SetSharedMemory(OUTPUT_SHM_NAME, current_shm_size, current_output_shm - output_shm_begin);
709 current_offset += current_shm_size;
710 }
711 }
712
713 triton::client::InferOptions options_; // Reused Triton inference options for model/version/timeout.
714 bool shm_; // Enables system shared memory for input and output transport.
715 bool variable_input_size_; // Defers input tensor creation until the caller provides shapes.
716 bool cuda_input_shm_requested_ = false; // Records whether CUDA input SHM was explicitly requested.
717 bool cuda_input_shm_enabled_ = false; // True once CUDA input SHM support has been validated and activated.
718 std::unique_ptr<triton::client::InferenceServerGrpcClient> triton_client_; // Underlying Triton gRPC client.
719 std::map<std::string, InputOutputMetaData> input_metadata_; // Declared input tensor shapes and datatypes.
720 std::map<std::string, InputOutputMetaData> output_metadata_; // Declared output tensor shapes and datatypes.
721 std::unique_ptr<SharedMemoryRegion> input_shm_; // Backing system SHM region for host-visible input tensors.
722#if defined(TRITON_CPP_ENABLE_CUDA_SHM)
723 std::unique_ptr<CudaSharedMemoryRegion> input_cuda_shm_; // Backing CUDA SHM region for device-resident inputs.
724#endif
725 std::unique_ptr<SharedMemoryRegion> output_shm_; // Backing system SHM region for output tensors.
726 std::string model_info_; // Cached human-readable summary of the Triton model interface.
727 std::map<std::string, InputData> inputs_; // Input tensor handles and their backing buffers keyed by name.
728 ModelOutput outputs_; // Requested output handles keyed by tensor name.
729 std::shared_ptr<triton::client::InferResult> results_; // Most recent Triton inference result.
730 std::vector<triton::client::InferInput*> raw_inputs_; // Raw input handles passed to Triton infer calls.
731 std::vector<const triton::client::InferRequestedOutput*> raw_outputs_; // Raw output handles passed to Triton infer calls.
732
733 const std::string RANDOM_INSTANCE_STRING = randstring(10); // Per-instance suffix to avoid SHM name collisions.
734 const std::string INPUT_SHM_NAME = "input_data_" + RANDOM_INSTANCE_STRING; // Triton-visible input SHM region name.
735 const std::string INPUT_SHM_KEY = "/triton_cpp_input_" + RANDOM_INSTANCE_STRING; // POSIX key for input system SHM.
736 const std::string OUTPUT_SHM_NAME = "output_data_" + RANDOM_INSTANCE_STRING; // Triton-visible output SHM region name.
737 const std::string OUTPUT_SHM_KEY = "/triton_cpp_output_" + RANDOM_INSTANCE_STRING; // POSIX key for output system SHM.
738};
739
740} // namespace triton_cpp
Synchronous, typed interface to one model served by Triton.
std::size_t nInputs() const
Get the number of inputs.
TritonInterface(const TritonInterface &)=delete
Copy construction is disabled because the interface owns registrations and buffers.
std::vector< int64_t > getOutputShape(const std::string &name) const
Get the Output Shape of a tensor.
~TritonInterface()
Unregister shared-memory regions owned by this interface.
TritonInterface(const std::string &model_name, const std::string &model_version, const std::string &server_url, bool shm, bool variable_input_size=false, bool retry_connection=false, double client_timeout_s=0.0, bool cuda_input_shm=false)
Connect to Triton and query metadata for one model.
TritonInterface & operator=(const TritonInterface &)=delete
Copy assignment is disabled because the interface owns registrations and buffers.
std::pair< uint8_t *, std::size_t > getInputTensor(const std::string &name)
Get the raw input buffer.
bool usesCudaInputSharedMemory() const
Whether input tensors are currently backed by Triton CUDA shared memory.
TensorType< const T, sizeof...(DimType)+3 > getOutputTensor(const std::string &name, int64_t dim0, int64_t dim1, int64_t dim2, DimType... dims) const
Get a view to the OutputBuffer, interpreted as a Tensor of rank >=3.
TensorType< T, sizeof...(DimType)+3 > getInputTensor(const std::string &name, int64_t dim0, int64_t dim1, int64_t dim2, DimType... dims)
Get a view to the InputBuffer, interpreted as a Tensor of rank >=3.
MatrixType< const T > getOutputTensor(const std::string &name, int64_t rows, int64_t cols) const
Get a view to the OutputBuffer, interpreted as a Matrix.
std::size_t nOutputs() const
Get the number of outputs.
void initInOutputs(std::optional< std::map< std::string, std::vector< int64_t > > > special_output_shapes, std::optional< std::map< std::string, std::vector< int64_t > > > special_input_shapes=std::nullopt)
Creates all input and output buffers for the model, based on the model metadata.
VectorType< T > getInputTensor(const std::string &name, int64_t rows)
Get a view to the InputBuffer, interpreted as a Vector.
std::vector< int64_t > getInputShape(const std::string &name) const
Get the Input Shape of a tensor.
std::pair< const uint8_t *, std::size_t > getOutputTensor(const std::string &name) const
Get the raw output buffer and its size.
TritonInterface(TritonInterface &&)=delete
Move construction is disabled to keep registered buffer addresses stable.
void infer()
Run synchronous inference with the data currently stored in the input buffers.
MatrixType< T > getInputTensor(const std::string &name, int64_t rows, int64_t cols)
Get a view to the InputBuffer, interpreted as a Matrix.
std::pair< uint8_t *, std::size_t > getInputTensorDevice(const std::string &name)
Get the device pointer for a CUDA shared-memory-backed input tensor.
std::string getModelInfo() const
Get a description of the model's in and outputs as human-readable text.
void copyInputTensorToDevice(const std::string &name, const void *host_data, std::size_t bytes)
Copy host data into a CUDA shared-memory-backed input tensor.
VectorType< const T > getOutputTensor(const std::string &name, int64_t rows) const
Get a view to the OutputBuffer, interpreted as a Vector.
TritonInterface & operator=(TritonInterface &&)=delete
Move assignment is disabled to keep registered buffer addresses stable.
std::size_t getSharedMemoryAlignment(inference::DataType type)
Return the alignment used when packing tensors into shared memory.
Definition types.hpp:126
typename std::conditional_t< std::is_const_v< T >, Eigen::Map< const Eigen::Matrix< typename std::remove_const_t< T >, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > >, Eigen::Map< Eigen::Matrix< T, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor > > > MatrixType
Helper type template for Eigen::Matrix compatible with Triton.
Definition types.hpp:41
std::string randstring(std::size_t len)
Generate an alphanumeric random string.
Definition utils.hpp:76
void fail_on_error(const triton::client::Error &err, std::string message="")
Convert a failed Triton client status into a C++ exception.
Definition utils.hpp:65
typename std::conditional_t< std::is_const_v< T >, Eigen::Map< const Eigen::VectorX< typename std::remove_const_t< T > > >, Eigen::Map< Eigen::VectorX< T > > > VectorType
Helper type template for Eigen::Vector compatible with Triton.
Definition types.hpp:31
void throw_on_cuda_error(cudaError_t status, const char *operation)
Throw a descriptive exception when a CUDA Runtime API call fails.
Definition cuda_shm.hpp:24
typename std::conditional_t< std::is_const_v< T >, Eigen::TensorMap< const Eigen::Tensor< typename std::remove_const_t< T >, rank, Eigen::RowMajor, Eigen::Index > >, Eigen::TensorMap< Eigen::Tensor< T, rank, Eigen::RowMajor, Eigen::Index > > > TensorType
Helper type template for Eigen::Tensor compatible with Triton.
Definition types.hpp:55
std::size_t alignUp(std::size_t offset, std::size_t alignment)
Round an offset up to an alignment boundary.
Definition types.hpp:137
bool LocalCudaSharedMemorySupported(std::string *reason=nullptr)
Check whether this process can allocate CUDA IPC shared memory.
Definition cuda_shm.hpp:36
std::map< std::string, std::shared_ptr< triton::client::InferRequestedOutput > > ModelOutput
Map model output names to Triton requested-output objects.
Definition types.hpp:148
Shape, datatype, and byte-size metadata for one model tensor.
Definition types.hpp:272