改进
This commit is contained in:
@@ -93,9 +93,9 @@ TEST(render_dag_test, analysis_derives_wait_critical_path_and_parallel_overlap_f
|
||||
EXPECT_EQ(analysis.nodes[2].scheduler_wait_ns, 10u);
|
||||
EXPECT_EQ(analysis.total_render_duration_ns, 100u);
|
||||
EXPECT_EQ(analysis.total_work_duration_ns, 100u);
|
||||
EXPECT_EQ(analysis.peak_parallelism, 2u);
|
||||
EXPECT_DOUBLE_EQ(analysis.average_parallelism, 1.0);
|
||||
EXPECT_EQ(analysis.parallel_overlap_ns, 30u);
|
||||
EXPECT_EQ(analysis.scheduler_peak_parallelism, 2u);
|
||||
EXPECT_DOUBLE_EQ(analysis.average_cpu_concurrency, 1.0);
|
||||
EXPECT_EQ(analysis.scheduler_parallel_overlap_ns, 30u);
|
||||
EXPECT_EQ(analysis.critical_path, (std::vector<Render_Node_Id>{100, 102}));
|
||||
EXPECT_EQ(analysis.critical_path_duration_ns, 70u);
|
||||
EXPECT_DOUBLE_EQ(analysis.nodes[0].work_contribution, 0.4);
|
||||
@@ -278,3 +278,75 @@ TEST(render_dag_test, capture_repository_retains_only_recent_completed_sessions)
|
||||
EXPECT_FALSE(repository.session(1));
|
||||
EXPECT_TRUE(repository.session(20));
|
||||
}
|
||||
|
||||
TEST(render_dag_test, render_domain_phases_are_counted_as_cpu_work_not_gpu_wall_time) {
|
||||
Render_Graph_Builder builder;
|
||||
builder.emplace(5001, 50, "3D Render", Render_Node_Kind::render);
|
||||
Render_Plan_History history;
|
||||
const auto plan = history.publish(std::move(builder).finish());
|
||||
|
||||
Frame_Snapshot snapshot;
|
||||
snapshot.frame_id = 11;
|
||||
snapshot.render_plan_version = plan->version;
|
||||
snapshot.render_start_ns = 10'000;
|
||||
snapshot.render_end_ns = 10'500;
|
||||
snapshot.node_executions.resize(1);
|
||||
auto& execution = snapshot.node_executions.front();
|
||||
execution.node_id = 5001;
|
||||
execution.ready_time_ns = 10'000;
|
||||
execution.start_time_ns = 10'000;
|
||||
execution.cpu_end_time_ns = 10'020;
|
||||
execution.external_start_time_ns = 10'020;
|
||||
execution.external_end_time_ns = 10'500;
|
||||
execution.end_time_ns = 10'500;
|
||||
execution.worker_id = 2;
|
||||
execution.status = Node_Execution_Status::complete;
|
||||
execution.metrics.set(Node_Metric_Kind::queue_wait_ns, 30);
|
||||
execution.metrics.set(Node_Metric_Kind::apply_duration_ns, 10);
|
||||
execution.metrics.set(Node_Metric_Kind::plan_emit_duration_ns, 20);
|
||||
execution.metrics.set(Node_Metric_Kind::backend_execute_duration_ns, 30);
|
||||
execution.metrics.set(Node_Metric_Kind::submit_duration_ns, 40);
|
||||
execution.metrics.set(Node_Metric_Kind::readback_duration_ns, 50);
|
||||
execution.metrics.set(Node_Metric_Kind::gpu_fence_wait_ns, 300);
|
||||
execution.metrics.set(Node_Metric_Kind::gpu_total_duration_ns, 250);
|
||||
|
||||
const auto analysis = analyze_frame(*plan, snapshot);
|
||||
ASSERT_EQ(analysis.nodes.size(), 1U);
|
||||
EXPECT_EQ(analysis.scheduler_work_duration_ns, 20U);
|
||||
EXPECT_EQ(analysis.render_domain_work_duration_ns, 150U);
|
||||
EXPECT_EQ(analysis.total_work_duration_ns, 170U);
|
||||
EXPECT_EQ(analysis.total_external_duration_ns, 480U);
|
||||
EXPECT_EQ(analysis.total_render_domain_queue_wait_ns, 30U);
|
||||
EXPECT_EQ(analysis.total_gpu_completion_wait_ns, 300U);
|
||||
EXPECT_EQ(analysis.total_gpu_execution_duration_ns, 250U);
|
||||
EXPECT_DOUBLE_EQ(analysis.scheduler_average_parallelism, 0.04);
|
||||
EXPECT_DOUBLE_EQ(analysis.render_domain_utilization, 0.3);
|
||||
EXPECT_DOUBLE_EQ(analysis.average_cpu_concurrency, 0.34);
|
||||
|
||||
const auto statistics = analyze_node_statistics(
|
||||
std::span<const Frame_Analysis>(&analysis, 1));
|
||||
ASSERT_EQ(statistics.size(), 1U);
|
||||
EXPECT_DOUBLE_EQ(statistics.front().average_render_domain_cpu_ns, 150.0);
|
||||
EXPECT_DOUBLE_EQ(statistics.front().average_total_cpu_work_ns, 170.0);
|
||||
EXPECT_DOUBLE_EQ(statistics.front().average_gpu_execution_ns, 250.0);
|
||||
}
|
||||
|
||||
TEST(render_dag_test, graph_cycle_is_validated_once_when_builder_finishes) {
|
||||
Render_Graph_Builder builder;
|
||||
const auto a = builder.emplace(6001, 60, "A", Render_Node_Kind::prepare);
|
||||
const auto b = builder.emplace(6002, 60, "B", Render_Node_Kind::prepare);
|
||||
const auto c = builder.emplace(6003, 60, "C", Render_Node_Kind::prepare);
|
||||
builder.precede(a, b);
|
||||
builder.precede(b, c);
|
||||
builder.precede(c, a);
|
||||
EXPECT_THROW(static_cast<void>(std::move(builder).finish()), std::invalid_argument);
|
||||
}
|
||||
|
||||
TEST(render_dag_test, plan_history_rejects_invalid_manually_constructed_graphs) {
|
||||
Render_Graph graph;
|
||||
graph.nodes.push_back({7001, 70, "A", Render_Node_Kind::prepare, 0});
|
||||
graph.edges.push_back({7001, 7999});
|
||||
Render_Plan_History history;
|
||||
EXPECT_THROW(static_cast<void>(history.publish(std::move(graph))),
|
||||
std::invalid_argument);
|
||||
}
|
||||
|
||||
@@ -193,3 +193,132 @@ TEST(render_graph_runtime_test,
|
||||
EXPECT_TRUE(source.complete());
|
||||
execution.join();
|
||||
}
|
||||
|
||||
TEST(external_operation_test, cancellation_is_terminal_and_delivered_once) {
|
||||
External_Operation_Source source;
|
||||
const auto operation = source.operation();
|
||||
EXPECT_TRUE(operation.cancel());
|
||||
EXPECT_FALSE(source.complete());
|
||||
EXPECT_EQ(operation.status(), External_Operation_Status::cancelled);
|
||||
|
||||
int completion_count{};
|
||||
std::exception_ptr completion_error;
|
||||
operation.on_complete([&](std::exception_ptr error) {
|
||||
completion_error = std::move(error);
|
||||
++completion_count;
|
||||
});
|
||||
EXPECT_EQ(completion_count, 1);
|
||||
ASSERT_TRUE(completion_error);
|
||||
EXPECT_THROW(std::rethrow_exception(completion_error),
|
||||
External_Operation_Cancelled);
|
||||
}
|
||||
|
||||
TEST(external_operation_test, deadline_cancels_pending_operation) {
|
||||
External_Operation_Source source;
|
||||
const auto operation = source.operation();
|
||||
std::mutex mutex;
|
||||
std::condition_variable condition;
|
||||
bool completed{};
|
||||
std::exception_ptr completion_error;
|
||||
operation.on_complete([&](std::exception_ptr error) {
|
||||
{
|
||||
std::lock_guard lock(mutex);
|
||||
completion_error = std::move(error);
|
||||
completed = true;
|
||||
}
|
||||
condition.notify_one();
|
||||
});
|
||||
source.set_deadline(External_Operation_Source::Clock::now());
|
||||
{
|
||||
std::unique_lock lock(mutex);
|
||||
ASSERT_TRUE(condition.wait_for(lock, std::chrono::seconds(1),
|
||||
[&] { return completed; }));
|
||||
}
|
||||
ASSERT_TRUE(completion_error);
|
||||
EXPECT_THROW(std::rethrow_exception(completion_error),
|
||||
External_Operation_Deadline_Exceeded);
|
||||
EXPECT_EQ(operation.status(), External_Operation_Status::cancelled);
|
||||
}
|
||||
|
||||
TEST(render_graph_runtime_test,
|
||||
cancellation_releases_external_wait_and_blocks_successor) {
|
||||
const auto plan = two_node_plan();
|
||||
External_Operation_Source source;
|
||||
std::vector<Node_Execution> execution_storage;
|
||||
auto slots = execution_slots(*plan, execution_storage);
|
||||
std::atomic<bool> submit_started{};
|
||||
std::atomic<bool> publish_executed{};
|
||||
std::exception_ptr graph_error;
|
||||
|
||||
renderive::render_graph::detail::Render_Graph_Runtime runtime(*plan);
|
||||
const auto execute_node = [&](std::size_t index, Node_Execution_Metrics*) {
|
||||
if (index == 0) {
|
||||
submit_started.store(true, std::memory_order_release);
|
||||
submit_started.notify_all();
|
||||
return Node_Execution_Result::external(source.operation());
|
||||
}
|
||||
publish_executed.store(true, std::memory_order_release);
|
||||
return Node_Execution_Result::completed();
|
||||
};
|
||||
|
||||
std::thread execution([&] {
|
||||
try {
|
||||
runtime.execute(slots, execute_node);
|
||||
} catch (...) {
|
||||
graph_error = std::current_exception();
|
||||
}
|
||||
});
|
||||
submit_started.wait(false, std::memory_order_acquire);
|
||||
runtime.cancel_pending();
|
||||
execution.join();
|
||||
|
||||
ASSERT_TRUE(graph_error);
|
||||
EXPECT_THROW(std::rethrow_exception(graph_error),
|
||||
External_Operation_Cancelled);
|
||||
EXPECT_FALSE(publish_executed.load(std::memory_order_acquire));
|
||||
EXPECT_EQ(execution_storage[0].status, Node_Execution_Status::cancelled);
|
||||
EXPECT_EQ(source.operation().status(), External_Operation_Status::cancelled);
|
||||
}
|
||||
|
||||
TEST(external_operation_test, completion_commit_is_exclusive_with_cancellation) {
|
||||
External_Operation_Source source;
|
||||
const auto operation = source.operation();
|
||||
std::atomic<int> published{};
|
||||
EXPECT_TRUE(operation.cancel());
|
||||
EXPECT_FALSE(source.complete([&] {
|
||||
published.fetch_add(1, std::memory_order_relaxed);
|
||||
}));
|
||||
EXPECT_EQ(published.load(std::memory_order_relaxed), 0);
|
||||
}
|
||||
|
||||
TEST(external_operation_test, completion_commit_precedes_completion_notification) {
|
||||
External_Operation_Source source;
|
||||
const auto operation = source.operation();
|
||||
std::atomic<int> published{};
|
||||
int observed{};
|
||||
operation.on_complete([&](std::exception_ptr error) {
|
||||
EXPECT_FALSE(error);
|
||||
observed = published.load(std::memory_order_acquire);
|
||||
});
|
||||
|
||||
EXPECT_TRUE(source.complete([&] {
|
||||
published.store(1, std::memory_order_release);
|
||||
EXPECT_EQ(operation.status(), External_Operation_Status::pending);
|
||||
}));
|
||||
EXPECT_EQ(observed, 1);
|
||||
EXPECT_EQ(operation.status(), External_Operation_Status::completed);
|
||||
}
|
||||
|
||||
TEST(external_operation_test, completion_commit_failure_fails_operation) {
|
||||
External_Operation_Source source;
|
||||
const auto operation = source.operation();
|
||||
std::exception_ptr completion_error;
|
||||
operation.on_complete([&](std::exception_ptr error) {
|
||||
completion_error = std::move(error);
|
||||
});
|
||||
|
||||
EXPECT_TRUE(source.complete([] { throw std::runtime_error("publish failed"); }));
|
||||
ASSERT_TRUE(completion_error);
|
||||
EXPECT_THROW(std::rethrow_exception(completion_error), std::runtime_error);
|
||||
EXPECT_EQ(operation.status(), External_Operation_Status::failed);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user