309 lines
13 KiB
Python
Executable File
309 lines
13 KiB
Python
Executable File
import tensorrt as trt
|
|
import pycuda.driver as cuda
|
|
import pycuda.autoinit
|
|
import numpy as np
|
|
import torch
|
|
import traceback
|
|
import os
|
|
from PIL import Image
|
|
|
|
TRT_LOGGER = trt.Logger()
|
|
SKIP_ENGINE_MODEL_CHECK = True
|
|
|
|
def get_engine(engine_file_path):
|
|
if os.path.exists(engine_file_path):
|
|
print(f"Loading engine from file {engine_file_path}...")
|
|
with open(engine_file_path, "rb") as f, trt.Runtime(TRT_LOGGER) as runtime:
|
|
return runtime.deserialize_cuda_engine(f.read())
|
|
else:
|
|
print(f"No file named {engine_file_path}! Please check the input.")
|
|
return None
|
|
|
|
|
|
def numpy_to_torch_dtype(np_dtype):
|
|
mapping = {
|
|
np.float32: torch.float,
|
|
np.float64: torch.double,
|
|
np.float16: torch.half,
|
|
np.int32: torch.int32,
|
|
np.int64: torch.int64,
|
|
np.int16: torch.int16,
|
|
np.int8: torch.int8,
|
|
np.uint8: torch.uint8,
|
|
np.bool_: torch.bool
|
|
}
|
|
return mapping.get(np_dtype, None)
|
|
|
|
def match_shape(a, b):
|
|
if(len(a) == len(b)):
|
|
return tuple(a) == tuple(b)
|
|
elif len(a) > len(b):
|
|
if(a[0] == 1):
|
|
return match_shape(a[1:], b)
|
|
else:
|
|
if(b[0] == 1):
|
|
return match_shape(a, b[1:])
|
|
return False
|
|
|
|
def match_dtype(a, b):
|
|
if(a.__class__ == torch.dtype):
|
|
a = torch.tensor(0,dtype=a).numpy().dtype
|
|
return a == b
|
|
|
|
|
|
class EngineModel:
|
|
def __init__(self, engine_file_path, stream = None, device_int = 0, extra_lock = None):
|
|
self.device_int = device_int
|
|
self.extra_lock = extra_lock
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.acquire()
|
|
assert os.path.exists(engine_file_path), "Engine model path not exists!"
|
|
self.ctx = cuda.Device(self.device_int).make_context()
|
|
try:
|
|
self.engine = get_engine(engine_file_path) # 载入TensorRT引擎
|
|
input_nvars = 0
|
|
output_nvars = 0
|
|
self.input_names = []
|
|
self.output_names = []
|
|
|
|
# 【辅助函数】用于获取安全的 Shape (消除 -1)
|
|
def get_safe_shape(engine, name):
|
|
shape = engine.get_tensor_shape(name)
|
|
# 如果形状里包含 -1 (动态维度)
|
|
if -1 in shape:
|
|
# 获取 Profile 0 的 (min, opt, max)
|
|
# 取下标 [2] 即 Max Shape,确保分配足够大的显存
|
|
profile = engine.get_tensor_profile_shape(name, 0)
|
|
if profile:
|
|
print(f"[EngineModel] Detected dynamic shape for {name}: {shape} -> Using Max Profile: {profile[2]}")
|
|
return profile[2]
|
|
else:
|
|
# 如果获取不到 Profile (通常发生在 Output),这是一个风险点
|
|
# 这里为了防止报错,可以尝试打印警告
|
|
print(f"[EngineModel] Warning: Dynamic output {name} has no profile. Mem alloc might fail.")
|
|
return shape
|
|
|
|
for binding in self.engine: # 遍历所有tensor,区分Input/Output
|
|
mode = self.engine.get_tensor_mode(binding)
|
|
if(mode== trt.TensorIOMode.INPUT):
|
|
input_nvars += 1
|
|
self.input_names.append(binding)
|
|
elif(mode == trt.TensorIOMode.OUTPUT):
|
|
output_nvars += 1
|
|
self.output_names.append(binding)
|
|
|
|
self.input_nvars = input_nvars # input的数量
|
|
self.output_nvars = output_nvars # output的数量
|
|
|
|
self.input_shapes = {name : get_safe_shape(self.engine, name) for name in self.input_names} # 获取每个 I/O 的 shape 和 dtype
|
|
self.input_dtypes = {name : self.engine.get_tensor_dtype(name) for name in self.input_names}
|
|
self.input_nbytes = {
|
|
name : trt.volume(self.input_shapes[name]) * trt.nptype(self.input_dtypes[name])().itemsize
|
|
for name in self.input_names
|
|
} # nbytes = tensor 占多少 CUDA 内存(字节数)
|
|
self.output_shapes = {name : get_safe_shape(self.engine, name) for name in self.output_names}
|
|
self.output_dtypes = {name : self.engine.get_tensor_dtype(name) for name in self.output_names}
|
|
self.output_nbytes = {
|
|
name : trt.volume(self.output_shapes[name]) * trt.nptype(self.output_dtypes[name])().itemsize
|
|
for name in self.output_names
|
|
}
|
|
self.dinputs = {name : cuda.mem_alloc(self.input_nbytes[name]) for name in self.input_names} # 为每个输入/输出分配 CUDA 设备内存
|
|
self.doutputs = {name :cuda.mem_alloc(self.output_nbytes[name]) for name in self.output_names}
|
|
self.context = self.engine.create_execution_context() # 创建 ExecutionContext(执行上下文)
|
|
if stream is None:
|
|
self.stream = cuda.Stream()
|
|
else:
|
|
self.stream = stream
|
|
for name in self.input_names: # 绑定 tensor 到 context
|
|
self.context.set_tensor_address(name, int(self.dinputs[name]))
|
|
for name in self.output_names:
|
|
self.context.set_tensor_address(name, int(self.doutputs[name]))
|
|
self.houtputs = {
|
|
name :
|
|
cuda.pagelocked_empty(
|
|
trt.volume(self.output_shapes[name]), dtype=trt.nptype(self.output_dtypes[name])
|
|
) for name in self.output_names
|
|
} # 分配 page-locked host 内存以存储输出
|
|
except:
|
|
self.ctx.pop()
|
|
raise Exception("CUDA Initialization Failed!")
|
|
self.ctx.pop()
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.release()
|
|
|
|
def __call__(self, skip_check=SKIP_ENGINE_MODEL_CHECK, output_list=[], return_tensor=False, **inputs):
|
|
if not skip_check:
|
|
for name in inputs:
|
|
assert name in self.input_names
|
|
assert match_shape(inputs[name].shape, self.input_shapes[name])
|
|
assert match_dtype(inputs[name].dtype, trt.nptype(self.input_dtypes[name]))
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.acquire()
|
|
self.ctx.push()
|
|
r = {}
|
|
try:
|
|
|
|
for name in inputs:
|
|
hinput = inputs[name]
|
|
if (isinstance(hinput,torch.Tensor) and hinput.device.type=="cuda" and hinput.device.index==self.device_int):
|
|
hinput_con = hinput.contiguous()
|
|
ptr = hinput_con.data_ptr()
|
|
cuda.memcpy_dtod_async(self.dinputs[name], ptr, self.input_nbytes[name], self.stream)
|
|
else:
|
|
hinput_con = np.ascontiguousarray(hinput)
|
|
cuda.memcpy_htod_async(self.dinputs[name], hinput_con, self.stream)
|
|
for name in self.input_names:
|
|
if name not in inputs:
|
|
self.context.set_input_shape(name, self.input_shapes[name])
|
|
self.context.execute_async_v3(self.stream.handle)
|
|
if(return_tensor):
|
|
for name in output_list:
|
|
t = torch.zeros(trt.volume(self.output_shapes[name]), device=f"cuda:{self.device_int}", dtype=numpy_to_torch_dtype(trt.nptype(self.output_dtypes[name])))
|
|
ptr = t.data_ptr()
|
|
cuda.memcpy_dtod_async(ptr, self.doutputs[name], self.output_nbytes[name], self.stream)
|
|
t = t.reshape(tuple(self.output_shapes[name]))
|
|
r[name] = t
|
|
else:
|
|
for name in output_list:
|
|
cuda.memcpy_dtoh_async(self.houtputs[name], self.doutputs[name], self.stream)
|
|
r[name] = self.houtputs[name]
|
|
self.stream.synchronize()
|
|
except Exception as e:
|
|
print("TensorRT Execution Failed!")
|
|
traceback.print_exc()
|
|
self.ctx.pop()
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.release()
|
|
return None
|
|
self.ctx.pop()
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.release()
|
|
return r
|
|
|
|
|
|
def prefill(self, skip_check=SKIP_ENGINE_MODEL_CHECK, **inputs):
|
|
if not (skip_check):
|
|
for name in inputs:
|
|
in_input = (name in self.input_names)
|
|
assert in_input or (name in self.output_names)
|
|
assert match_shape(inputs[name].shape, self.input_shapes[name] if in_input else self.output_shapes[name])
|
|
assert match_dtype(inputs[name].dtype, trt.nptype(self.input_dtypes[name] if in_input else self.output_dtypes[name]))
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.acquire()
|
|
self.ctx.push()
|
|
try:
|
|
for name in inputs:
|
|
in_input = (name in self.input_names)
|
|
hinput = inputs[name]
|
|
|
|
dst_ptr = self.dinputs[name] if in_input else self.doutputs[name]
|
|
real_nbytes = 0
|
|
if isinstance(hinput, torch.Tensor):
|
|
real_nbytes = hinput.numel() * hinput.element_size()
|
|
else:
|
|
# 假设是 numpy
|
|
real_nbytes = hinput.nbytes
|
|
|
|
if (isinstance(hinput,torch.Tensor) and hinput.device.type=="cuda" and hinput.device.index==self.device_int):
|
|
hinput_con = hinput.contiguous()
|
|
ptr = hinput_con.data_ptr()
|
|
cuda.memcpy_dtod_async(dst_ptr, ptr, real_nbytes, self.stream)
|
|
else:
|
|
hinput_con = np.ascontiguousarray(hinput)
|
|
cuda.memcpy_htod_async(dst_ptr, hinput, self.stream)
|
|
self.stream.synchronize()
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
self.ctx.pop()
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.release()
|
|
return False
|
|
self.ctx.pop()
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.release()
|
|
return True
|
|
|
|
def __repr__(self):
|
|
r = "TensorRTEngineModel(\n\tInput=[\n"
|
|
for name in self.input_names:
|
|
r += f"\t\t{name}: \t{trt.nptype(self.input_dtypes[name]).__name__}{self.input_shapes[name]},\n"
|
|
r += "\t],Output=[\n"
|
|
for name in self.output_names:
|
|
r += f"\t\t{name}: \t{trt.nptype(self.output_dtypes[name]).__name__}{self.output_shapes[name]},\n"
|
|
r+="\t]\n)"
|
|
return r
|
|
|
|
def link(self, other, var_map, skip_check=SKIP_ENGINE_MODEL_CHECK):
|
|
assert self.device_int == other.device_int
|
|
if not (skip_check):
|
|
for source in var_map:
|
|
assert source in other.output_names
|
|
target = var_map[source]
|
|
assert target in self.input_names
|
|
assert match_shape(other.output_shapes[source], self.input_shapes[target])
|
|
assert match_dtype(other.output_dtypes[source], self.input_dtypes[target])
|
|
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.acquire()
|
|
self.ctx.push()
|
|
try:
|
|
for source in var_map:
|
|
target = var_map[source]
|
|
self.context.set_tensor_address(target, int(other.doutputs[source]))
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
self.ctx.pop()
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.release()
|
|
return False
|
|
self.ctx.pop()
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.release()
|
|
return True
|
|
|
|
def bind(self, var_map, skip_check=SKIP_ENGINE_MODEL_CHECK):
|
|
if not (skip_check):
|
|
for source in var_map:
|
|
assert source in self.output_names
|
|
target = var_map[source]
|
|
assert target in self.input_names
|
|
assert match_shape(self.output_shapes[source], self.input_shapes[target])
|
|
assert match_dtype(self.output_dtypes[source], self.input_dtypes[target])
|
|
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.acquire()
|
|
self.ctx.push()
|
|
try:
|
|
for source in var_map:
|
|
target = var_map[source]
|
|
self.context.set_tensor_address(target, int(self.doutputs[source]))
|
|
except Exception as e:
|
|
traceback.print_exc()
|
|
self.ctx.pop()
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.release()
|
|
return False
|
|
self.ctx.pop()
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.release()
|
|
return True
|
|
|
|
def unlink(self):
|
|
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.acquire()
|
|
self.ctx.push()
|
|
try:
|
|
for name in self.input_names:
|
|
self.context.set_tensor_address(name, int(self.dinputs[name]))
|
|
except:
|
|
self.ctx.pop()
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.release()
|
|
return False
|
|
self.ctx.pop()
|
|
if not(self.extra_lock is None):
|
|
self.extra_lock.release()
|
|
return True
|
|
|