53 const std::string& model_version,
54 const std::string& server_url,
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");
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");
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);
77 throw std::runtime_error(
"Failed to create Triton client: " + err.Message());
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);
87 throw std::runtime_error(
"Failed to get model config from Triton server: " + err.Message());
90 std::tie(input_metadata_, output_metadata_) = build_model_info(model_config);
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 "
100 metadata_status.Message());
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();
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");
110 throw std::runtime_error(
111 "CUDA input shared memory was requested, but local CUDA shared memory is not "
115 cuda_input_shm_enabled_ =
true;
119 throw std::runtime_error(
120 "CUDA input shared memory was requested, but triton_cpp was built without CUDA SHM "
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();
152#if defined(TRITON_CPP_ENABLE_CUDA_SHM)
153 input_cuda_shm_.reset();
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);
164 const auto datatype = it->second.datatype;
165 input_metadata_.erase(it);
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);
176 const auto datatype = it->second.datatype;
177 output_metadata_.erase(it);
181 if (!variable_input_size_) {
182 if (cuda_input_shm_enabled_) {
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()));
189 setup_shm_inputs(input_metadata_);
191 setup_standard_inputs(input_metadata_);
196 setup_shm_outputs(output_metadata_);
198 setup_standard_outputs(output_metadata_);
203 if (!variable_input_size_) {
205 for (
const auto& input : inputs_) {
206 raw_inputs_.push_back(input.second.input.get());
210 raw_outputs_.clear();
211 for (
const auto& output : outputs_) {
212 raw_outputs_.push_back(output.second.get());
223 if (variable_input_size_) {
225 for (
const auto& input : inputs_) {
226 raw_inputs_.push_back(input.second.input.get());
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()));
234 results_.reset(raw_results);
246 template <
typename T>
248 if (variable_input_size_) {
249 CreateInputTensor(name, rows);
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");
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
259 throw std::invalid_argument(
"Invalid input tensor size: " + ss.str());
261 return VectorType<T>{
reinterpret_cast<T*
>(input.data_raw), rows};
274 template <
typename T>
276 if (variable_input_size_) {
277 CreateInputTensor(name, rows, cols);
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");
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());
289 return MatrixType<T>{
reinterpret_cast<T*
>(input.data_raw), rows, cols};
304 template <
typename T,
typename... DimType>
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...);
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");
314 if (((dim0 * dim1 * dim2) * ... * dims) *
sizeof(T) != input.data_raw_size) {
315 std::stringstream ss;
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());
322 return TensorType<T,
sizeof...(dims) + 3>{
reinterpret_cast<T*
>(input.data_raw), dim0, dim1, dim2, dims...};
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));
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");
341 return {input.data_raw, input.data_raw_size};
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");
363 return {input.device_data_raw, input.data_raw_size};
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");
382 if (bytes != input.data_raw_size) {
383 throw std::invalid_argument(
"Input tensor '" + name +
"' byte size mismatch for host-to-device copy");
385 throw_on_cuda_error(cudaMemcpy(input.device_data_raw, host_data, bytes, cudaMemcpyHostToDevice),
"cudaMemcpy");
391 throw std::invalid_argument(
"triton_cpp was built without CUDA SHM support");
404 template <
typename T>
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());
425 template <
typename T>
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
432 throw std::invalid_argument(
"Invalid output tensor size: " + ss.str());
449 template <
typename T,
typename... DimType>
451 const std::string& name, int64_t dim0, int64_t dim1, int64_t dim2, DimType... dims)
const {
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());
460 return TensorType<
const T,
sizeof...(dims) + 3>{
reinterpret_cast<const T*
>(raw_data_buf), dim0, dim1, dim2, dims...};
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;
473 auto shm = outputs_.at(name);
475 std::string shm_name;
476 shm->SharedMemoryInfo(&shm_name, &raw_data_size, &offset);
477 raw_data_buf = output_shm_->getAddress() + offset;
479 results_->RawData(name, &raw_data_buf, &raw_data_size);
481 return {raw_data_buf, raw_data_size};
496 std::size_t
nInputs()
const {
return input_metadata_.size(); }
503 std::size_t
nOutputs()
const {
return output_metadata_.size(); }
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);
517 return it->second.shape;
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);
532 return it->second.shape;
536 void releaseSharedMemoryRegistrations() {
537 if (triton_client_ ==
nullptr) {
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()
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()
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()
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) {
577 for (
int j{0}; j < input.dims_size(); ++j) {
578 shape.push_back(input.dims(j));
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";
586 model_info_builder <<
"-------------------\n";
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));
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";
600 model_info_builder <<
"-------------------\n";
601 model_info_ = model_info_builder.str();
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.");
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());
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());
628 std::size_t computeSharedMemorySize(
const std::map<std::string, InputOutputMetaData>& metadata)
const {
630 std::size_t total_size = 0;
631 for (
const auto& [_, meta] : metadata) {
633 total_size +=
static_cast<std::size_t
>(meta.bytesize);
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;
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;
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;
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;
665 fail_on_error(triton_client_->RegisterCudaSharedMemory(INPUT_SHM_NAME, input_cuda_shm_->getIpcHandle(),
666 input_cuda_shm_->getDeviceId(), shm_size),
667 "RegisterCudaSharedMemory");
669 for (
const auto& [name, meta] : metadata) {
670 std::size_t current_shm_size = meta.bytesize;
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;
681 throw std::invalid_argument(
"triton_cpp was built without CUDA SHM support");
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));
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;
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;
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;
713 triton::client::InferOptions options_;
715 bool variable_input_size_;
716 bool cuda_input_shm_requested_ =
false;
717 bool cuda_input_shm_enabled_ =
false;
718 std::unique_ptr<triton::client::InferenceServerGrpcClient> triton_client_;
719 std::map<std::string, InputOutputMetaData> input_metadata_;
720 std::map<std::string, InputOutputMetaData> output_metadata_;
721 std::unique_ptr<SharedMemoryRegion> input_shm_;
722#if defined(TRITON_CPP_ENABLE_CUDA_SHM)
723 std::unique_ptr<CudaSharedMemoryRegion> input_cuda_shm_;
725 std::unique_ptr<SharedMemoryRegion> output_shm_;
726 std::string model_info_;
727 std::map<std::string, InputData> inputs_;
729 std::shared_ptr<triton::client::InferResult> results_;
730 std::vector<triton::client::InferInput*> raw_inputs_;
731 std::vector<const triton::client::InferRequestedOutput*> raw_outputs_;
733 const std::string RANDOM_INSTANCE_STRING =
randstring(10);
734 const std::string INPUT_SHM_NAME =
"input_data_" + RANDOM_INSTANCE_STRING;
735 const std::string INPUT_SHM_KEY =
"/triton_cpp_input_" + RANDOM_INSTANCE_STRING;
736 const std::string OUTPUT_SHM_NAME =
"output_data_" + RANDOM_INSTANCE_STRING;
737 const std::string OUTPUT_SHM_KEY =
"/triton_cpp_output_" + RANDOM_INSTANCE_STRING;