Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
329 changes: 267 additions & 62 deletions CMakeLists.txt

Large diffs are not rendered by default.

24 changes: 23 additions & 1 deletion cmake/trtmc_pipeline_plugins.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,25 @@ if(TRTMC_MODEL_PROOF_MODEL)
endif()
endif()

if(TRTMC_RUNTIME_MODELS)
set(_trtmc_selected_model_manifests)
foreach(_trtmc_model IN LISTS TRTMC_RUNTIME_MODELS)
if(NOT _trtmc_model MATCHES "^[A-Za-z0-9_.-]+$")
message(FATAL_ERROR
"TRTMC_RUNTIME_MODELS contains an unsafe model id: '${_trtmc_model}'")
endif()
set(_trtmc_selected_manifest
"${PROJECT_SOURCE_DIR}/src/runtime/models/${_trtmc_model}/MODEL.toml")
if(NOT EXISTS "${_trtmc_selected_manifest}")
message(FATAL_ERROR
"TRTMC_RUNTIME_MODELS requests unknown model '${_trtmc_model}'")
endif()
list(APPEND _trtmc_selected_model_manifests "${_trtmc_selected_manifest}")
endforeach()
list(REMOVE_DUPLICATES _trtmc_selected_model_manifests)
set(TRTMC_RUNTIME_MODEL_MANIFESTS ${_trtmc_selected_model_manifests})
endif()

set(TRTMC_RUNTIME_MODEL_IDS)
foreach(_trtmc_model_manifest IN LISTS TRTMC_RUNTIME_MODEL_MANIFESTS)
get_filename_component(_trtmc_model_dir "${_trtmc_model_manifest}" DIRECTORY)
Expand All @@ -71,7 +90,10 @@ foreach(_trtmc_model_manifest IN LISTS TRTMC_RUNTIME_MODEL_MANIFESTS)

_trtmc_model_manifest_string("${_trtmc_model_manifest_text}" "runtime_library"
_trtmc_runtime_library)
if(NOT _trtmc_runtime_library)
if(WIN32)
set(_trtmc_runtime_library
"${CMAKE_SHARED_LIBRARY_PREFIX}trtmc_model_${_trtmc_model}${CMAKE_SHARED_LIBRARY_SUFFIX}")
elseif(NOT _trtmc_runtime_library)
set(_trtmc_runtime_library "libtrtmc_model_${_trtmc_model}.so")
endif()

Expand Down
73 changes: 64 additions & 9 deletions examples/trtmc_benchmark_worker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include <cmath>
#include <cstdint>
#include <cstdlib>
#include <cstring>
#include <fstream>
#include <iostream>
#include <nlohmann/json.hpp>
Expand Down Expand Up @@ -252,6 +253,12 @@ double finite_sum(const std::vector<float>& values) {
});
}

std::size_t nonfinite_count(const std::vector<float>& values) {
return static_cast<std::size_t>(std::count_if(values.begin(), values.end(), [](float value) {
return !std::isfinite(value);
}));
}

Json run_generate(trtmc::IPipeline& pipeline, const Json& request, const TimingConfig& timing) {
const int warmup = timing.warmup;
const int iterations = timing.iterations;
Expand Down Expand Up @@ -495,9 +502,16 @@ std::vector<trtmc::ImageResult> generate_images(trtmc::IPipeline& pipeline,
const std::vector<std::uint32_t>& seeds,
const trtmc::io::LoadedImage& input_image,
const trtmc::GenerateConfig& config) {
if (!input_image.empty()) {
return {pipeline.generate_image(prompts.front(), input_image.pixels.data(),
input_image.height, input_image.width, config)};
if (prompts.size() == 1) {
if (seeds.size() != 1)
throw std::runtime_error("single-image generation requires exactly one seed");
auto single_config = config;
single_config.seed = static_cast<int32_t>(seeds.front());
if (!input_image.empty()) {
return {pipeline.generate_image(prompts.front(), input_image.pixels.data(),
input_image.height, input_image.width, single_config)};
}
return {pipeline.generate_image(prompts.front(), single_config)};
}
return pipeline.generate_image_batch(prompts, seeds, config);
}
Expand All @@ -518,37 +532,66 @@ Json run_generate_image(trtmc::IPipeline& pipeline, const Json& request,
}
Json observations = Json::array();
for (int index = 0; index < timing.iterations; ++index) {
const IterationTimer timer(timing.scope);
last = generate();
// Drop the previous host result before opening the public-call timing
// boundary. Destroying a 124-frame float tensor must not be charged to
// the next inference request.
last.clear();
last.shrink_to_fit();
std::vector<trtmc::ImageResult> current;
double measured_ms = 0.0;
if (prompts.size() == 1) {
auto single_config = config;
single_config.seed = static_cast<int32_t>(seeds.front());
const IterationTimer timer(timing.scope);
auto image = input_image.empty()
? pipeline.generate_image(prompts.front(), single_config)
: pipeline.generate_image(prompts.front(), input_image.pixels.data(),
input_image.height, input_image.width,
single_config);
measured_ms = timer.elapsed_ms();
current.push_back(std::move(image));
} else {
const IterationTimer timer(timing.scope);
current = generate();
measured_ms = timer.elapsed_ms();
}
const std::size_t generated_pixels =
std::accumulate(last.begin(), last.end(), std::size_t{0},
std::accumulate(current.begin(), current.end(), std::size_t{0},
[](std::size_t count, const trtmc::ImageResult& image) {
return count + image.pixels.size();
});
const std::size_t generated_frames = std::accumulate(
last.begin(), last.end(), std::size_t{0},
current.begin(), current.end(), std::size_t{0},
[](std::size_t count, const trtmc::ImageResult& image) {
return count + static_cast<std::size_t>(std::max<int32_t>(image.num_frames, 1));
});
const double measured_ms = timer.elapsed_ms();
const std::size_t generated_nonfinite =
std::accumulate(current.begin(), current.end(), std::size_t{0},
[](std::size_t count, const trtmc::ImageResult& image) {
return count + nonfinite_count(image.pixels);
});
observations.push_back({
{"iteration", index},
{"measured_wall_ms", measured_ms},
{"runtime_e2e_wall_ms", measured_ms},
{"generated_images", last.size()},
{"generated_images", current.size()},
{"generated_frames", generated_frames},
{"generated_pixels", generated_pixels},
{"nonfinite_elements", generated_nonfinite},
});
last = std::move(current);
}
if (last.empty()) {
throw std::runtime_error("generate_image_batch returned no images");
}
const auto& first = last.front();
double output_sum = 0.0;
std::size_t element_count = 0;
std::size_t output_nonfinite = 0;
for (const auto& image : last) {
output_sum += finite_sum(image.pixels);
element_count += image.pixels.size();
output_nonfinite += nonfinite_count(image.pixels);
}
return {
{"observations", std::move(observations)},
Expand All @@ -560,6 +603,7 @@ Json run_generate_image(trtmc::IPipeline& pipeline, const Json& request,
{"channels", first.channels},
{"num_frames", first.num_frames},
{"element_count", element_count},
{"nonfinite_elements", output_nonfinite},
{"finite_sum", output_sum},
}},
};
Expand Down Expand Up @@ -1142,6 +1186,17 @@ int main(int argc, char** argv) {
}
output_path = arguments.output_path;
write_json(output_path, execute(read_json(arguments.request_path)));
// This executable is an isolated worker and has no useful process-global
// teardown after its result file is closed. TensorRT-RTX may retain DLL
// globals past pipeline destruction on Windows, so an opt-in immediate
// exit avoids unsafe cross-DLL static destruction without changing the
// measured call or the persisted result.
const char* fast_exit = std::getenv("TRTMC_BENCHMARK_FAST_EXIT");
if (fast_exit != nullptr && fast_exit[0] != '\0' && std::strcmp(fast_exit, "0") != 0) {
std::cerr.flush();
std::cout.flush();
std::_Exit(0);
}
return 0;
} catch (const std::exception& exception) {
std::cerr << "trtmc_benchmark_worker: " << exception.what() << '\n';
Expand Down
7 changes: 3 additions & 4 deletions include/trtmc/runtime/distributed_runtime.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,9 @@ struct DistributedRuntimeGroup {

// Initialize an NCCL communicator for TensorRT 11.0+ distributed collective layers.
//
// This intentionally avoids compile-time MPI/NCCL dependencies: ranks are
// discovered from common mpirun environment variables, and NCCL is loaded with
// dlopen at runtime. Rank 0 writes the NCCL unique ID to a small rendezvous
// file under /tmp unless TRTMC_NCCL_RENDEZVOUS points elsewhere.
// Linux avoids compile-time MPI/NCCL dependencies: ranks are discovered from
// common mpirun environment variables and NCCL is loaded at runtime. Native
// Windows builds support only the single-device case and reject tp_size > 1.
DistributedRuntimeGroup initialize_tensor_parallel_group(int tp_size);

} // namespace trtmc
5 changes: 3 additions & 2 deletions include/trtmc/runtime/pipeline_plugin_loader.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ std::optional<std::string> legacy_runtime_strategy_alias_target(const std::strin
const std::string& config_text);

// Load the model plugin that owns strategy. Search paths are directories that
// contain libtrtmc_model_<model>.so; TRTMC_MODEL_PLUGIN_DIR and build/install
// defaults are consulted after these explicit paths. When
// contain the platform-native trtmc_model_<model> shared library;
// TRTMC_MODEL_PLUGIN_DIR and build/install defaults are consulted after these
// explicit paths. When
// TRTMC_MODEL_PLUGIN_STRICT=1, only explicit paths and TRTMC_MODEL_PLUGIN_DIR
// are used, so an installed or stale build-tree DSO cannot satisfy a CI proof.
void load_model_plugin_for_strategy(const std::string& strategy,
Expand Down
5 changes: 3 additions & 2 deletions python/tensorrt_model_connect/bundle_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,8 +178,9 @@ def _open_atomic_bundle_output(destination: Path):
except FileExistsError:
continue
try:
if destination_mode is not None:
os.fchmod(descriptor, destination_mode)
fchmod = getattr(os, "fchmod", None)
if destination_mode is not None and fchmod is not None:
fchmod(descriptor, destination_mode)
return os.fdopen(descriptor, "wb"), temporary_path
except Exception:
os.close(descriptor)
Expand Down
29 changes: 29 additions & 0 deletions python/tensorrt_model_connect/engine_builder.py
Original file line number Diff line number Diff line change
Expand Up @@ -1722,6 +1722,35 @@ def _build_diffusion_bundle(
if "_transformer_config" in weights:
config.raw["_transformer_config"] = weights["_transformer_config"]

# A model may require isolated component processes and file-backed bundle
# sections when built for RTX. Keep that policy model-owned; ordinary TRT
# diffusion builds continue through build_components() below.
build_staged_bundle = getattr(plugin, "build_staged_bundle", None)
if rtx and callable(build_staged_bundle):
if fp8_scales or save_fp8_scales:
raise ValueError("Staged TensorRT-RTX builds do not support FP8 calibration")
staged_t0 = time.monotonic()
build_staged_bundle(
str(model_dir_path),
output_path,
config,
weights,
precision=precision,
verbose=verbose,
parallel_config=parallel,
max_batch_size=max_batch_size,
)
staged_elapsed = time.monotonic() - staged_t0
_add_build_timing(build_timing, "trt_compile_diffusion_components_s", staged_elapsed)
build_timing["total_s"] = time.monotonic() - t0
_write_build_timing(build_timing)
print(
f"[trtmc build] Staged RTX bundle saved: {output_path} "
f"[{staged_elapsed:.1f}s]",
file=sys.stderr,
)
return

# Prefer a family-provided scale asset before running live calibration.
if fp8_scales == "auto":
precomputed_fn = getattr(plugin, "fp8_precomputed_scales", None)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

import gc
import sys
from pathlib import Path

from tensorrt_model_connect import trt_compat

Expand Down Expand Up @@ -44,20 +45,23 @@ def checkpoint_keys(
return tuple(names)


@op.cleanup_failed_build
def build_adaln_precompute_engine(
weights: dict,
profile: MiniMaxH3Config,
*,
verbose: bool = False,
consume_weights: bool = False,
workspace_bytes: int | None = None,
) -> bytes:
weight_streaming: bool = False,
output_path: str | Path | None = None,
) -> bytes | dict[str, int | str]:
profile.validate()
logger = trt.Logger(trt.Logger.VERBOSE if verbose else trt.Logger.WARNING)
builder = trt.Builder(logger)
network = builder.create_network(1 << int(trt.NetworkDefinitionCreationFlag.STRONGLY_TYPED))
config = builder.create_builder_config()
op.configure_builder(config)
op.configure_builder(config, weight_streaming=weight_streaming)
op.configure_workspace(
config,
workspace_bytes,
Expand Down Expand Up @@ -128,14 +132,21 @@ def build_adaln_precompute_engine(
f"timesteps={profile.max_timestep_count}",
file=sys.stderr,
)
plan = None
record = None
try:
plan = builder.build_serialized_network(network, config)
if output_path is None:
plan = builder.build_serialized_network(network, config)
else:
record = trt_compat.build_serialized_network_to_file(
builder, network, config, output_path
)
finally:
op.release_weight_buffers(network)
if consume_weights:
weights.clear()
if plan is None:
if output_path is None and plan is None:
raise RuntimeError("TensorRT failed to build MiniMax-H3 AdaLN precompute engine")
del network, config, builder
gc.collect()
return bytes(plan)
return record if record is not None else bytes(plan)
7 changes: 7 additions & 0 deletions python/tensorrt_model_connect/families/minimax_h3/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@
DENOISER_DEFAULT_WORKSPACE_BYTES = 96 << 30
VAE_TILE_DECODER_DEFAULT_WORKSPACE_BYTES = 96 << 30

# The RTX path builds each plan in a fresh process, so one conservative
# workspace and runtime budget cover every stage without coupling the public
# artifact to a particular workstation identity.
RTX_STAGED_WORKSPACE_BYTES = 16 << 30
RTX_WEIGHT_STREAMING_BUDGET_BYTES = 32 << 30
RTX_CUDA_MAJOR = 12

DEFAULT_WORKSPACE_LIMIT_BYTES = {
"text_encoder.plan": TEXT_ENCODER_DEFAULT_WORKSPACE_BYTES,
"adaln_precompute.plan": ADALN_PRECOMPUTE_DEFAULT_WORKSPACE_BYTES,
Expand Down
Loading