Skip to content

Commit f55687b

Browse files
committed
adds dockerfile and updated cicd to use it and updated cli usage with benchmark mode
1 parent 79773a5 commit f55687b

3 files changed

Lines changed: 93 additions & 32 deletions

File tree

.github/workflows/build-check.yml

Lines changed: 11 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -12,48 +12,35 @@ concurrency:
1212

1313
jobs:
1414
# ── Header / compilation smoke test (no GPU required) ──────────────────────
15-
# This job uses a free GitHub-hosted runner. It installs CUDA stubs (headers
16-
# + import libraries only — no driver, no device) so we can compile all
17-
# library and CLI targets and catch API breakage, missing includes, and
18-
# CMake errors without needing a real GPU.
19-
#
20-
# Tests are NOT run here (they require a real CUDA device). Full GPU testing
21-
# happens in ci.yml on the self-hosted runner.
15+
# This job uses a free GitHub-hosted runner with an official NVIDIA Docker container.
16+
# The container provides the CUDA compiler (nvcc) and headers, allowing us to
17+
# compile all library and CLI targets to catch API breakage and CMake errors
18+
# without needing a real GPU.
2219
compile-only:
23-
name: Compile (CPU-only, stub CUDA)
20+
name: Compile (CPU-only)
2421
runs-on: ubuntu-24.04
22+
container: nvcr.io/nvidia/cuda:12.6.0-devel-ubuntu24.04
2523

2624
steps:
2725
- name: Checkout
2826
uses: actions/checkout@v4
2927
with:
3028
submodules: recursive
3129

32-
- name: Install CUDA stub toolkit
30+
- name: Install CMake
3331
run: |
34-
# Install CUDA keyring and then only the headers + stub libs.
35-
# This lets nvcc compile host-side code without a GPU.
36-
wget -qO /tmp/cuda-keyring.deb \
37-
https://developer.download.nvidia.com/compute/cuda/repos/ubuntu2404/x86_64/cuda-keyring_1.1-1_all.deb
38-
sudo dpkg -i /tmp/cuda-keyring.deb
39-
sudo apt-get update -qq
40-
# cuda-cudart-dev — headers for cudart
41-
# cuda-nvcc — the nvcc compiler
42-
# libcuda1-stub — linker stubs (no driver needed)
43-
sudo apt-get install -y --no-install-recommends \
44-
cuda-cudart-dev-12-6 \
45-
cuda-nvcc-12-6 \
46-
libcuda1-560-server || true
47-
echo "/usr/local/cuda/bin" >> "$GITHUB_PATH"
32+
apt-get update -qq
33+
apt-get install -y --no-install-recommends cmake ninja-build git
4834
4935
- name: Configure
5036
run: |
5137
cmake -S . -B build/check \
38+
-G Ninja \
5239
-DCMAKE_BUILD_TYPE=Release \
5340
-DBUILD_TESTING=OFF \
5441
-DBUILD_EXAMPLES=OFF \
5542
-DBUILD_PROFILING=OFF \
5643
-DCMAKE_CUDA_ARCHITECTURES=86
5744
5845
- name: Build (library + CLI only)
59-
run: cmake --build build/check --parallel --target fzgmod fzgmod-cli
46+
run: cmake --build build/check --target fzgmod fzgmod-cli

Dockerfile

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
# Base image: NVIDIA CUDA Toolkit (Development environment)
2+
# Using 12.6 to match the development version used for FZGPUModules
3+
FROM nvcr.io/nvidia/cuda:12.6.0-devel-ubuntu24.04
4+
5+
# Prevent interactive prompts during apt installations
6+
ENV DEBIAN_FRONTEND=noninteractive
7+
8+
# Install essential build tools and dependencies
9+
RUN apt-get update -qq && apt-get install -y --no-install-recommends \
10+
cmake \
11+
ninja-build \
12+
git \
13+
g++ \
14+
libasan8 \
15+
libubsan1 \
16+
curl \
17+
wget \
18+
&& rm -rf /var/lib/apt/lists/*
19+
20+
# Set up a working directory
21+
WORKDIR /workspace
22+
23+
# By default, open a bash shell
24+
CMD ["/bin/bash"]

src/utils/cli/cli.cpp

Lines changed: 58 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -598,30 +598,43 @@ static int run_decompress(CliSettings s) {
598598
size_t output_size = 0;
599599

600600
try {
601+
std::vector<uint8_t> orig;
602+
if (!s.original_path.empty()) {
603+
orig = read_binary_file(s.original_path);
604+
}
605+
601606
const auto t0 = std::chrono::high_resolution_clock::now();
602607
Pipeline::decompressFromFile(s.input_path, &d_output, &output_size, 0);
603608
FZ_CUDA_CHECK(cudaDeviceSynchronize());
604609
const auto t1 = std::chrono::high_resolution_clock::now();
605610
double host_ms = std::chrono::duration<double, std::milli>(t1 - t0).count();
606611

607-
std::vector<uint8_t> host(output_size);
608-
if (output_size > 0) {
609-
FZ_CUDA_CHECK(cudaMemcpy(host.data(), d_output, output_size, cudaMemcpyDeviceToHost));
612+
// Truncate to the original size if we have it (to remove chunk padding)
613+
size_t usable_size = output_size;
614+
if (!orig.empty() && orig.size() < output_size) {
615+
usable_size = orig.size();
616+
}
617+
618+
std::vector<uint8_t> host(usable_size);
619+
if (usable_size > 0) {
620+
FZ_CUDA_CHECK(cudaMemcpy(host.data(), d_output, usable_size, cudaMemcpyDeviceToHost));
610621
}
611622

612623
if (!s.output_path.empty()) {
613624
write_binary_file(s.output_path, host.data(), host.size());
614625
}
615626

616627
if (s.report || !s.original_path.empty()) {
617-
double tput = static_cast<double>(output_size) / (host_ms * 1e-3) / 1e9;
628+
double tput = static_cast<double>(usable_size) / (host_ms * 1e-3) / 1e9;
618629
std::cout << "\n[Decompress Report]\n"
619-
<< " Output size: " << output_size << " bytes\n"
620-
<< " Time: " << std::fixed << std::setprecision(3) << host_ms << " ms\n"
630+
<< " Output size: " << usable_size << " bytes\n";
631+
if (usable_size != output_size) {
632+
std::cout << " (Padded size: " << output_size << " bytes, truncated to match original)\n";
633+
}
634+
std::cout << " Time: " << std::fixed << std::setprecision(3) << host_ms << " ms\n"
621635
<< " Throughput: " << std::setprecision(2) << tput << " GB/s\n";
622636

623-
if (!s.original_path.empty()) {
624-
std::vector<uint8_t> orig = read_binary_file(s.original_path);
637+
if (!orig.empty()) {
625638
if (orig.size() != host.size()) {
626639
std::cout << " Compare error: Size mismatch! Original=" << orig.size()
627640
<< ", Reconstructed=" << host.size() << "\n";
@@ -683,6 +696,7 @@ static int run_benchmark(CliSettings s) {
683696
FZ_CUDA_CHECK(cudaDeviceSynchronize());
684697

685698
TimingSummary compress_stats, decompress_stats;
699+
std::vector<uint8_t> final_recon;
686700

687701
for (int i = 0; i < s.benchmark_runs; ++i) {
688702
const auto t0 = std::chrono::high_resolution_clock::now();
@@ -704,6 +718,11 @@ static int run_benchmark(CliSettings s) {
704718
}
705719
decompress_stats.add(std::chrono::duration<double, std::milli>(t3 - t2).count(), pipeline->getLastPerfResult().dag_elapsed_ms);
706720

721+
if ((s.report || !s.original_path.empty()) && i == s.benchmark_runs - 1) {
722+
final_recon.resize(recon_size);
723+
FZ_CUDA_CHECK(cudaMemcpy(final_recon.data(), d_recon, recon_size, cudaMemcpyDeviceToHost));
724+
}
725+
707726
if (!pipeline->isPoolManagedDecompOutput() && d_recon) {
708727
FZ_CUDA_CHECK(cudaFree(d_recon));
709728
}
@@ -712,6 +731,37 @@ static int run_benchmark(CliSettings s) {
712731
print_summary("compress", compress_stats, payload_bytes);
713732
print_summary("decompress", decompress_stats, payload_bytes);
714733

734+
if (s.report || !s.original_path.empty()) {
735+
double ratio = static_cast<double>(payload_bytes) / compressed_size;
736+
std::cout << "\n[Quality Report]\n"
737+
<< " Input size: " << payload_bytes << " bytes\n"
738+
<< " Compressed size: " << compressed_size << " bytes\n"
739+
<< " Ratio: " << std::fixed << std::setprecision(2) << ratio << "x\n";
740+
741+
std::vector<uint8_t> orig;
742+
if (!s.original_path.empty()) {
743+
orig = read_binary_file(s.original_path);
744+
} else {
745+
orig = input_bytes;
746+
}
747+
748+
if (!orig.empty() && orig.size() == final_recon.size()) {
749+
Metrics m;
750+
if (s.type == "f32") m = calc_metrics<float>(orig, final_recon);
751+
else if (s.type == "f64") m = calc_metrics<double>(orig, final_recon);
752+
else if (s.type == "i32") m = calc_metrics<int32_t>(orig, final_recon);
753+
else if (s.type == "i64") m = calc_metrics<int64_t>(orig, final_recon);
754+
755+
std::cout << " Value Range: [" << std::scientific << m.val_min << ", " << m.val_max << "] (Span: " << m.val_range << ")\n"
756+
<< " Max Abs Error: " << std::scientific << m.max_err << "\n"
757+
<< " PSNR: " << std::fixed << std::setprecision(2) << m.psnr << " dB\n"
758+
<< " NRMSE: " << std::scientific << m.nrmse << "\n";
759+
} else if (!orig.empty()) {
760+
std::cout << " Compare error: Size mismatch! Original=" << orig.size()
761+
<< ", Reconstructed=" << final_recon.size() << "\n";
762+
}
763+
}
764+
715765
} catch (...) {
716766
if (d_input) FZ_CUDA_CHECK_WARN(cudaFree(d_input));
717767
throw;

0 commit comments

Comments
 (0)