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
28 changes: 28 additions & 0 deletions zstd/zstdgpu_ci_tests/main.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -116,6 +116,8 @@ static void PrintUsage(const char* exe)
<< " within --gbv-max-mb). A positive N samples that many files by stride.\n"
<< " --gpu-name <name> Adapter name of this machine. Consumed only by the manifest's\n"
<< " scenario_skips; if omitted, no scenario is skipped by GPU name.\n"
<< " --gpu-ven-id <hex-id> Optional PCI vendor ID forwarded to zstdgpu_demo for adapter\n"
<< " selection (for example, 10de selects NVIDIA).\n"
<< " --gbv-max-mb <N> Max largest single frame decompressed size for GBV tests in MB (default: 4).\n"
<< " A value <= 0 disables the cap and runs GBV on files of any size.\n"
<< " --max-frame-mb <N> Skip any .zst file whose largest single on-disk zstd frame exceeds N MB,\n"
Expand DownExpand Up@@ -188,6 +190,22 @@ static bool ParseArgs(int argc, char** argv, TestConfig& config, bool& shouldExi
{
config.gpuName = argv[++i];
}
else if (std::strcmp(argv[i], "--gpu-ven-id") == 0)
{
if (i + 1 >= argc)
{
std::cerr << "Error: --gpu-ven-id requires a hexadecimal value." << std::endl;
return false;
}
std::string parseError;
const std::string value = argv[++i];
if (!ParseGpuVendorId(value, config.gpuVendorId, parseError))
{
std::cerr << "Error: invalid --gpu-ven-id '" << value << "': "
<< parseError << "." << std::endl;
return false;
}
}
else if (std::strcmp(argv[i], "--perf-min-mb") == 0 && i + 1 < argc)
{
config.perfMinMB = std::atoi(argv[++i]);
Expand DownExpand Up@@ -261,6 +279,16 @@ static int ValidateAndDiscover(TestConfig& config)
std::cout << "Discovered " << config.discoveredFiles.size() << " .zst file(s) at '"
<< config.contentPath << "'.\n";

if (config.gpuVendorId != 0)
{
std::cout << "GPU vendor selection enabled: 0x" << std::hex
<< config.gpuVendorId << std::dec << ".\n";
}
else
{
std::cout << "GPU vendor selection not configured; demo default selection will be used.\n";
}

if (config.logDir.empty())
{
config.logDir = std::filesystem::current_path().string();
Expand Down
139 changes: 139 additions & 0 deletions zstd/zstdgpu_ci_tests/zstdgpu_ci_tests.cpp
Original file line numberDiff line numberDiff line change
Expand Up@@ -34,15 +34,75 @@
#include "zstdgpu_ci_tests.h"
#include "zstd_frame_size.h"
#include <gtest/gtest.h>
#include <algorithm>
#include <array>
#include <charconv>
#include <filesystem>
#include <fstream>
#include <iostream>
#include <iterator>
#include <sstream>
#include <system_error>
#include <thread>
#include <unordered_set>
#include <Windows.h>

bool ParseGpuVendorId(std::string_view value, uint32_t& vendorId, std::string& error)
{
vendorId = 0;
error.clear();

if (value.size() >= 2 && value[0] == '0' && (value[1] == 'x' || value[1] == 'X'))
{
value.remove_prefix(2);
}
if (value.empty())
{
error = "GPU vendor ID is empty";
return false;
}

uint32_t parsed = 0;
const auto result = std::from_chars(value.data(), value.data() + value.size(), parsed, 16);
if (result.ec == std::errc::result_out_of_range)
{
error = "GPU vendor ID is outside the 32-bit range";
return false;
}
if (result.ec != std::errc{} || result.ptr != value.data() + value.size())
{
error = "GPU vendor ID must contain only hexadecimal digits";
return false;
}
if (parsed == 0)
{
error = "GPU vendor ID must be nonzero";
return false;
}

vendorId = parsed;
return true;
}

void AppendGpuVendorArgs(std::vector<std::string>& args, uint32_t vendorId)
{
if (vendorId == 0)
{
return;
}

static constexpr char digits[] = "0123456789abcdef";
char buffer[8]{};
size_t index = sizeof(buffer);
for (uint32_t remaining = vendorId; remaining != 0; remaining >>= 4)
{
buffer[--index] = digits[remaining & 0xF];
}

args.push_back("--gpu-ven-id");
args.emplace_back(buffer + index, sizeof(buffer) - index);
}

// Internal types + forward declarations
//
// These are used only inside this translation unit; keeping them out of the
Expand DownExpand Up@@ -747,6 +807,7 @@ std::vector<std::string> BuildCorrectnessArgs(
args.push_back("--idx-max");
args.push_back(std::to_string(g_testConfig.idxMax));
}
AppendGpuVendorArgs(args, g_testConfig.gpuVendorId);
for (const auto& flag : scenarioFlags)
{
args.push_back(flag);
Expand DownExpand Up@@ -781,11 +842,89 @@ std::vector<std::string> BuildPerformanceArgs(
args.push_back("--idx-max");
args.push_back(std::to_string(g_testConfig.idxMax));
}
AppendGpuVendorArgs(args, g_testConfig.gpuVendorId);
for (const auto& flag : extraFlags)
{
args.push_back(flag);
}
return args;
}

TEST(GpuVendorArgsTests, ParsesHexadecimalVendorId)
{
uint32_t vendorId = 0;
std::string error;
EXPECT_TRUE(ParseGpuVendorId("10de", vendorId, error));
EXPECT_EQ(vendorId, 0x10deu);
EXPECT_TRUE(error.empty());

EXPECT_TRUE(ParseGpuVendorId("0X10DE", vendorId, error));
EXPECT_EQ(vendorId, 0x10deu);
}

TEST(GpuVendorArgsTests, RejectsMalformedZeroAndOverflowValues)
{
for (const std::string value : {"", "0", "10de-tail", "100000000"})
{
uint32_t vendorId = 123;
std::string error;
EXPECT_FALSE(ParseGpuVendorId(value, vendorId, error)) << value;
EXPECT_EQ(vendorId, 0u) << value;
EXPECT_FALSE(error.empty()) << value;
}
}

TEST(GpuVendorArgsTests, OmitsUnsetVendorId)
{
std::vector<std::string> args{"--chk-gpu"};
AppendGpuVendorArgs(args, 0);
EXPECT_EQ(args, std::vector<std::string>({"--chk-gpu"}));
}

TEST(GpuVendorArgsTests, AppendsNormalizedVendorId)
{
std::vector<std::string> args{"--chk-gpu"};
AppendGpuVendorArgs(args, 0x10de);
EXPECT_EQ(args, std::vector<std::string>({"--chk-gpu", "--gpu-ven-id", "10de"}));
}

TEST(GpuVendorForwardingTests, CorrectnessArgsOmitUnsetVendor)
{
const uint32_t originalVendorId = g_testConfig.gpuVendorId;
g_testConfig.gpuVendorId = 0;

const auto args = BuildCorrectnessArgs("content.zst", {});

g_testConfig.gpuVendorId = originalVendorId;
EXPECT_EQ(std::find(args.begin(), args.end(), "--gpu-ven-id"), args.end());
}

TEST(GpuVendorForwardingTests, CorrectnessArgsIncludeConfiguredVendor)
{
const uint32_t originalVendorId = g_testConfig.gpuVendorId;
g_testConfig.gpuVendorId = 0x10de;

const auto args = BuildCorrectnessArgs("content.zst", {});

g_testConfig.gpuVendorId = originalVendorId;
const auto option = std::find(args.begin(), args.end(), "--gpu-ven-id");
ASSERT_NE(option, args.end());
ASSERT_NE(std::next(option), args.end());
EXPECT_EQ(*std::next(option), "10de");
}

TEST(GpuVendorForwardingTests, PerformanceArgsIncludeConfiguredVendor)
{
const uint32_t originalVendorId = g_testConfig.gpuVendorId;
g_testConfig.gpuVendorId = 0x10de;

const auto args = BuildPerformanceArgs("content.zst", 1, 2, "results.csv", {});

g_testConfig.gpuVendorId = originalVendorId;
const auto option = std::find(args.begin(), args.end(), "--gpu-ven-id");
ASSERT_NE(option, args.end());
ASSERT_NE(std::next(option), args.end());
EXPECT_EQ(*std::next(option), "10de");
}

} // namespace
11 changes: 11 additions & 0 deletions zstd/zstdgpu_ci_tests/zstdgpu_ci_tests.h
Original file line numberDiff line numberDiff line change
Expand Up@@ -15,7 +15,9 @@

#pragma once

#include <cstdint>
#include <string>
#include <string_view>
#include <vector>

#include "adversarial_manifest.h"
Expand All@@ -31,6 +33,7 @@ struct TestConfig
std::string adversarialManifestPath; // Optional path to adversarial_manifest.json (--adversarial-manifest)
std::string gpuName; // Adapter name of the machine under test (--gpu-name). Used only for the
// manifest's scenario skips.
uint32_t gpuVendorId = 0; // PCI vendor ID forwarded to zstdgpu_demo. 0 = unset.
int runCount = 40; // Number of iterations for performance tests
int timeoutSeconds = 0; // Max seconds before killing a demo process (0 = no timeout)
int perfMinMB = 4; // Min .zst size (MB) required for perf tests. Smaller files skip perf (individually-compressed textures are not representative).
Expand All@@ -55,6 +58,14 @@ struct TestConfig
// from test bodies.
extern TestConfig g_testConfig;

// Parses a nonzero PCI vendor ID written in hexadecimal. An optional 0x prefix
// is accepted. On failure, returns false and describes the invalid value.
bool ParseGpuVendorId(std::string_view value, uint32_t& vendorId, std::string& error);

// Appends the zstdgpu_demo adapter selector when an explicit vendor was set.
// A zero ID means "use the demo's normal adapter selection".
void AppendGpuVendorArgs(std::vector<std::string>& args, uint32_t vendorId);

// File discovery — scans a directory for *.zst files. Returns sorted full paths.
// Called by main() during startup.
std::vector<std::string> DiscoverZstFiles(const std::string& contentPath);