Files
ViewDesignEngine/tests/core/test_performance.cpp
T

261 lines
7.7 KiB
C++
Raw Normal View History

#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);
}