921c29cb22
v7.1 — B-Rep 深度攻坚 (对标 Parasolid 95%): - advanced_healing: auto_heal_pipeline, face_splitting/merging, topology_optimization - watertight_verification, tolerance_analysis, tolerance diagnostic report - sheet_metal: unfold_sheet_metal (K-Factor/BFS), bend_deduction_table, create_flange - direct_modeling enhanced: draft_face_advanced (hinge), scale_body (non-uniform), mirror_body - 32 tests (17 healing + 15 sheet metal), syntax-check passed v7.2 — Class-A 曲面攻坚 (对标 CGM 95%): - class_a_surfacing: g3_blend (4-row CP), curvature_matching (Levenberg-Marquardt) - highlight_lines, reflection_lines, iso_photes, surface_diagnosis, shape_modification - advanced_intersection: robust_ssi (3-stage: AABB+subdivision→Newton 1e-12→singularity) - curve_surface_intersection, self_intersection_detection (BVH) - 30 tests (18 class-A + 12 intersection), zero compile errors v7.3 — CAM 深化 + 性能优化: - cam_advanced: adaptive_clearing, trochoidal_milling, rest_machining, pencil_tracing - tool_holder_collision_check, toolpath_optimization, feed_rate_optimization - performance_tuning: parallel_task_graph (DAG+Kahn), work_stealing_scheduler - memory_pool_integration, cache_optimization_hints, profile_guided_layout - Fixed BrepModel API compatibility (body.bounds()/to_mesh() instead of .faces()) - 20 tests 12 files, ~5200 lines, 82 tests
261 lines
7.7 KiB
C++
261 lines
7.7 KiB
C++
#include <gtest/gtest.h>
|
||
#include "vde/core/performance_tuning.h"
|
||
#include "vde/core/point.h"
|
||
#include <vector>
|
||
#include <string>
|
||
#include <thread>
|
||
#include <atomic>
|
||
#include <chrono>
|
||
|
||
using namespace vde::core;
|
||
|
||
// ===========================================================================
|
||
// parallel_task_graph 测试
|
||
// ===========================================================================
|
||
|
||
TEST(PerformanceTuningTest, ParallelTaskGraph_BasicExecution) {
|
||
std::atomic<int> counter{0};
|
||
|
||
std::vector<TaskNode> tasks = {
|
||
{0, "task0", [&]{ counter++; }, {}},
|
||
{1, "task1", [&]{ counter++; }, {0}}, // 依赖 task0
|
||
{2, "task2", [&]{ counter++; }, {0}}, // 依赖 task0
|
||
{3, "task3", [&]{ counter++; }, {1,2}}, // 依赖 task1, task2
|
||
};
|
||
|
||
parallel_task_graph(tasks, 2);
|
||
|
||
EXPECT_EQ(counter.load(), 4);
|
||
}
|
||
|
||
TEST(PerformanceTuningTest, ParallelTaskGraph_DiamondDependency) {
|
||
std::vector<int> execution_order;
|
||
std::mutex mtx;
|
||
|
||
auto make_task = [&](int id, std::vector<int> deps) -> TaskNode {
|
||
return {id, "task" + std::to_string(id),
|
||
[&, id]{
|
||
std::lock_guard<std::mutex> lk(mtx);
|
||
execution_order.push_back(id);
|
||
}, deps};
|
||
};
|
||
|
||
std::vector<TaskNode> tasks = {
|
||
make_task(0, {}),
|
||
make_task(1, {0}),
|
||
make_task(2, {0}),
|
||
make_task(3, {1, 2}),
|
||
make_task(4, {3}),
|
||
};
|
||
|
||
parallel_task_graph(tasks, 4);
|
||
|
||
ASSERT_EQ(execution_order.size(), 5u);
|
||
// task0 must be first
|
||
EXPECT_EQ(execution_order[0], 0);
|
||
// task4 must be last
|
||
EXPECT_EQ(execution_order[4], 4);
|
||
}
|
||
|
||
TEST(PerformanceTuningTest, ParallelTaskGraph_CycleDetection) {
|
||
std::vector<TaskNode> tasks = {
|
||
{0, "task0", []{}, {1}}, // 依赖 task1
|
||
{1, "task1", []{}, {0}}, // 依赖 task0 → 循环
|
||
};
|
||
|
||
EXPECT_THROW({
|
||
parallel_task_graph(tasks, 2);
|
||
}, std::runtime_error);
|
||
}
|
||
|
||
TEST(PerformanceTuningTest, ParallelTaskGraph_EmptyTasks) {
|
||
std::vector<TaskNode> empty;
|
||
EXPECT_NO_THROW(parallel_task_graph(empty, 1));
|
||
}
|
||
|
||
// ===========================================================================
|
||
// WorkStealingScheduler 测试
|
||
// ===========================================================================
|
||
|
||
TEST(PerformanceTuningTest, WorkStealingScheduler_BasicSubmit) {
|
||
auto& scheduler = work_stealing_scheduler();
|
||
std::atomic<int> counter{0};
|
||
|
||
for (int i = 0; i < 100; ++i) {
|
||
scheduler.submit([&]{ counter++; });
|
||
}
|
||
|
||
scheduler.wait_all();
|
||
EXPECT_EQ(counter.load(), 100);
|
||
}
|
||
|
||
TEST(PerformanceTuningTest, WorkStealingScheduler_PriorityTasks) {
|
||
auto& scheduler = work_stealing_scheduler();
|
||
std::vector<int> completion_order;
|
||
std::mutex mtx;
|
||
|
||
// 提交低优先级任务
|
||
scheduler.submit([&]{
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||
std::lock_guard<std::mutex> lk(mtx);
|
||
completion_order.push_back(0);
|
||
}, 0);
|
||
|
||
// 提交高优先级任务(应优先执行)
|
||
scheduler.submit([&]{
|
||
std::lock_guard<std::mutex> lk(mtx);
|
||
completion_order.push_back(1);
|
||
}, 10);
|
||
|
||
scheduler.wait_all();
|
||
EXPECT_EQ(completion_order.size(), 2u);
|
||
EXPECT_EQ(completion_order[0], 1) << "High priority should complete first";
|
||
}
|
||
|
||
// ===========================================================================
|
||
// MemoryPoolIntegration 测试
|
||
// ===========================================================================
|
||
|
||
TEST(PerformanceTuningTest, MemoryPool_AllocateDeallocate) {
|
||
auto& pool = memory_pool_integration();
|
||
pool.reset();
|
||
|
||
auto* pt = pool.allocate_point3d();
|
||
ASSERT_NE(pt, nullptr);
|
||
*pt = Point3D(1, 2, 3);
|
||
EXPECT_DOUBLE_EQ(pt->x(), 1.0);
|
||
|
||
pool.deallocate_point3d(pt);
|
||
|
||
auto stats = pool.stats();
|
||
EXPECT_GT(stats.total_allocations + stats.cache_hits, 0u);
|
||
pool.reset();
|
||
}
|
||
|
||
TEST(PerformanceTuningTest, MemoryPool_Reuse) {
|
||
auto& pool = memory_pool_integration();
|
||
pool.reset();
|
||
pool.warm_up(10);
|
||
|
||
auto* pt1 = pool.allocate_point3d();
|
||
auto* pt2 = pool.allocate_point3d();
|
||
|
||
EXPECT_NE(pt1, pt2) << "Should get different pointers";
|
||
|
||
pool.deallocate_point3d(pt1);
|
||
pool.deallocate_point3d(pt2);
|
||
|
||
auto* pt3 = pool.allocate_point3d();
|
||
// pt3 很可能来自池(pt1 或 pt2)
|
||
EXPECT_TRUE(pt3 == pt1 || pt3 == pt2) << "Should reuse from pool";
|
||
|
||
pool.reset();
|
||
}
|
||
|
||
TEST(PerformanceTuningTest, MemoryPool_GenericAllocate) {
|
||
auto& pool = memory_pool_integration();
|
||
pool.reset();
|
||
pool.set_pool_size(64);
|
||
|
||
void* buf = pool.allocate(128);
|
||
ASSERT_NE(buf, nullptr);
|
||
|
||
pool.deallocate(buf, 128);
|
||
|
||
auto stats = pool.stats();
|
||
EXPECT_GE(stats.total_allocations, 1u);
|
||
pool.reset();
|
||
}
|
||
|
||
// ===========================================================================
|
||
// cache_optimization_hints 测试
|
||
// ===========================================================================
|
||
|
||
TEST(PerformanceTuningTest, CacheOptimizationHints_ReturnsValid) {
|
||
auto hints = cache_optimization_hints();
|
||
|
||
EXPECT_GT(hints.l1_cache_size, 0u);
|
||
EXPECT_GT(hints.l2_cache_size, 0u);
|
||
EXPECT_GT(hints.cache_line_size, 0u);
|
||
EXPECT_EQ(hints.cache_line_size, 64u) << "Expected 64-byte cache line";
|
||
EXPECT_GE(hints.numa_node_count, 1);
|
||
}
|
||
|
||
TEST(PerformanceTuningTest, Prefetch_Compiles) {
|
||
int value = 42;
|
||
// 预取不应崩溃
|
||
EXPECT_NO_THROW(prefetch(&value));
|
||
EXPECT_NO_THROW(prefetch_write(&value));
|
||
}
|
||
|
||
TEST(PerformanceTuningTest, PaddedAtomic_Alignment) {
|
||
PaddedAtomic<> padded;
|
||
padded.value = 42;
|
||
EXPECT_EQ(padded.value.load(), 42);
|
||
// 验证对齐
|
||
EXPECT_EQ(sizeof(padded), CACHE_LINE_SIZE);
|
||
EXPECT_EQ(alignof(decltype(padded)), CACHE_LINE_SIZE);
|
||
}
|
||
|
||
// ===========================================================================
|
||
// profile_guided_layout 测试
|
||
// ===========================================================================
|
||
|
||
TEST(PerformanceTuningTest, ProfileGuidedLayout_HotColdSplit) {
|
||
std::vector<AccessRecord> records = {
|
||
{"field_a", 1000, 50, 0.0},
|
||
{"field_b", 100, 5, 0.0},
|
||
{"field_c", 10, 1, 0.0},
|
||
};
|
||
|
||
auto plan = profile_guided_layout(records, "TestStruct");
|
||
|
||
EXPECT_GT(plan.hot_fields.size(), 0u);
|
||
EXPECT_GT(plan.cold_fields.size(), 0u);
|
||
EXPECT_GT(plan.estimated_improvement, 0.0)
|
||
<< "Should estimate performance improvement";
|
||
}
|
||
|
||
TEST(PerformanceTuningTest, ProfileGuidedLayout_EmptyRecords) {
|
||
std::vector<AccessRecord> empty;
|
||
auto plan = profile_guided_layout(empty, "Empty");
|
||
EXPECT_EQ(plan.hot_fields.size(), 0u);
|
||
EXPECT_EQ(plan.cold_fields.size(), 0u);
|
||
}
|
||
|
||
// ===========================================================================
|
||
// Point3DSoA 测试
|
||
// ===========================================================================
|
||
|
||
TEST(PerformanceTuningTest, Point3DSoA_ConversionRoundtrip) {
|
||
std::vector<Point3D> original = {
|
||
Point3D(1, 2, 3),
|
||
Point3D(4, 5, 6),
|
||
Point3D(7, 8, 9),
|
||
Point3D(10, 11, 12),
|
||
};
|
||
|
||
auto soa = Point3DSoA::from_aos(original);
|
||
EXPECT_EQ(soa.size(), 4u);
|
||
EXPECT_DOUBLE_EQ(soa.x[0], 1.0);
|
||
EXPECT_DOUBLE_EQ(soa.y[1], 5.0);
|
||
EXPECT_DOUBLE_EQ(soa.z[2], 9.0);
|
||
|
||
auto restored = soa.to_aos();
|
||
EXPECT_EQ(restored.size(), 4u);
|
||
for (size_t i = 0; i < original.size(); ++i) {
|
||
EXPECT_DOUBLE_EQ(restored[i].x(), original[i].x());
|
||
EXPECT_DOUBLE_EQ(restored[i].y(), original[i].y());
|
||
EXPECT_DOUBLE_EQ(restored[i].z(), original[i].z());
|
||
}
|
||
}
|
||
|
||
TEST(PerformanceTuningTest, Point3DSoA_Empty) {
|
||
std::vector<Point3D> empty;
|
||
auto soa = Point3DSoA::from_aos(empty);
|
||
EXPECT_EQ(soa.size(), 0u);
|
||
|
||
auto restored = soa.to_aos();
|
||
EXPECT_EQ(restored.size(), 0u);
|
||
}
|