/* * SPDX-FileCopyrightText: Copyright (c) 1993-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved. * SPDX-License-Identifier: Apache-2.0 * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include "NvInfer.h" #include "NvOnnxParser.h" #include "ErrorRecorder.h" #include "common.h" #include "logger.h" #include "sampleDevice.h" #include "sampleEngines.h" #include "sampleEntrypoints.h" #include "sampleOptions.h" #include "sampleUtils.h" #if ENABLE_UNIFIED_BUILDER #include "NvInferConsistency.h" #include "safeErrorRecorder.h" #endif // cspell:ignore calib CUFILE nvonnxparser using namespace nvinfer1; namespace sample { namespace { class FileStreamWriter final : public nvinfer1::IStreamWriter { protected: std::ofstream mStream; int64_t mTotalWrittenSize; public: FileStreamWriter(std::string const& path) : mStream(path, std::ios::binary) , mTotalWrittenSize(0) { } int64_t write(void const* data, int64_t nbBytes) final { SMP_RETVAL_IF_FALSE( (mStream.is_open() && mStream.good()), "Cannot write to FileStreamWriter", -1, sample::gLogError); auto const* src = reinterpret_cast(data); mStream.write(src, nbBytes); mTotalWrittenSize += nbBytes; return nbBytes; } int64_t finalize() { mStream.close(); return mTotalWrittenSize; } }; } // namespace nvinfer1::ICudaEngine* LazilyDeserializedEngine::get() { SMP_RETVAL_IF_FALSE( !mIsSafe, "Safe mode is enabled, but trying to get standard engine!", nullptr, sample::gLogError); if (mEngine == nullptr) { SMP_RETVAL_IF_FALSE(getAsyncFileReader().isOpen() || !getBlob().empty(), "Engine is empty. Nothing to deserialize!", nullptr, sample::gLogError); using time_point = std::chrono::time_point; using duration = std::chrono::duration; time_point const deserializeStartTime{std::chrono::high_resolution_clock::now()}; if (mLeanDLLPath.empty()) { mRuntime.reset(createRuntime()); } else { mParentRuntime.reset(createRuntime()); ASSERT(mParentRuntime != nullptr); mRuntime.reset(mParentRuntime->loadRuntime(mLeanDLLPath.c_str())); } ASSERT(mRuntime != nullptr); if (mVersionCompatible) { // Application needs to opt into allowing deserialization of engines with embedded lean runtime. mRuntime->setEngineHostCodeAllowed(true); } if (!mTempdir.empty()) { mRuntime->setTemporaryDirectory(mTempdir.c_str()); } mRuntime->setTempfileControlFlags(mTempfileControls); SMP_RETVAL_IF_FALSE(mRuntime != nullptr, "runtime creation failed", nullptr, sample::gLogError); if (mDLACore != -1) { mRuntime->setDLACore(mDLACore); } mRuntime->setErrorRecorder(&gRecorder); for (auto const& pluginPath : mDynamicPlugins) { mRuntime->getPluginRegistry().loadLibrary(pluginPath.c_str()); } if (getAsyncFileReader().isOpen()) { mEngine.reset(mRuntime->deserializeCudaEngine(getAsyncFileReader())); } else { auto const& engineBlob = getBlob(); mEngine.reset(mRuntime->deserializeCudaEngine(engineBlob.data, engineBlob.size)); } SMP_RETVAL_IF_FALSE(mEngine != nullptr, "Engine deserialization failed", nullptr, sample::gLogError); time_point const deserializeEndTime{std::chrono::high_resolution_clock::now()}; sample::gLogInfo << "Engine deserialized in " << duration(deserializeEndTime - deserializeStartTime).count() << " sec." << std::endl; } return mEngine.get(); } nvinfer1::ICudaEngine* LazilyDeserializedEngine::release() { return mEngine.release(); } bool LazilyDeserializedEngine::checkDLASafe() { ASSERT(sample::hasSafeRuntime()); SMP_RETVAL_IF_FALSE(mDLACore == -1, "Safe DLA engine built with kDLA_STANDALONE should not be run via TRT!", false, sample::gLogError); return true; } //! //! \brief Generate a network definition for a given model //! //! \param[in] model Model options for this network //! \param[in,out] network Network storing the parsed results //! \param[in,out] err Error stream //! \param[out] vcPluginLibrariesUsed If not nullptr, will be populated with paths to VC plugin libraries required by //! the parsed network. //! \param[in] builderConfig Builder config required for DLA capability validation. //! //! \return Parser The parser used to initialize the network and that holds the weights for the network, or an invalid //! parser (the returned parser converts to false if tested) //! //! Constant input dimensions in the model must not be changed in the corresponding //! network definition, because its correctness may rely on the constants. //! //! \see Parser::operator bool() //! Parser modelToNetwork(ModelOptions const& model, BuildOptions const& build, nvinfer1::INetworkDefinition& network, std::ostream& err, std::vector* vcPluginLibrariesUsed, nvinfer1::IBuilderConfig const& builderConfig) { sample::gLogInfo << "Start parsing network model." << std::endl; auto const tBegin = std::chrono::high_resolution_clock::now(); Parser parser; switch (model.baseModel.format) { case ModelFormat::kONNX: { using namespace nvonnxparser; parser.onnxParser.reset(createONNXParser(network)); ASSERT(parser.onnxParser != nullptr); // kNATIVE_INSTANCENORM is ON by default in the parser and must be cleared to use the plugin implementation. if (build.pluginInstanceNorm) { parser.onnxParser->clearFlag(OnnxParserFlag::kNATIVE_INSTANCENORM); } if (build.enableUInt8AsymmetricQuantizationDLA) { parser.onnxParser->setFlag(OnnxParserFlag::kENABLE_UINT8_AND_ASYMMETRIC_QUANTIZATION_DLA); } if (build.reportCapabilityDLA) { parser.onnxParser->setFlag(OnnxParserFlag::kREPORT_CAPABILITY_DLA); parser.onnxParser->setBuilderConfig(&builderConfig); } if (build.adjustForDLA) { parser.onnxParser->setFlag(OnnxParserFlag::kADJUST_FOR_DLA); } if (build.enablePluginOverride) { parser.onnxParser->setFlag(OnnxParserFlag::kENABLE_PLUGIN_OVERRIDE); } if (!parser.onnxParser->parseFromFile( model.baseModel.model.c_str(), static_cast(sample::gLogger.getReportableSeverity()))) { err << "Failed to parse onnx file" << std::endl; parser.onnxParser.reset(); } if (vcPluginLibrariesUsed && parser.onnxParser.get()) { int64_t nbPluginLibs; char const* const* pluginLibArray = parser.onnxParser->getUsedVCPluginLibraries(nbPluginLibs); if (nbPluginLibs >= 0) { vcPluginLibrariesUsed->reserve(nbPluginLibs); for (int64_t i = 0; i < nbPluginLibs; ++i) { sample::gLogInfo << "Using VC plugin library " << pluginLibArray[i] << std::endl; vcPluginLibrariesUsed->emplace_back(std::string{pluginLibArray[i]}); } } else { sample::gLogWarning << "Failure to query VC plugin libraries required by parsed ONNX network" << std::endl; } } break; } case ModelFormat::kANY: break; } auto const tEnd = std::chrono::high_resolution_clock::now(); float const parseTime = std::chrono::duration(tEnd - tBegin).count(); sample::gLogInfo << "Finished parsing network model. Parse time: " << parseTime << std::endl; return parser; } namespace { void setLayerDeviceTypes( INetworkDefinition const& network, IBuilderConfig& config, LayerDeviceTypes const& layerDeviceTypes) { for (int32_t layerIdx = 0; layerIdx < network.getNbLayers(); ++layerIdx) { auto* layer = network.getLayer(layerIdx); auto const layerName = layer->getName(); auto match = findPlausible(layerDeviceTypes, layerName); if (match != layerDeviceTypes.end()) { DeviceType const deviceType = match->second; sample::gLogInfo << "Set layer " << layerName << " to device type " << deviceType << std::endl; config.setDeviceType(layer, deviceType); } } } void setDecomposables(INetworkDefinition& network, DecomposableAttentions const& decomposableAttentions) { for (int32_t layerIdx = 0; layerIdx < network.getNbLayers(); ++layerIdx) { auto* layer = network.getLayer(layerIdx); if (layer->getType() == LayerType::kATTENTION_INPUT) { auto* attention = static_cast(layer)->getAttention(); auto const attentionName = attention->getName(); auto match = findPlausible(decomposableAttentions, attentionName); if (match != decomposableAttentions.end()) { attention->setDecomposable(match->second); sample::gLogInfo << "Set attention " << attentionName << " to decomposable = " << ((match->second) ? "true" : "false") << std::endl; } } } } void markDebugTensors(INetworkDefinition& network, StringSet const& debugTensors) { for (int64_t inputIndex = 0; inputIndex < network.getNbInputs(); ++inputIndex) { auto* t = network.getInput(inputIndex); auto const tensorName = t->getName(); if (debugTensors.count(tensorName) > 0) { network.markDebug(*t); } } for (int64_t layerIndex = 0; layerIndex < network.getNbLayers(); ++layerIndex) { auto* layer = network.getLayer(layerIndex); for (int64_t outputIndex = 0; outputIndex < layer->getNbOutputs(); ++outputIndex) { auto* t = layer->getOutput(outputIndex); auto const tensorName = t->getName(); if (debugTensors.count(tensorName) > 0) { network.markDebug(*t); } } } } void setMemoryPoolLimits(IBuilderConfig& config, BuildOptions const& build) { auto const roundToBytes = [](double const size, bool fromMB = true) { return static_cast(size * (fromMB ? 1.0_MiB : 1.0_KiB)); }; if (build.workspace >= 0) { config.setMemoryPoolLimit(MemoryPoolType::kWORKSPACE, roundToBytes(build.workspace)); } if (build.dlaSRAM >= 0) { size_t const sizeInBytes = roundToBytes(build.dlaSRAM); size_t sizeInPowerOf2{1}; // Using 2^30 bytes as a loose upper bound to prevent the possibility of overflows and infinite loops. while (sizeInPowerOf2 < 31 && (static_cast(1) << sizeInPowerOf2) <= sizeInBytes) { ++sizeInPowerOf2; } --sizeInPowerOf2; if (sizeInPowerOf2 == 30) { sample::gLogWarning << "User-specified DLA managed SRAM size is too large and has been clipped to 2^30 bytes. " << "Please make sure that this is the intended managed SRAM size." << std::endl; } config.setMemoryPoolLimit(MemoryPoolType::kDLA_MANAGED_SRAM, static_cast(1) << sizeInPowerOf2); } if (build.dlaLocalDRAM >= 0) { config.setMemoryPoolLimit(MemoryPoolType::kDLA_LOCAL_DRAM, roundToBytes(build.dlaLocalDRAM)); } if (build.dlaGlobalDRAM >= 0) { config.setMemoryPoolLimit(MemoryPoolType::kDLA_GLOBAL_DRAM, roundToBytes(build.dlaGlobalDRAM)); } if (build.tacticSharedMem >= 0) { config.setMemoryPoolLimit(MemoryPoolType::kTACTIC_SHARED_MEMORY, roundToBytes(build.tacticSharedMem, false)); } } void setPreviewFeatures(IBuilderConfig& config, BuildOptions const& build) { auto const setFlag = [&](PreviewFeature feat) { int32_t featVal = static_cast(feat); if (build.previewFeatures.find(featVal) != build.previewFeatures.end()) { config.setPreviewFeature(feat, build.previewFeatures.at(featVal)); } }; setFlag(PreviewFeature::kALIASED_PLUGIN_IO_10_03); setFlag(PreviewFeature::kRUNTIME_ACTIVATION_RESIZE_10_10); } [[nodiscard]] bool setupTilingSettings(BuildOptions const& build, IBuilderConfig& config, std::ostream& err) { if (!config.setTilingOptimizationLevel(static_cast(build.tilingOptimizationLevel))) { err << "Can not set tilingOptimizationLevel(" << build.tilingOptimizationLevel << ")" << std::endl; return false; } if (build.l2LimitForTiling != -1) { if (!config.setL2LimitForTiling(build.l2LimitForTiling)) { err << "Can not set l2LimitForTiling(" << build.l2LimitForTiling << ")" << std::endl; return false; } } return true; } // NOLINTNEXTLINE(readability-function-cognitive-complexity, readability-function-size) bool setupNetworkAndConfig(BuildOptions const& build, SystemOptions const& sys, IBuilder& builder, INetworkDefinition& network, IBuilderConfig& config, std::ostream& err, std::vector>& sparseWeights) { std::vector profiles{}; profiles.resize(build.optProfiles.size()); for (auto& profile : profiles) { profile = builder.createOptimizationProfile(); } bool hasDynamicShapes{false}; bool broadcastInputFormats = broadcastIOFormats(build.inputFormats, network.getNbInputs()); // Check if the provided input tensor names match the input tensors of the engine. // Throw an error if the provided input tensor names cannot be found because it implies a potential typo. for (auto const& shapes : build.optProfiles) { for (auto const& shape : shapes) { bool tensorNameFound{false}; for (int32_t i = 0; i < network.getNbInputs(); ++i) { if (matchStringWithOneWildcard(shape.first, network.getInput(i)->getName())) { tensorNameFound = true; break; } } if (!tensorNameFound) { sample::gLogError << "Cannot find input tensor with name \"" << shape.first << "\" in the network " << "inputs! Please make sure the input tensor names are correct." << std::endl; return false; } } } for (uint32_t i = 0, n = network.getNbInputs(); i < n; i++) { // Set formats and data types of inputs auto* input = network.getInput(i); if (!build.inputFormats.empty()) { int32_t inputFormatIndex = broadcastInputFormats ? 0 : i; input->setAllowedFormats(build.inputFormats[inputFormatIndex].formats); } auto const dims = input->getDimensions(); auto const isScalar = dims.nbDims == 0; auto const isDynamicInput = std::any_of(dims.d, dims.d + dims.nbDims, [](int32_t dim) { return dim == -1; }) || input->isShapeTensor(); if (isDynamicInput) { hasDynamicShapes = true; for (size_t i = 0; i < build.optProfiles.size(); i++) { auto const& optShapes = build.optProfiles[i]; auto profile = profiles[i]; auto const tensorName = input->getName(); auto shape = findPlausible(optShapes, tensorName); ShapeRange shapes{}; // If no shape is provided, set dynamic dimensions to 1. if (shape == optShapes.end()) { constexpr int32_t kDEFAULT_DIMENSION{1}; std::vector staticDims; if (input->isShapeTensor()) { if (isScalar) { staticDims.push_back(1); } else { staticDims.resize(dims.d[0]); std::fill(staticDims.begin(), staticDims.end(), kDEFAULT_DIMENSION); } } else { staticDims.resize(dims.nbDims); std::transform(dims.d, dims.d + dims.nbDims, staticDims.begin(), [&](int dimension) { return dimension > 0 ? dimension : kDEFAULT_DIMENSION; }); } sample::gLogWarning << "Dynamic dimensions required for input: " << tensorName << ", but no shapes were provided. Automatically overriding shape to: " << staticDims << std::endl; std::fill(shapes.begin(), shapes.end(), staticDims); } else { shapes = shape->second; } std::vector profileDims{}; if (input->isShapeTensor()) { profileDims = shapes[static_cast(OptProfileSelector::kMIN)]; SMP_RETVAL_IF_FALSE(profile->setShapeValuesV2(tensorName, OptProfileSelector::kMIN, profileDims.data(), static_cast(profileDims.size())), "Error in set shape values MIN", false, err); profileDims = shapes[static_cast(OptProfileSelector::kOPT)]; SMP_RETVAL_IF_FALSE(profile->setShapeValuesV2(tensorName, OptProfileSelector::kOPT, profileDims.data(), static_cast(profileDims.size())), "Error in set shape values OPT", false, err); profileDims = shapes[static_cast(OptProfileSelector::kMAX)]; SMP_RETVAL_IF_FALSE(profile->setShapeValuesV2(tensorName, OptProfileSelector::kMAX, profileDims.data(), static_cast(profileDims.size())), "Error in set shape values MAX", false, err); sample::gLogInfo << "Set input shape tensor " << tensorName << " for optimization profile " << i << " to:" << " MIN=" << shapes[static_cast(OptProfileSelector::kMIN)] << " OPT=" << shapes[static_cast(OptProfileSelector::kOPT)] << " MAX=" << shapes[static_cast(OptProfileSelector::kMAX)] << std::endl; } else { profileDims = shapes[static_cast(OptProfileSelector::kMIN)]; SMP_RETVAL_IF_FALSE( profile->setDimensions(tensorName, OptProfileSelector::kMIN, toDims(profileDims)), "Error in set dimensions to profile MIN", false, err); profileDims = shapes[static_cast(OptProfileSelector::kOPT)]; SMP_RETVAL_IF_FALSE( profile->setDimensions(tensorName, OptProfileSelector::kOPT, toDims(profileDims)), "Error in set dimensions to profile OPT", false, err); profileDims = shapes[static_cast(OptProfileSelector::kMAX)]; SMP_RETVAL_IF_FALSE( profile->setDimensions(tensorName, OptProfileSelector::kMAX, toDims(profileDims)), "Error in set dimensions to profile MAX", false, err); sample::gLogInfo << "Set shape of input tensor " << tensorName << " for optimization profile " << i << " to:" << " MIN=" << shapes[static_cast(OptProfileSelector::kMIN)] << " OPT=" << shapes[static_cast(OptProfileSelector::kOPT)] << " MAX=" << shapes[static_cast(OptProfileSelector::kMAX)] << std::endl; } } } } for (uint32_t i = 0, n = network.getNbOutputs(); i < n; i++) { auto* output = network.getOutput(i); auto const dims = output->getDimensions(); // A shape tensor output with known static dimensions may have dynamic shape values inside it. auto const isDynamicOutput = std::any_of(dims.d, dims.d + dims.nbDims, [](int32_t dim) { return dim == -1; }) || output->isShapeTensor(); if (isDynamicOutput) { hasDynamicShapes = true; } } if (!hasDynamicShapes && !build.optProfiles[0].empty()) { sample::gLogError << "Static model does not take explicit shapes since the shape of inference tensors will be " "determined by the model itself" << std::endl; return false; } if (hasDynamicShapes) { for (auto profile : profiles) { SMP_RETVAL_IF_FALSE(profile->isValid(), "Required optimization profile is invalid", false, err); SMP_RETVAL_IF_FALSE( config.addOptimizationProfile(profile) != -1, "Error in add optimization profile", false, err); } } bool broadcastOutputFormats = broadcastIOFormats(build.outputFormats, network.getNbOutputs(), false); for (uint32_t i = 0, n = network.getNbOutputs(); i < n; i++) { // Set formats and data types of outputs auto* output = network.getOutput(i); if (!build.outputFormats.empty()) { int32_t outputFormatIndex = broadcastOutputFormats ? 0 : i; output->setAllowedFormats(build.outputFormats[outputFormatIndex].formats); } } setMemoryPoolLimits(config, build); setPreviewFeatures(config, build); if (build.builderOptimizationLevel != defaultBuilderOptimizationLevel) { config.setBuilderOptimizationLevel(build.builderOptimizationLevel); } if (build.maxTactics != defaultMaxTactics) { config.setMaxNbTactics(build.maxTactics); } if (build.timingCacheMode == TimingCacheMode::kDISABLE) { config.setFlag(BuilderFlag::kDISABLE_TIMING_CACHE); } if (build.disableCompilationCache) { config.setFlag(BuilderFlag::kDISABLE_COMPILATION_CACHE); } if (build.errorOnTimingCacheMiss) { config.setFlag(BuilderFlag::kERROR_ON_TIMING_CACHE_MISS); } if (!build.tf32) { config.clearFlag(BuilderFlag::kTF32); } if (build.refittable) { config.setFlag(BuilderFlag::kREFIT); } if (build.stripWeights) { // The kREFIT_IDENTICAL is enabled by default when kSTRIP_PLAN is on. config.setFlag(BuilderFlag::kSTRIP_PLAN); } if (build.versionCompatible) { config.setFlag(BuilderFlag::kVERSION_COMPATIBLE); } std::vector pluginPaths; for (auto const& pluginPath : sys.setPluginsToSerialize) { sample::gLogVerbose << "Setting plugin to serialize: " << pluginPath << std::endl; pluginPaths.push_back(pluginPath.c_str()); } if (!pluginPaths.empty()) { config.setPluginsToSerialize(pluginPaths.data(), pluginPaths.size()); } if (build.excludeLeanRuntime) { config.setFlag(BuilderFlag::kEXCLUDE_LEAN_RUNTIME); } if (build.sparsity != SparsityFlag::kDISABLE) { config.setFlag(BuilderFlag::kSPARSE_WEIGHTS); if (build.sparsity == SparsityFlag::kFORCE) { sparsify(network, sparseWeights); } } if (build.enableMonitorMemory) { config.setFlag(BuilderFlag::kMONITOR_MEMORY); } if (build.distributiveIndependence) { config.setFlag(BuilderFlag::kDISTRIBUTIVE_INDEPENDENCE); } config.setProfilingVerbosity(build.profilingVerbosity); config.setAvgTimingIterations(build.avgTiming); if (build.directIO) { config.setFlag(BuilderFlag::kDIRECT_IO); } if (!build.layerDeviceTypes.empty()) { setLayerDeviceTypes(network, config, build.layerDeviceTypes); } if (!build.decomposableAttentions.empty()) { setDecomposables(network, build.decomposableAttentions); } if (!build.debugTensors.empty()) { markDebugTensors(network, build.debugTensors); } if (build.markUnfusedTensorsAsDebugTensors) { network.markUnfusedTensorsAsDebugTensors(); } if (build.safe && sys.DLACore == -1) { config.setEngineCapability(EngineCapability::kSAFETY); } if (sys.DLACore != -1) { if (sys.DLACore < builder.getNbDLACores()) { config.setDefaultDeviceType(DeviceType::kDLA); config.setDLACore(sys.DLACore); if (build.buildDLAStandalone) { config.setEngineCapability(EngineCapability::kDLA_STANDALONE); } if (build.allowGPUFallback) { config.setFlag(BuilderFlag::kGPU_FALLBACK); } else { // Reformatting runs on GPU, so avoid I/O reformatting. config.setFlag(BuilderFlag::kDIRECT_IO); } } else { err << "Cannot create DLA engine, " << sys.DLACore << " not available" << std::endl; return false; } } if (build.enabledTactics || build.disabledTactics) { TacticSources tacticSources = config.getTacticSources(); tacticSources |= build.enabledTactics; tacticSources &= ~build.disabledTactics; config.setTacticSources(tacticSources); } config.setHardwareCompatibilityLevel(build.hardwareCompatibilityLevel); config.setRuntimePlatform(build.runtimePlatform); if (build.maxAuxStreams != defaultMaxAuxStreams) { config.setMaxAuxStreams(build.maxAuxStreams); } if (build.allowWeightStreaming) { config.setFlag(BuilderFlag::kWEIGHT_STREAMING); } if (!setupTilingSettings(build, config, err)) { return false; } if (!build.remoteAutoTuningConfig.empty()) { SMP_RETVAL_IF_FALSE(config.setRemoteAutoTuningConfig(build.remoteAutoTuningConfig.c_str()), "Failed to set remote auto tuning config", false, err); } return true; } } // namespace //! \brief Build a serialized engine in memory (as opposed to streaming to a file). bool buildSerializedEngine(BuildOptions const& build, SystemOptions const& sys, IBuilder& builder, INetworkDefinition& network, IBuilderConfig& config, BuildEnvironment& env, std::ostream& err) { IHostMemory* serializedEngine{nullptr}; if (build.safe && build.save && build.dumpKernelText) { IHostMemory* kernelText{nullptr}; serializedEngine = builder.buildSerializedNetwork(network, config, kernelText); if (kernelText != nullptr && kernelText->size() > 0) { std::unique_ptr kernelTextPtr(kernelText); env.kernelText.setBlob(kernelTextPtr); sample::gLogInfo << "Created kernel CPP with size: " << (kernelText->size() / 1.0_MiB) << " MiB" << std::endl; } else { sample::gLogError << "Failed to create kernel CPP." << std::endl; } } else { serializedEngine = builder.buildSerializedNetwork(network, config); } SMP_RETVAL_IF_FALSE(serializedEngine != nullptr, "Engine could not be created from network", false, err); sample::gLogInfo << "Created engine with size: " << (serializedEngine->size() / 1.0_MiB) << " MiB" << std::endl; if (build.safe && build.consistency) { std::vector pluginBuildLibPaths; #if ENABLE_UNIFIED_BUILDER pluginBuildLibPaths.reserve(sys.safetyPlugins.size()); std::transform(sys.safetyPlugins.begin(), sys.safetyPlugins.end(), std::back_inserter(pluginBuildLibPaths), [](auto const& sp) { return sp.libraryName; }); #endif if (!checkSafeEngine(serializedEngine->data(), serializedEngine->size(), pluginBuildLibPaths)) { return false; } } std::unique_ptr serializedEnginePtr(serializedEngine); env.engine.setBlob(serializedEnginePtr); return true; } //! //! \brief Create a serialized engine for a network definition //! //! \return Whether the engine creation succeeds or fails. //! bool networkToSerializedEngine( BuildOptions const& build, SystemOptions const& sys, BuildEnvironment& env, std::ostream& err, PostConfigCallback const& postConfigHook) { IBuilder& builder = *env.builder; IBuilderConfig& config = *env.builderConfig; INetworkDefinition& network = *env.network; std::vector> sparseWeights; SMP_RETVAL_IF_FALSE(setupNetworkAndConfig(build, sys, builder, network, config, err, sparseWeights), "Network And Config setup failed", false, err); if (postConfigHook) { postConfigHook(builder, config, build, sys); } std::unique_ptr const timingCache = (!build.cpuOnly && build.timingCacheMode == TimingCacheMode::kGLOBAL) ? // Try to load cache from file. Create a fresh cache if the file doesn't exist samplesCommon::buildTimingCacheFromFile(gLogger.getTRTLogger(), config, build.timingCacheFile) : nullptr; // CUDA stream used for profiling by the builder. auto profileStream = build.cpuOnly ? std::unique_ptr{nullptr, samplesCommon::StreamDeleter} : samplesCommon::makeCudaStream(); if (!build.cpuOnly) { SMP_RETVAL_IF_FALSE(profileStream != nullptr, "Cuda stream creation failed", false, err); config.setProfileStream(*profileStream); } auto const tBegin = std::chrono::high_resolution_clock::now(); if (!(build.safe || build.buildDLAStandalone) && build.save) { auto const engineFile = build.engine; FileStreamWriter writer(engineFile); builder.buildSerializedNetworkToStream(network, config, writer); auto const engineSize = writer.finalize(); std::vector streamEngine(engineSize, 0); std::ifstream reader(engineFile, std::ios::binary); SMP_RETVAL_IF_FALSE((reader.is_open() && reader.good()), "Failed to open engine file for reading", false, err); reader.read(reinterpret_cast(streamEngine.data()), engineSize); SMP_RETVAL_IF_FALSE((!reader.fail()), "Error when reading engine file", false, err); reader.close(); sample::gLogInfo << "Created engine with size: " << (engineSize / 1.0_MiB) << " MiB" << std::endl; env.engine.setBlob(std::move(streamEngine)); } else if (!buildSerializedEngine(build, sys, builder, network, config, env, err)) { return false; } auto const tEnd = std::chrono::high_resolution_clock::now(); float const buildTime = std::chrono::duration(tEnd - tBegin).count(); sample::gLogInfo << "Engine built in " << buildTime << " sec." << std::endl; if (!build.cpuOnly && build.timingCacheMode == TimingCacheMode::kGLOBAL) { auto timingCache = config.getTimingCache(); samplesCommon::updateTimingCacheFile(gLogger.getTRTLogger(), build.timingCacheFile, timingCache, builder); } return true; } //! //! \brief Parse a given model, create a network and an engine. //! bool modelToBuildEnv( ModelOptions const& model, BuildOptions const& build, SystemOptions& sys, BuildEnvironment& env, std::ostream& err, PostConfigCallback const& postConfigHook) { env.builder.reset(createBuilder()); SMP_RETVAL_IF_FALSE(env.builder != nullptr, "Builder creation failed", false, err); env.builderConfig.reset(env.builder->createBuilderConfig()); SMP_RETVAL_IF_FALSE(env.builderConfig != nullptr, "Builder config creation failed", false, err); // Apply --setBuildRoute to pin the engine build to a specific knob configuration. // This is the public reproducibility surface for tuning iterations. // Gated by ENABLE_FEATURE_GLOBAL_PERF_TUNER because the underlying // IBuilderConfig::setBuildRoute API is only declared when the feature is on. if (!build.buildRoute.empty()) { sample::gLogInfo << "Using tuning build route: " << build.buildRoute << std::endl; SMP_RETVAL_IF_FALSE(env.builderConfig->setBuildRoute(build.buildRoute.c_str()), "IBuilderConfig::setBuildRoute failed for: " + build.buildRoute, false, err); } env.builder->setErrorRecorder(&gRecorder); auto networkFlags = 1U << static_cast(nvinfer1::NetworkDefinitionCreationFlag::kSTRONGLY_TYPED); for (auto const& pluginPath : sys.dynamicPlugins) { env.builder->getPluginRegistry().loadLibrary(pluginPath.c_str()); } env.network.reset(env.builder->createNetworkV2(networkFlags)); std::vector vcPluginLibrariesUsed; SMP_RETVAL_IF_FALSE(env.network != nullptr, "Network creation failed", false, err); env.parser = modelToNetwork(model, build, *env.network, err, build.versionCompatible ? &vcPluginLibrariesUsed : nullptr, *env.builderConfig); SMP_RETVAL_IF_FALSE(env.parser.operator bool(), "Parsing model failed", false, err); if (build.versionCompatible && !sys.ignoreParsedPluginLibs && !vcPluginLibrariesUsed.empty()) { sample::gLogInfo << "The following plugin libraries were identified by the parser as required for a " "version-compatible engine:" << std::endl; for (auto const& lib : vcPluginLibrariesUsed) { sample::gLogInfo << " " << lib << std::endl; } if (!build.excludeLeanRuntime) { sample::gLogInfo << "These libraries will be added to --setPluginsToSerialize since --excludeLeanRuntime " "was not specified." << std::endl; std::copy(vcPluginLibrariesUsed.begin(), vcPluginLibrariesUsed.end(), std::back_inserter(sys.setPluginsToSerialize)); } sample::gLogInfo << "These libraries will be added to --dynamicPlugins for use at inference time." << std::endl; std::copy(vcPluginLibrariesUsed.begin(), vcPluginLibrariesUsed.end(), std::back_inserter(sys.dynamicPlugins)); // Implicitly-added plugins from ONNX parser should be loaded into plugin registry as well. for (auto const& pluginPath : vcPluginLibrariesUsed) { env.builder->getPluginRegistry().loadLibrary(pluginPath.c_str()); } sample::gLogInfo << "Use --ignoreParsedPluginLibs to disable this behavior." << std::endl; } SMP_RETVAL_IF_FALSE( networkToSerializedEngine(build, sys, env, err, postConfigHook), "Building engine failed", false, err); return true; } namespace { std::pair, std::vector> getLayerWeightsRolePair(IRefitter& refitter) { // Get number of refittable items. auto const nbAll = refitter.getAll(0, nullptr, nullptr); std::vector layerNames(nbAll); // Allocate buffers for the items and get them. std::vector weightsRoles(nbAll); refitter.getAll(nbAll, layerNames.data(), weightsRoles.data()); std::vector layerNameStrs(nbAll); std::transform(layerNames.begin(), layerNames.end(), layerNameStrs.begin(), [](char const* name) { if (name == nullptr) { return std::string{}; } return std::string{name}; }); return {layerNameStrs, weightsRoles}; } std::pair, std::vector> getMissingLayerWeightsRolePair(IRefitter& refitter) { // Get number of refittable items. auto const nbMissing = refitter.getMissing(0, nullptr, nullptr); std::vector layerNames(nbMissing); // Allocate buffers for the items and get them. std::vector weightsRoles(nbMissing); refitter.getMissing(nbMissing, layerNames.data(), weightsRoles.data()); // Convert null names in `layerNames` to empty strings: std::vector layerNameStrs(nbMissing); std::transform(layerNames.begin(), layerNames.end(), layerNameStrs.begin(), [](char const* name) { if (name == nullptr) { return std::string{}; } return std::string{name}; }); return {std::move(layerNameStrs), std::move(weightsRoles)}; } } // namespace bool loadStreamingEngineToBuildEnv(std::string const& filepath, BuildEnvironment& env, std::ostream& err) { auto& reader = env.engine.getAsyncFileReader(); SMP_RETVAL_IF_FALSE(reader.open(filepath), "", false, err << "Error opening engine file: " << filepath); return true; } bool loadAsyncStreamingEngineToBuildEnv(std::string const& filepath, BuildEnvironment& env, std::ostream& err) { auto& asyncReader = env.engine.getAsyncFileReader(); SMP_RETVAL_IF_FALSE(asyncReader.open(filepath), "", false, err << "Error opening engine file: " << filepath); return true; } bool loadEngineToBuildEnv(std::string const& filepath, BuildEnvironment& env, std::ostream& err, SystemOptions const& sys, bool const enableConsistency) { auto const tBegin = std::chrono::high_resolution_clock::now(); std::ifstream engineFile(filepath, std::ios::binary); SMP_RETVAL_IF_FALSE(engineFile.good(), "", false, err << "Error opening engine file: " << filepath); engineFile.seekg(0, std::ifstream::end); int64_t fsize = engineFile.tellg(); engineFile.seekg(0, std::ifstream::beg); std::vector engineBlob(fsize); engineFile.read(reinterpret_cast(engineBlob.data()), fsize); SMP_RETVAL_IF_FALSE(engineFile.good(), "", false, err << "Error loading engine file: " << filepath); auto const tEnd = std::chrono::high_resolution_clock::now(); float const loadTime = std::chrono::duration(tEnd - tBegin).count(); sample::gLogInfo << "Engine loaded in " << loadTime << " sec." << std::endl; sample::gLogInfo << "Loaded engine with size: " << (fsize / 1.0_MiB) << " MiB" << std::endl; if (enableConsistency) { std::vector pluginBuildLibPaths; #if ENABLE_UNIFIED_BUILDER pluginBuildLibPaths.reserve(sys.safetyPlugins.size()); std::transform(sys.safetyPlugins.begin(), sys.safetyPlugins.end(), std::back_inserter(pluginBuildLibPaths), [](auto const& sp) { return sp.libraryName; }); #endif if (!checkSafeEngine(engineBlob.data(), fsize, pluginBuildLibPaths)) { sample::gLogError << "Consistency validation is not enabled." << std::endl; return false; } } env.engine.setBlob(std::move(engineBlob)); return true; } bool printPlanVersion(BuildEnvironment& env, std::ostream& err) { constexpr int64_t kPLAN_SIZE{28}; std::vector data(kPLAN_SIZE); auto blob = data.data(); auto& asyncReader = env.engine.getAsyncFileReader(); if (asyncReader.isOpen()) { SMP_RETVAL_IF_FALSE(asyncReader.read(data.data(), kPLAN_SIZE, cudaStream_t{}) == kPLAN_SIZE, "Failed to read plan file", false, err); } else { SMP_RETVAL_IF_FALSE(env.engine.getBlob().data != nullptr, "Plan file is empty", false, err); SMP_RETVAL_IF_FALSE(env.engine.getBlob().size >= 28, "Plan file is incorrect", false, err); blob = static_cast(env.engine.getBlob().data); } auto blob32 = reinterpret_cast(blob); //! Correct TensorRT plan file starts with this tag constexpr uint32_t kPLAN_FILE_TAG{0x74727466U}; SMP_RETVAL_IF_FALSE(blob32[0] == kPLAN_FILE_TAG, "Failed to verify a plan tag.", false, err); switch (blob32[1]) { case 0U: { // Blob index to store the plan version may depend on the serialization version. sample::gLogInfo << "Plan was created with TensorRT version " << static_cast(blob[24]) << "." << static_cast(blob[25]) << "." << static_cast(blob[26]) << "." << static_cast(blob[27]) << std::endl; return true; } } sample::gLogError << "Serialization version is not supported." << std::endl; return false; } void dumpRefittable(nvinfer1::ICudaEngine& engine) { std::unique_ptr refitter{createRefitter(engine)}; if (refitter == nullptr) { sample::gLogError << "Failed to create a refitter." << std::endl; return; } auto const& layerWeightsRolePair = getLayerWeightsRolePair(*refitter); auto const& layerNames = layerWeightsRolePair.first; auto const& weightsRoles = layerWeightsRolePair.second; auto const nbAll = layerWeightsRolePair.first.size(); for (size_t i = 0; i < nbAll; ++i) { sample::gLogInfo << layerNames[i] << " " << weightsRoles[i] << std::endl; } } ICudaEngine* loadEngine(std::string const& engine, int32_t DLACore, std::ostream& err) { BuildEnvironment env(/* isSafe */ false, /* versionCompatible */ false, DLACore, "", getTempfileControlDefaults()); SystemOptions sys; if (!loadEngineToBuildEnv(engine, env, err, sys, false)) { return nullptr; } // Trigger deserialization before releasing ownership if (env.engine.get() == nullptr) { return nullptr; } return env.engine.release(); } bool saveEngine(ICudaEngine const& engine, std::string const& fileName, std::ostream& err) { std::ofstream engineFile(fileName, std::ios::binary); if (!engineFile) { err << "Cannot open engine file: " << fileName << std::endl; return false; } std::unique_ptr serializedEngine{engine.serialize()}; if (serializedEngine == nullptr) { err << "Engine serialization failed" << std::endl; return false; } engineFile.write(static_cast(serializedEngine->data()), serializedEngine->size()); return !engineFile.fail(); } // NOLINTNEXTLINE(readability-function-cognitive-complexity) bool getEngineBuildEnv( ModelOptions const& model, BuildOptions const& build, SystemOptions& sys, BuildEnvironment& env, std::ostream& err, PostConfigCallback const& postConfigHook) { bool createEngineSuccess{false}; if (build.load) { if (build.safe) { createEngineSuccess = loadEngineToBuildEnv(build.engine, env, err, sys, build.safe && build.consistency); } else { if (build.asyncFileReader) { createEngineSuccess = loadAsyncStreamingEngineToBuildEnv(build.engine, env, err); } else { createEngineSuccess = loadStreamingEngineToBuildEnv(build.engine, env, err); } } } else { createEngineSuccess = modelToBuildEnv(model, build, sys, env, err, postConfigHook); } SMP_RETVAL_IF_FALSE(createEngineSuccess, "Failed to create engine from model or file.", false, err); if (build.getPlanVersionOnly && build.load) { SMP_RETVAL_IF_FALSE(printPlanVersion(env, err), "Failed to get plan file version.", false, err); return true; } if (build.save) { std::ofstream engineFile(build.engine, std::ios::binary); auto& engineBlob = env.engine.getBlob(); engineFile.write(static_cast(engineBlob.data), engineBlob.size); SMP_RETVAL_IF_FALSE(!engineFile.fail(), "Saving engine to file failed.", false, err); engineFile.flush(); engineFile.close(); if (!build.safe) { env.engine.releaseBlob(); if (build.asyncFileReader) { SMP_RETVAL_IF_FALSE(loadAsyncStreamingEngineToBuildEnv(build.engine, env, err), "Reading engine file via async stream reader failed.", false, err); } else { SMP_RETVAL_IF_FALSE(loadStreamingEngineToBuildEnv(build.engine, env, err), "Reading engine file via stream reader failed.", false, err); } } if (build.safe && build.dumpKernelText) { auto const engineTextFileName = build.engine + ".txt"; auto const kernelTextBlob = env.kernelText.getBlobOrEmpty(); if (kernelTextBlob.data != nullptr && kernelTextBlob.size > 0) { std::ofstream engineTextFile(engineTextFileName); engineTextFile.write(static_cast(kernelTextBlob.data), kernelTextBlob.size); SMP_RETVAL_IF_FALSE(!engineTextFile.fail(), "Saving engine kernel text to file failed.", false, err); engineTextFile.close(); } else { sample::gLogWarning << "Kernel text was not produced; skipping dump to " << engineTextFileName << std::endl; } } } return true; } // There is not a getWeightsName API, so we need to use WeightsRole. std::vector> getAllRefitWeightsForLayer(ILayer const& l) { switch (l.getType()) { case LayerType::kCONSTANT: { auto const& layer = static_cast(l); auto const weights = layer.getWeights(); switch (weights.type) { case DataType::kFLOAT: case DataType::kHALF: case DataType::kBF16: case DataType::kINT8: case DataType::kINT32: case DataType::kINT64: return {std::make_pair(WeightsRole::kCONSTANT, weights)}; case DataType::kBOOL: case DataType::kUINT8: case DataType::kFP8: case DataType::kINT4: case DataType::kFP4: case DataType::kE8M0: // Refit not supported for these types. break; } break; } case LayerType::kCONVOLUTION: { auto const& layer = static_cast(l); return {std::make_pair(WeightsRole::kKERNEL, layer.getKernelWeights()), std::make_pair(WeightsRole::kBIAS, layer.getBiasWeights())}; } case LayerType::kDECONVOLUTION: { auto const& layer = static_cast(l); return {std::make_pair(WeightsRole::kKERNEL, layer.getKernelWeights()), std::make_pair(WeightsRole::kBIAS, layer.getBiasWeights())}; } case LayerType::kSCALE: { auto const& layer = static_cast(l); return {std::make_pair(WeightsRole::kSCALE, layer.getScale()), std::make_pair(WeightsRole::kSHIFT, layer.getShift())}; } case LayerType::kACTIVATION: case LayerType::kATTENTION_INPUT: case LayerType::kATTENTION_OUTPUT: case LayerType::kASSERTION: case LayerType::kCAST: case LayerType::kCONCATENATION: case LayerType::kCONDITION: case LayerType::kCONDITIONAL_INPUT: case LayerType::kCONDITIONAL_OUTPUT: case LayerType::kCUMULATIVE: case LayerType::kDEQUANTIZE: case LayerType::kDIST_COLLECTIVE: case LayerType::kDYNAMIC_QUANTIZE: case LayerType::kEINSUM: case LayerType::kELEMENTWISE: case LayerType::kFILL: case LayerType::kGATHER: case LayerType::kGRID_SAMPLE: case LayerType::kIDENTITY: case LayerType::kITERATOR: case LayerType::kKVCACHE_UPDATE: case LayerType::kLOOP_OUTPUT: case LayerType::kLRN: case LayerType::kMATRIX_MULTIPLY: case LayerType::kMOE: case LayerType::kNMS: case LayerType::kNON_ZERO: case LayerType::kNORMALIZATION: case LayerType::kONE_HOT: case LayerType::kPADDING: case LayerType::kPARAMETRIC_RELU: case LayerType::kPLUGIN: case LayerType::kPLUGIN_V2: case LayerType::kPLUGIN_V3: case LayerType::kPOOLING: case LayerType::kQUANTIZE: case LayerType::kRAGGED_SOFTMAX: case LayerType::kRECURRENCE: case LayerType::kREDUCE: case LayerType::kRESIZE: case LayerType::kREVERSE_SEQUENCE: case LayerType::kROTARY_EMBEDDING: case LayerType::kSCATTER: case LayerType::kSELECT: case LayerType::kSHAPE: case LayerType::kSHUFFLE: case LayerType::kSLICE: case LayerType::kSOFTMAX: case LayerType::kSQUEEZE: case LayerType::kTOPK: case LayerType::kTRIP_LIMIT: case LayerType::kUNARY: case LayerType::kUNSQUEEZE: return {}; } return {}; } bool refitFromOnnx(nvinfer1::ICudaEngine& engine, std::string onnxModelFile, bool multiThreading) { sample::gLogInfo << "Refitting engine from ONNX model " << onnxModelFile << std::endl; std::unique_ptr refitter{createRefitter(engine)}; if (multiThreading && !refitter->setMaxThreads(10)) { sample::gLogError << "Failed to set max threads to refitter." << std::endl; return false; } std::unique_ptr parserRefitter{createONNXRefitter(*refitter)}; if (!parserRefitter->refitFromFile(onnxModelFile.c_str())) { return false; } TrtCudaStream stream; if (!refitter->refitCudaEngineAsync(stream.get())) { return false; } stream.synchronize(); sample::gLogInfo << "Engine successfully refitted from ONNX model " << onnxModelFile << std::endl; return true; } bool timeRefit(INetworkDefinition const& network, nvinfer1::ICudaEngine& engine, bool multiThreading) { using time_point = std::chrono::time_point; using durationMs = std::chrono::duration; auto const nbLayers = network.getNbLayers(); std::unique_ptr refitter{createRefitter(engine)}; // Set max threads that can be used by refitter. if (multiThreading && !refitter->setMaxThreads(10)) { sample::gLogError << "Failed to set max threads to refitter." << std::endl; return false; } auto const& layerWeightsRolePair = getLayerWeightsRolePair(*refitter); // We use std::string instead of char const* since we can have copies of layer names. std::set> layerRoleSet; auto const& layerNames = layerWeightsRolePair.first; auto const& weightsRoles = layerWeightsRolePair.second; std::transform(layerNames.begin(), layerNames.end(), weightsRoles.begin(), std::inserter(layerRoleSet, layerRoleSet.begin()), [](std::string const& layerName, WeightsRole const role) { return std::make_pair(layerName, role); }); auto const isRefittable = [&layerRoleSet](char const* layerName, WeightsRole const role) { return layerRoleSet.find(std::make_pair(layerName, role)) != layerRoleSet.end(); }; auto const setWeights = [&] { for (int32_t i = 0; i < nbLayers; i++) { auto const layer = network.getLayer(i); auto const roleWeightsVec = getAllRefitWeightsForLayer(*layer); for (auto const& roleWeights : roleWeightsVec) { if (isRefittable(layer->getName(), roleWeights.first)) { bool const success = refitter->setWeights(layer->getName(), roleWeights.first, roleWeights.second); if (!success) { return false; } } } } return true; }; auto const reportMissingWeights = [&] { auto const& missingPair = getMissingLayerWeightsRolePair(*refitter); auto const& layerNames = missingPair.first; auto const& weightsRoles = missingPair.second; for (size_t i = 0; i < layerNames.size(); ++i) { sample::gLogError << "Missing (" << layerNames[i] << ", " << weightsRoles[i] << ") for refitting." << std::endl; } return layerNames.empty(); }; // Skip weights validation since we are confident that the new weights are similar to the weights used to build // engine. refitter->setWeightsValidation(false); // Warm up and report missing weights // We only need to set weights for the first time and that can be reused in later refitting process. bool const success = setWeights() && reportMissingWeights() && refitter->refitCudaEngine(); if (!success) { return false; } TrtCudaStream stream; constexpr int32_t kLOOP = 10; time_point const refitStartTime{std::chrono::steady_clock::now()}; { for (int32_t l = 0; l < kLOOP; l++) { if (!refitter->refitCudaEngineAsync(stream.get())) { return false; } } } stream.synchronize(); time_point const refitEndTime{std::chrono::steady_clock::now()}; sample::gLogInfo << "Engine refitted" << " in " << durationMs(refitEndTime - refitStartTime).count() / kLOOP << " ms." << std::endl; return true; } namespace { #if !defined(_WIN32) //! A function-object that calls `dlclose(handle)`. struct DllDeleter { void operator()(void* handle) const noexcept { dlclose(handle); } }; //! If available, \return std::unique_ptr{dlopen(dllName, nonSanitizerFlags)} unless SANITIZER_BUILD, //! in which case dlopen with `RTLD_LAZY | RTLD_NODELETE` flags. [[nodiscard]] auto doDlopen(char const* dllName, int32_t nonSanitizerFlags) { auto flags = nonSanitizerFlags; #if SANITIZER_BUILD // Sanitizer builds override the flags: flags = RTLD_LAZY | RTLD_NODELETE; #endif // SANITIZER_BUILD return std::unique_ptr{dlopen(dllName, flags)}; } [[nodiscard]] auto initSafeRuntime() { // Currently libnvinfer_safe_debug.so for samplesCommon::isDebug() is not ready. return doDlopen("libnvinfer_safe.so", RTLD_LAZY | RTLD_GLOBAL); } [[nodiscard]] auto initConsistencyCheckerLibrary() { return doDlopen("libnvinfer_checker_shared.so", RTLD_LAZY); } static auto const kSAFE_RUNTIME_LIBRARY{initSafeRuntime()}; static auto const kCONSISTENCY_CHECKER_LIBRARY{initConsistencyCheckerLibrary()}; #else static constexpr auto kSAFE_RUNTIME_LIBRARY = nullptr; static constexpr auto kCONSISTENCY_CHECKER_LIBRARY = nullptr; #endif // !defined(_WIN32) } // namespace #if ENABLE_UNIFIED_BUILDER std::unique_ptr createConsistencyChecker( sample::SampleSafeRecorder& recorder, void const* serializedEngine, int32_t const engineSize, std::vector const& pluginBuildLibPath) noexcept { if (serializedEngine == nullptr || engineSize == 0) { return nullptr; } #if !defined(_WIN32) if (hasSafeRuntime()) { constexpr char symbolName[] = "createConsistencyChecker"; using CreateCheckerFn = ErrorCode (*)(nvinfer2::safe::consistency::IConsistencyChecker*& checker, sample::SampleSafeRecorder& recorder, void const* data, size_t size, std::vector const& pluginBuildLibPath); if (auto const createFn = reinterpret_cast(dlsym(kCONSISTENCY_CHECKER_LIBRARY.get(), symbolName))) { if (nvinfer2::safe::consistency::IConsistencyChecker * checker{nullptr}; ErrorCode::kSUCCESS == createFn(checker, recorder, serializedEngine, engineSize, pluginBuildLibPath)) { return std::unique_ptr{checker}; } } } #endif return nullptr; } #endif bool hasSafeRuntime() { return kSAFE_RUNTIME_LIBRARY != nullptr; } bool hasConsistencyChecker() { return kCONSISTENCY_CHECKER_LIBRARY != nullptr; } bool checkSafeEngine( void const* serializedEngine, int64_t const engineSize, std::vector const& pluginBuildLibPath) { #if !ENABLE_UNIFIED_BUILDER return false; #else if (!hasConsistencyChecker()) { sample::gLogError << "Cannot perform consistency check because the checker is not loaded." << std::endl; return false; } sample::SampleSafeRecorder recorder{nvinfer2::safe::Severity::kINFO}; std::unique_ptr checker = createConsistencyChecker(recorder, serializedEngine, engineSize, pluginBuildLibPath); if (checker == nullptr) { sample::gLogError << "Failed to create consistency checker." << std::endl; return false; } sample::gLogInfo << "Start consistency checking." << std::endl; if (!checker->validate()) { sample::gLogError << "Consistency validation failed." << std::endl; return false; } sample::gLogInfo << "Consistency validation passed." << std::endl; return true; #endif } } // namespace sample