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)
195 lines
5.6 KiB
C++
195 lines
5.6 KiB
C++
/// @file bench_io_perf.cpp
|
|
/// @brief 导入/导出性能基准测试
|
|
///
|
|
/// 测试 IGES/STEP 格式的导出耗时和导入耗时。
|
|
|
|
#include <gtest/gtest.h>
|
|
#include "vde/brep/brep.h"
|
|
#include "vde/brep/modeling.h"
|
|
#include "vde/brep/iges_export.h"
|
|
#include "vde/brep/iges_import.h"
|
|
#include "vde/brep/step_export.h"
|
|
#include "vde/brep/step_import.h"
|
|
#include <chrono>
|
|
#include <iostream>
|
|
#include <iomanip>
|
|
#include <vector>
|
|
|
|
using namespace vde::brep;
|
|
using namespace std::chrono;
|
|
|
|
struct IOBenchmarkResult {
|
|
std::string name;
|
|
double time_ms;
|
|
size_t data_size_bytes;
|
|
};
|
|
|
|
static std::vector<IOBenchmarkResult> g_io_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_io(const std::string& name, double ms, size_t bytes) {
|
|
g_io_results.push_back({name, ms, bytes});
|
|
std::cout << " [BENCH] " << std::left << std::setw(40) << name
|
|
<< std::right << std::setw(10) << std::fixed << std::setprecision(2)
|
|
<< ms << " ms"
|
|
<< " (data=" << bytes << " bytes)"
|
|
<< std::endl;
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════
|
|
// IGES 基准
|
|
// ═══════════════════════════════════════════════
|
|
|
|
TEST(BenchmarkIO, IGES_Export_SingleBody) {
|
|
auto body = make_box(10.0, 5.0, 3.0);
|
|
|
|
std::string result;
|
|
double ms = measure_ms([&]() {
|
|
result = export_iges({body});
|
|
}, 5);
|
|
|
|
EXPECT_FALSE(result.empty());
|
|
record_io("IO.IGES.Export/SingleBox", ms, result.size());
|
|
}
|
|
|
|
TEST(BenchmarkIO, IGES_Export_Sphere) {
|
|
auto body = make_sphere(5.0, 16, 8);
|
|
|
|
std::string result;
|
|
double ms = measure_ms([&]() {
|
|
result = export_iges({body});
|
|
}, 5);
|
|
|
|
EXPECT_FALSE(result.empty());
|
|
record_io("IO.IGES.Export/Sphere16x8", ms, result.size());
|
|
}
|
|
|
|
TEST(BenchmarkIO, IGES_RoundTrip_Box) {
|
|
auto body = make_box(10.0, 5.0, 3.0);
|
|
|
|
// Export
|
|
std::string iges = export_iges({body});
|
|
|
|
// Import benchmark
|
|
double ms = measure_ms([&]() {
|
|
auto loaded = import_iges_from_string(iges);
|
|
(void)loaded.size();
|
|
}, 5);
|
|
|
|
auto loaded = import_iges_from_string(iges);
|
|
EXPECT_FALSE(loaded.empty());
|
|
record_io("IO.IGES.Import/SingleBox", ms, iges.size());
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════
|
|
// STEP 基准
|
|
// ═══════════════════════════════════════════════
|
|
|
|
TEST(BenchmarkIO, STEP_Export_SingleBody) {
|
|
auto body = make_box(10.0, 5.0, 3.0);
|
|
|
|
std::string result;
|
|
double ms = measure_ms([&]() {
|
|
result = export_step({body});
|
|
}, 5);
|
|
|
|
EXPECT_FALSE(result.empty());
|
|
record_io("IO.STEP.Export/SingleBox", ms, result.size());
|
|
}
|
|
|
|
TEST(BenchmarkIO, STEP_Export_Sphere) {
|
|
auto body = make_sphere(5.0, 16, 8);
|
|
|
|
std::string result;
|
|
double ms = measure_ms([&]() {
|
|
result = export_step({body});
|
|
}, 5);
|
|
|
|
EXPECT_FALSE(result.empty());
|
|
record_io("IO.STEP.Export/Sphere16x8", ms, result.size());
|
|
}
|
|
|
|
TEST(BenchmarkIO, STEP_Export_Cylinder) {
|
|
auto body = make_cylinder(3.0, 10.0, 32);
|
|
|
|
std::string result;
|
|
double ms = measure_ms([&]() {
|
|
result = export_step({body});
|
|
}, 5);
|
|
|
|
EXPECT_FALSE(result.empty());
|
|
record_io("IO.STEP.Export/Cylinder32", ms, result.size());
|
|
}
|
|
|
|
TEST(BenchmarkIO, STEP_RoundTrip_Box) {
|
|
auto body = make_box(10.0, 5.0, 3.0);
|
|
std::string step = export_step({body});
|
|
|
|
double ms = measure_ms([&]() {
|
|
auto loaded = import_step_from_string(step);
|
|
(void)loaded.size();
|
|
}, 5);
|
|
|
|
auto loaded = import_step_from_string(step);
|
|
EXPECT_FALSE(loaded.empty());
|
|
record_io("IO.STEP.Import/SingleBox", ms, step.size());
|
|
}
|
|
|
|
// ═══════════════════════════════════════════════
|
|
// 大规模数据IO基准
|
|
// ═══════════════════════════════════════════════
|
|
|
|
TEST(BenchmarkIO, STEP_Export_LargeModel) {
|
|
// 高细分球体 → 大量面 → 大 STEP 文件
|
|
auto body = make_sphere(5.0, 32, 16);
|
|
|
|
std::string result;
|
|
double ms = measure_ms([&]() {
|
|
result = export_step({body});
|
|
}, 3);
|
|
|
|
EXPECT_FALSE(result.empty());
|
|
record_io("IO.STEP.Export/LargeSphere32x16", ms, result.size());
|
|
}
|
|
|
|
TEST(BenchmarkIO, IGES_Export_LargeModel) {
|
|
auto body = make_sphere(5.0, 32, 16);
|
|
|
|
std::string result;
|
|
double ms = measure_ms([&]() {
|
|
result = export_iges({body});
|
|
}, 3);
|
|
|
|
EXPECT_FALSE(result.empty());
|
|
record_io("IO.IGES.Export/LargeSphere32x16", ms, result.size());
|
|
}
|
|
|
|
TEST(BenchmarkIO, MultiBody_Export) {
|
|
// 多体导出
|
|
std::vector<BrepModel> bodies = {
|
|
make_box(3.0, 3.0, 3.0),
|
|
make_sphere(2.0, 12, 6),
|
|
make_cylinder(1.0, 5.0, 16),
|
|
make_box(1.0, 1.0, 10.0),
|
|
};
|
|
|
|
std::string result;
|
|
double ms = measure_ms([&]() {
|
|
result = export_step(bodies);
|
|
}, 3);
|
|
|
|
EXPECT_FALSE(result.empty());
|
|
record_io("IO.STEP.Export/MultiBody(4)", ms, result.size());
|
|
}
|