Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
e4db511
perf(trt): GPU-fuse face-restoration paste_back (39ms->2.4ms) + bench…
wangzijian1010 May 30, 2026
421c054
perf(trt): cache static mask + GPU-fuse face-restoration preprocess (…
wangzijian1010 May 30, 2026
7c122bb
docs(readme): add Benchmark section for the GPU-optimized face-restor…
wangzijian1010 May 30, 2026
965e37b
docs(readme): reframe Benchmark as a pipeline-wide GPU-optimization e…
wangzijian1010 May 30, 2026
deb9f94
docs(readme): make Benchmark a flat per-algorithm table
wangzijian1010 May 30, 2026
68c2cd5
Update README.md
DefTruth May 31, 2026
e07415c
refactor: drop MNN/NCNN/TNN backends, keep ORT (reference) + TRT (pro…
wangzijian1010 May 31, 2026
fadba05
docs(readme): reframe around extreme-GPU-inference + FaceFusion flagship
wangzijian1010 May 31, 2026
24f8efe
feat(facefusion): out-of-box CLI runner + quickstart for the flagship…
wangzijian1010 May 31, 2026
0014e40
fix(facefusion): fail fast with clear errors instead of segfaulting
wangzijian1010 May 31, 2026
e590a7b
feat(facefusion): per-stage pipeline profiling + whole-pipeline bench…
wangzijian1010 Jun 5, 2026
eca2d23
fix(trt): stop unbounded GPU/host memory growth in the facefusion pip…
wangzijian1010 Jun 5, 2026
159eac6
feat(facefusion): in-memory Mat-in/Mat-out pipeline API + compute-onl…
wangzijian1010 Jun 5, 2026
83c7acc
perf(trt): GPU-fuse the face-swap paste-back (swap 16.9 -> 9.4ms)
wangzijian1010 Jun 5, 2026
7603793
perf(trt): GPU-fuse the face-detect (yoloface) preprocess (detect 9.6…
wangzijian1010 Jun 7, 2026
15aa2ca
tools: mixed-precision GFPGAN FP16 engine builder for TRT 10.1
wangzijian1010 Jun 7, 2026
8acca60
fix(trt): isolate ROI in yoloface letterbox padding (BORDER_ISOLATED)
wangzijian1010 Jun 7, 2026
1090755
perf/cleanup(trt): hoist per-frame constants out of the face-swap hot…
wangzijian1010 Jun 7, 2026
5885953
feat(facefusion): split pipeline into prepare_source() + process() (s…
wangzijian1010 Jun 7, 2026
d535d8a
perf(trt): fold restoration postprocess RGB->BGR + uint8->float into …
wangzijian1010 Jun 7, 2026
75ec4de
perf(trt): GPU-resident NPP warp for face-restoration preprocess (dev…
wangzijian1010 Jun 7, 2026
c842356
perf(facefusion): ship mixed-precision GFPGAN by default (clean FP16,…
wangzijian1010 Jun 7, 2026
6af7f38
perf(trt): DeviceFrame brick 1 — restoration uploads input frame once…
wangzijian1010 Jun 7, 2026
dc44469
perf(trt): DeviceFrame brick 2 — fold restoration's blend_frame into …
wangzijian1010 Jun 7, 2026
f5d50d9
perf(trt): DeviceFrame brick 3 — weld the swap->restoration seam (no …
wangzijian1010 Jun 7, 2026
dc2a4a5
perf(trt): DeviceFrame brick 4 — restoration crop stays on device (po…
wangzijian1010 Jun 7, 2026
ab10242
perf(trt): DeviceFrame brick 5 — swap input device-resident via one s…
wangzijian1010 Jun 7, 2026
23c4932
Update README.md
DefTruth Jun 8, 2026
a9495d6
docs: reframe README/quickstart around the device-resident pipeline +…
wangzijian1010 Jun 14, 2026
2019bd6
Update README.md
DefTruth Jun 16, 2026
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
28 changes: 28 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,34 @@ Run `bash ./build.sh tensorrt` to build lite.ai.toolkit with TensorRT support, a
auto *yolov5 = new lite::trt::cv::detection::YOLOV5(engine_path);
```

## ⚡ Benchmark 🔥
<div id="benchmark"></div>

GPU-inference optimization log. For each algorithm we profile it with a built-in, backend-agnostic harness ([`lite/bench/profiler.h`](https://github.com/xlite-dev/lite.ai.toolkit/blob/main/lite/bench/profiler.h)), then move the CPU pre/post-processing (affine warp, color convert, normalize, tensor layout, paste-back, NMS …) into **fused CUDA kernels** with reused device buffers and pinned + async copies, so the algorithm spends its time on real inference instead of host glue and `cudaMalloc`/sync round-trips. All numbers are **RTX 4090 · TensorRT 10.1 · CUDA 12.4**, median (p50), compute-only, reproducible via the `lite_*_bench` binaries.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

benchmark部分建议使用最新的tensorrt,以及cuda 13+


| Algorithm | Before | After | Speedup | What changed |
|:--|:--:|:--:|:--:|:--|
| **FaceFusion · face restoration (GFPGAN 1.4)** | 78.2 ms<br>(12.8 FPS) | **17.7 ms<br>(56.6 FPS)** | **4.4×** | inverse-mapping paste-back kernel (replaces 2× CPU `warpAffine` + per-frame `cudaMalloc`); cached static mask; fused `bgr2rgb+normalize+CHW` straight into the input buffer |
| FaceFusion · face detect (YOLOv8-face) | 🚧 | 🚧 | — | bbox decode + NMS → CUDA |
| FaceFusion · 68 landmarks (2DFAN4) | 🚧 | 🚧 | — | warp + preprocess → CUDA |
| FaceFusion · face swap (InSwapper) | 🚧 | 🚧 | — | warp + paste → CUDA |
| FP16 / mixed-precision | 🚧 | 🚧 | — | layer-pinned style convs (keep sensitive layers FP32) |

<details>
<summary><b>FaceFusion · face restoration — per-stage breakdown</b></summary>

| Stage | Baseline (ms) | Optimized (ms) | Speedup |
|:--|:--:|:--:|:--:|
| preprocess (warp + bgr2rgb + normalize + tensor) | 14.49 | 1.25 | **11.6×** |
| inference (TensorRT) | 11.30 | 10.79 | 1.05× |
| postprocess (incl. paste-back) | 52.02 | 5.32 | **9.8×** |
| &nbsp;&nbsp;└ paste-back | 39.07 | 2.34 | **16.7×** |
| **End-to-end** | **78.17** | **17.66** | **4.4×** |

paste-back is numerically equivalent to the CPU path (max |diff| = 2/255). The static box mask used to be rebuilt every frame (a large-kernel Gaussian blur) although it only depends on the crop size. With pre/post off the critical path, inference is now ~60% of the stage — FP16 is the next lever.

</details>

## Quick Setup 👀

To quickly setup `lite.ai.toolkit`, you can follow the `CMakeLists.txt` listed as belows. 👇👀
Expand Down
1 change: 1 addition & 0 deletions examples/lite/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,7 @@ add_lite_executable(lite_face_68landmarks cv)
add_lite_executable(lite_face_recognizer cv)
add_lite_executable(lite_face_swap cv)
add_lite_executable(lite_face_restoration cv)
add_lite_executable(lite_face_restoration_bench cv)
add_lite_executable(lite_facefusion_pipeline cv)
add_lite_executable(lite_yolov8 cv)
add_lite_executable(lite_yolov11 cv)
Expand Down
110 changes: 110 additions & 0 deletions examples/lite/cv/test_lite_face_restoration_bench.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
//
// End-to-end benchmark for the GFPGAN face-restoration stage (Phase 0).
// Usage:
// lite_face_restoration_bench [engine_path] [test_img] [iters] [warmup] [csv]
// Defaults point at a gfpgan engine + a test image on the remote 4090; override via argv.
//
// It first runs a CPU-vs-GPU paste_back equivalence check, then a compute-only
// latency/throughput benchmark of restore() with per-stage aggregation
// (preprocess / infer / postprocess / paste_back). Disk I/O (imwrite) is kept
// out of the timed loop; one result image is saved afterwards for visual checking.
//
#include "lite/lite.h"
#include "lite/bench/profiler.h"

#ifdef ENABLE_TENSORRT
#include "lite/trt/cv/trt_face_restoration.h"
#include "lite/ort/cv/face_utils.h"
#include "lite/trt/kernel/paste_back_manager.h"

// A/B numerical check: per-pixel difference between the GPU fused paste_back and the
// CPU reference, on the same real inputs (real affine + real crop).
static void check_paste_back_equivalence(const cv::Mat &frame,
std::vector<cv::Point2f> &lmk5) {
// Run the real warp to obtain the real affine + real 512 crop (uint8), then to float (as in the pipeline)
cv::Mat crop_u8, affine;
std::tie(crop_u8, affine) =
face_utils::warp_face_by_face_landmark_5(frame, lmk5, face_utils::FFHQ_512);
cv::Mat crop_f;
crop_u8.convertTo(crop_f, CV_32FC3);
cv::Mat mask = face_utils::create_static_box_mask({512, 512});

cv::Mat out_cpu = launch_paste_back(frame, crop_f, mask, affine);
PasteBackGPU gpu;
cv::Mat out_gpu = gpu.paste_back(frame, crop_f, mask, affine, nullptr);

cv::Mat diff;
cv::absdiff(out_cpu, out_gpu, diff);
cv::Scalar mean_diff = cv::mean(diff);
double max_diff = 0.0;
cv::minMaxLoc(diff.reshape(1), nullptr, &max_diff);
std::cout << "[check] paste_back CPU vs GPU max|diff|=" << max_diff
<< " mean|diff|(B,G,R)=" << mean_diff[0] << "," << mean_diff[1]
<< "," << mean_diff[2] << " (uint8 pixel values, smaller = closer)" << std::endl;
}
#endif

int main(__unused int argc, __unused char *argv[]) {
#ifdef ENABLE_TENSORRT
std::string engine_path =
argc > 1 ? argv[1] : "/root/autodl-tmp/gfpgan_proj/gfpgan_fp32.engine";
std::string test_img_path =
argc > 2 ? argv[2] : "../../../examples/lite/resources/test_lite_face_restoration.jpg";
int iters = argc > 3 ? std::atoi(argv[3]) : 50;
int warmup = argc > 4 ? std::atoi(argv[4]) : 10;
std::string csv_path = argc > 5 ? argv[5] : "bench_face_restoration.csv";

// Fixed 5-point landmarks (same as test_lite_face_restoration.cpp); the benchmark only
// cares about timing, so whether they exactly match the image does not affect the numbers.
std::vector<cv::Point2f> face_landmark_5 = {
cv::Point2f(569.092041f, 398.845886f),
cv::Point2f(701.891724f, 399.156677f),
cv::Point2f(634.767212f, 482.927216f),
cv::Point2f(584.270996f, 543.294617f),
cv::Point2f(684.877991f, 543.067078f)};

cv::Mat img_bgr = cv::imread(test_img_path);
if (img_bgr.empty()) {
std::cerr << "[bench] cannot read test image: " << test_img_path << std::endl;
return 1;
}

std::cout << "[bench] engine=" << engine_path << "\n[bench] img="
<< test_img_path << " (" << img_bgr.cols << "x" << img_bgr.rows
<< ")\n[bench] warmup=" << warmup << " iters=" << iters << std::endl;

// Numerical correctness check (CPU vs GPU paste_back) before benchmarking
check_paste_back_equivalence(img_bgr, face_landmark_5);

trtcv::TRTFaceFusionFaceRestoration restorer(engine_path);
const std::string tmp_out = "/tmp/bench_restoration_out.jpg";

// Warmup (first runs include lazy engine/context init and cudnn autotune; excluded from stats)
for (int i = 0; i < warmup; ++i) {
restorer.restore(img_bgr, face_landmark_5, nullptr);
}

// Timed: restore() does not write to disk; the profiler collects per-stage timings
// (preprocess/infer/postprocess/paste_back) and the end-to-end TOTAL. imwrite is moved
// out of the loop; one image is saved at the end for visual verification.
lite::bench::Profiler prof;
cv::Mat dst;
for (int i = 0; i < iters; ++i) {
lite::bench::CpuTimer t;
t.start();
dst = restorer.restore(img_bgr, face_landmark_5, &prof);
prof.tick(t.stop_ms());
}

prof.report("GFPGAN face restoration (compute-only, no disk I/O)");
prof.to_csv(csv_path);

if (!dst.empty()) {
cv::imwrite(tmp_out, dst);
std::cout << "[bench] sample result (saved once, outside the loop): " << tmp_out << std::endl;
}
#else
std::cerr << "This benchmark requires ENABLE_TENSORRT=ON." << std::endl;
#endif
return 0;
}
Loading