7b76689ea1
v11.1 — Test Infrastructure (14 files): - .github/workflows/ci.yml: Ubuntu 22.04 + GCC 11, auto ctest on push - tests/regression/: degenerate geometry, extreme coords, thin wall, large models - tests/fuzz/: SDF random CSG, random booleans, format round-trip, continuous mode - tests/benchmark/: boolean perf, MC perf, IO perf benchmarks - docs/benchmark-report.md: benchmark template v11.2 — Code Quality + Docs: - .clang-tidy: 15 check categories, 22 exclusions - .github/workflows/static-analysis.yml: clang-tidy CI scan - CODEOWNERS, SECURITY.md - docs: API overview, testing guide, contributing guide - README: module capability overview table Pending: binary formats + curve projection (retrying)
194 lines
6.2 KiB
C++
194 lines
6.2 KiB
C++
/// @file bench_boolean_perf.cpp
|
|
/// @brief 布尔性能基准测试
|
|
///
|
|
/// 测试不同面数模型的 union / intersect / difference 性能。
|
|
/// 结果输出到 stdout,由 CI 汇总后写入 docs/benchmark-report.md
|
|
|
|
#include <gtest/gtest.h>
|
|
#include "vde/brep/brep.h"
|
|
#include "vde/brep/modeling.h"
|
|
#include "vde/brep/brep_boolean.h"
|
|
#include <chrono>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include <vector>
|
|
#include <string>
|
|
|
|
using namespace vde::brep;
|
|
using namespace std::chrono;
|
|
|
|
// ── 计时辅助 ──
|
|
|
|
struct BenchmarkResult {
|
|
std::string name;
|
|
double time_ms;
|
|
size_t faces_a;
|
|
size_t faces_b;
|
|
size_t faces_result;
|
|
};
|
|
|
|
static std::vector<BenchmarkResult> g_results;
|
|
|
|
static double measure_ms(std::function<void()> fn, int iterations = 3) {
|
|
double best = 1e18;
|
|
for (int i = 0; i < iterations; ++i) {
|
|
auto start = high_resolution_clock::now();
|
|
fn();
|
|
auto end = high_resolution_clock::now();
|
|
double ms = duration_cast<microseconds>(end - start).count() / 1000.0;
|
|
if (ms < best) best = ms;
|
|
}
|
|
return best;
|
|
}
|
|
|
|
static void record(const std::string& name, double ms,
|
|
size_t fa, size_t fb, size_t fr) {
|
|
g_results.push_back({name, ms, fa, fb, fr});
|
|
std::cout << " [BENCH] " << std::left << std::setw(40) << name
|
|
<< std::right << std::setw(10) << std::fixed << std::setprecision(2)
|
|
<< ms << " ms"
|
|
<< " (faces: " << fa << " + " << fb << " → " << fr << ")"
|
|
<< std::endl;
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════
|
|
// 布尔并集基准
|
|
// ═══════════════════════════════════════════════
|
|
|
|
TEST(BenchmarkBoolean, Union_SmallModels) {
|
|
// 小面数:两个低分辨率球体
|
|
auto a = make_sphere(1.0, 8, 4);
|
|
auto b = make_sphere(0.8, 8, 4);
|
|
|
|
double ms = measure_ms([&]() {
|
|
auto u = brep_union(a, b);
|
|
(void)u.num_faces();
|
|
}, 5);
|
|
|
|
auto result = brep_union(a, b);
|
|
record("Boolean.Union/Small", ms, a.num_faces(), b.num_faces(), result.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(BenchmarkBoolean, Union_MediumModels) {
|
|
// 中面数:两个中等细分球体
|
|
auto a = make_sphere(2.0, 16, 8);
|
|
auto b = make_sphere(1.5, 16, 8);
|
|
|
|
double ms = measure_ms([&]() {
|
|
auto u = brep_union(a, b);
|
|
(void)u.num_faces();
|
|
}, 3);
|
|
|
|
auto result = brep_union(a, b);
|
|
record("Boolean.Union/Medium", ms, a.num_faces(), b.num_faces(), result.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(BenchmarkBoolean, Union_LargeModels) {
|
|
// 大面数:两个高细分球体
|
|
auto a = make_sphere(3.0, 24, 16);
|
|
auto b = make_sphere(2.5, 24, 16);
|
|
|
|
double ms = measure_ms([&]() {
|
|
auto u = brep_union(a, b);
|
|
(void)u.num_faces();
|
|
}, 3);
|
|
|
|
auto result = brep_union(a, b);
|
|
record("Boolean.Union/Large", ms, a.num_faces(), b.num_faces(), result.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════
|
|
// 布尔交集基准
|
|
// ═══════════════════════════════════════════════
|
|
|
|
TEST(BenchmarkBoolean, Intersection_Small) {
|
|
auto a = make_sphere(1.0, 8, 4);
|
|
auto b = make_box(1.2, 1.2, 1.2);
|
|
|
|
double ms = measure_ms([&]() {
|
|
auto i = brep_intersection(a, b);
|
|
(void)i.num_faces();
|
|
}, 5);
|
|
|
|
auto result = brep_intersection(a, b);
|
|
record("Boolean.Intersection/Small", ms, a.num_faces(), b.num_faces(), result.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(BenchmarkBoolean, Intersection_Medium) {
|
|
auto a = make_sphere(2.0, 16, 8);
|
|
auto b = make_cylinder(1.5, 4.0, 32);
|
|
|
|
double ms = measure_ms([&]() {
|
|
auto i = brep_intersection(a, b);
|
|
(void)i.num_faces();
|
|
}, 3);
|
|
|
|
auto result = brep_intersection(a, b);
|
|
record("Boolean.Intersection/Medium", ms, a.num_faces(), b.num_faces(), result.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(BenchmarkBoolean, Intersection_Large) {
|
|
auto a = make_sphere(3.0, 24, 16);
|
|
auto b = make_cylinder(2.0, 6.0, 64);
|
|
|
|
double ms = measure_ms([&]() {
|
|
auto i = brep_intersection(a, b);
|
|
(void)i.num_faces();
|
|
}, 3);
|
|
|
|
auto result = brep_intersection(a, b);
|
|
record("Boolean.Intersection/Large", ms, a.num_faces(), b.num_faces(), result.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════
|
|
// 布尔差集基准
|
|
// ═══════════════════════════════════════════════
|
|
|
|
TEST(BenchmarkBoolean, Difference_Small) {
|
|
auto a = make_box(2.0, 2.0, 2.0);
|
|
auto b = make_sphere(0.8, 8, 4);
|
|
|
|
double ms = measure_ms([&]() {
|
|
auto d = brep_difference(a, b);
|
|
(void)d.num_faces();
|
|
}, 5);
|
|
|
|
auto result = brep_difference(a, b);
|
|
record("Boolean.Difference/Small", ms, a.num_faces(), b.num_faces(), result.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(BenchmarkBoolean, Difference_Medium) {
|
|
auto a = make_box(4.0, 4.0, 4.0);
|
|
auto b = make_sphere(1.5, 16, 8);
|
|
|
|
double ms = measure_ms([&]() {
|
|
auto d = brep_difference(a, b);
|
|
(void)d.num_faces();
|
|
}, 3);
|
|
|
|
auto result = brep_difference(a, b);
|
|
record("Boolean.Difference/Medium", ms, a.num_faces(), b.num_faces(), result.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|
|
|
|
TEST(BenchmarkBoolean, Difference_Large) {
|
|
auto a = make_box(6.0, 6.0, 6.0);
|
|
auto b = make_sphere(2.0, 24, 16);
|
|
|
|
double ms = measure_ms([&]() {
|
|
auto d = brep_difference(a, b);
|
|
(void)d.num_faces();
|
|
}, 3);
|
|
|
|
auto result = brep_difference(a, b);
|
|
record("Boolean.Difference/Large", ms, a.num_faces(), b.num_faces(), result.num_faces());
|
|
EXPECT_TRUE(result.is_valid());
|
|
}
|