内核优化

This commit is contained in:
2026-08-10 13:50:17 +08:00
parent 2abdba699d
commit 302b1dcdfd
46 changed files with 1955 additions and 515 deletions
@@ -1,4 +1,11 @@
#include <gtest/gtest.h>
#include <algorithm>
#include <atomic>
#include <functional>
#include <memory>
#include <mutex>
#include <thread>
#include <vector>
#include "renderive/frame_control/Frame_Control.hpp"
struct Flow_Refresh_Test_Frame {
int value{};
@@ -26,3 +33,150 @@ TEST(flow_refresh_strategy_test, returns_empty_lease_when_queue_is_empty) {
EXPECT_FALSE(frame);
EXPECT_EQ(strategy.state().empty_acquire_count, 1);
}
TEST(flow_refresh_strategy_test, accepts_multiple_concurrent_producers_without_losing_frames) {
Flow_Refresh_Test_Strategy strategy;
constexpr int producer_count = 4;
constexpr int frames_per_producer = 100;
std::vector<std::thread> producers;
for (int producer = 0; producer < producer_count; ++producer) {
producers.emplace_back([&strategy, producer] {
for (int index = 0; index < frames_per_producer; ++index) {
auto frame = strategy.acquire_painter();
frame->value = producer * 1000 + index;
}
});
}
for (auto& producer : producers) {
producer.join();
}
EXPECT_EQ(strategy.pending_frame_count(), producer_count * frames_per_producer);
std::vector<int> values;
while (auto frame = strategy.acquire_renderer()) {
values.push_back(frame->value);
}
std::sort(values.begin(), values.end());
EXPECT_EQ(values.size(), producer_count * frames_per_producer);
for (int producer = 0; producer < producer_count; ++producer) {
for (int index = 0; index < frames_per_producer; ++index) {
EXPECT_TRUE(std::binary_search(values.begin(), values.end(), producer * 1000 + index));
}
}
}
TEST(flow_refresh_strategy_test, concurrent_consumer_does_not_underflow_pending_count) {
Flow_Refresh_Test_Strategy strategy;
constexpr int producer_count = 4;
constexpr int frames_per_producer = 200;
constexpr int frame_count = producer_count * frames_per_producer;
std::atomic<int> producers_done{};
std::atomic<int> consumed{};
std::vector<int> values;
values.reserve(frame_count);
std::thread consumer([&] {
while (consumed.load(std::memory_order_acquire) != frame_count) {
auto frame = strategy.acquire_renderer();
if (!frame) {
if (producers_done.load(std::memory_order_acquire) == producer_count && strategy.pending_frame_count() == 0) {
break;
}
std::this_thread::yield();
continue;
}
values.push_back(frame->value);
consumed.fetch_add(1, std::memory_order_release);
}
});
std::vector<std::thread> producers;
for (int producer = 0; producer < producer_count; ++producer) {
producers.emplace_back([&strategy, &producers_done, producer] {
for (int index = 0; index < frames_per_producer; ++index) {
auto frame = strategy.acquire_painter();
frame->value = producer * 1000 + index;
}
producers_done.fetch_add(1, std::memory_order_release);
});
}
for (auto& producer : producers) {
producer.join();
}
consumer.join();
EXPECT_EQ(consumed.load(std::memory_order_acquire), frame_count);
EXPECT_EQ(strategy.pending_frame_count(), 0);
std::sort(values.begin(), values.end());
EXPECT_EQ(values.size(), frame_count);
for (int producer = 0; producer < producer_count; ++producer) {
for (int index = 0; index < frames_per_producer; ++index) {
EXPECT_TRUE(std::binary_search(values.begin(), values.end(), producer * 1000 + index));
}
}
}
struct Flow_Refresh_Reentrant_Observer_Data {
std::function<void(int)> callback;
};
struct Flow_Refresh_Reentrant_Observer {
static constexpr bool enabled = true;
std::shared_ptr<Flow_Refresh_Reentrant_Observer_Data> data{std::make_shared<Flow_Refresh_Reentrant_Observer_Data>()};
template <class Observation>
void observe(const Observation& observation) noexcept {
if (data->callback) {
data->callback(static_cast<int>(observation.event));
}
}
};
using Flow_Refresh_Reentrant_Observer_State = Observer_State<Flow_Refresh_Reentrant_Observer>;
using Flow_Refresh_Reentrant_Strategy = Flow_Refresh_Strategy<Flow_Refresh_Test_Frame, std::mutex, Flow_Refresh_Reentrant_Observer_State>;
TEST(flow_refresh_strategy_test, observer_can_reenter_observer_state_without_deadlock) {
Flow_Refresh_Reentrant_Observer recorder;
auto data = recorder.data;
Flow_Refresh_Reentrant_Strategy strategy{Flow_Refresh_Reentrant_Observer_State(recorder)};
std::atomic<bool> reentered{};
data->callback = [&](int event) {
if (event != static_cast<int>(Flow_Refresh_Reentrant_Strategy::Observation_Event::enqueued) || reentered.exchange(true)) {
return;
}
strategy.on_real_time_data_update({Real_Time_Data_Observation_Event::updated, {nullptr, Real_Time_Data_Retention::latest, 1, 1, 1, 1}});
};
{
auto frame = strategy.acquire_painter();
frame->value = 1;
}
EXPECT_TRUE(reentered.load());
EXPECT_EQ(strategy.state().real_time_data_update_sequence, 1);
}
struct Flow_Refresh_Render_Reentrant_Observer_Data {
std::function<void()> callback;
};
struct Flow_Refresh_Render_Reentrant_Observer {
static constexpr bool enabled = true;
std::shared_ptr<Flow_Refresh_Render_Reentrant_Observer_Data> data{std::make_shared<Flow_Refresh_Render_Reentrant_Observer_Data>()};
template <class Observation>
void observe(const Observation& observation) noexcept {
if (static_cast<int>(observation.event) == 1 && data->callback) {
data->callback();
}
}
};
using Flow_Refresh_Render_Reentrant_Observer_State = Observer_State<Flow_Refresh_Render_Reentrant_Observer>;
using Flow_Refresh_Render_Reentrant_Strategy = Flow_Refresh_Strategy<Flow_Refresh_Test_Frame, std::mutex, Flow_Refresh_Render_Reentrant_Observer_State>;
TEST(flow_refresh_strategy_test, render_observer_reentry_does_not_deadlock_render_mutex) {
Flow_Refresh_Render_Reentrant_Observer recorder;
auto data = recorder.data;
Flow_Refresh_Render_Reentrant_Strategy strategy{Flow_Refresh_Render_Reentrant_Observer_State(recorder)};
for (int value = 1; value <= 2; ++value) {
auto frame = strategy.acquire_painter();
frame->value = value;
}
std::atomic<bool> nested_result{true};
data->callback = [&] {
auto nested = strategy.acquire_renderer();
nested_result.store(static_cast<bool>(nested), std::memory_order_release);
};
{
auto frame = strategy.acquire_renderer();
ASSERT_TRUE(frame);
EXPECT_EQ(frame->value, 1);
}
EXPECT_FALSE(nested_result.load(std::memory_order_acquire));
auto second = strategy.acquire_renderer();
ASSERT_TRUE(second);
EXPECT_EQ(second->value, 2);
}
@@ -1,4 +1,6 @@
#include <gtest/gtest.h>
#include <limits>
#include <stdexcept>
#include "Low_Latency_Strategy_Test_Types.hpp"
TEST(low_latency_strategy_test, publishes_and_renders_latest_frame) {
Low_Latency_Test_Time_Source time_source;
@@ -98,3 +100,27 @@ TEST(low_latency_strategy_test, detects_render_limited_state) {
}
EXPECT_EQ(strategy.state().limit_state, Low_Latency_Test_Strategy::Limit_State::render_limited);
}
TEST(low_latency_strategy_test, rejects_non_finite_frequency_configuration) {
Low_Latency_Test_Time_Source time_source;
Low_Latency_Test_Observer observer;
EXPECT_THROW((Low_Latency_Test_Strategy(Low_Latency_Test_Observer_State(observer, time_source), {std::numeric_limits<double>::quiet_NaN()})), std::invalid_argument);
EXPECT_THROW((Low_Latency_Test_Strategy(Low_Latency_Test_Observer_State(observer, time_source), {std::numeric_limits<double>::infinity()})), std::invalid_argument);
auto strategy = make_low_latency_test_strategy(time_source, observer);
EXPECT_THROW(strategy.set_frequency_hz(std::numeric_limits<double>::quiet_NaN()), std::invalid_argument);
EXPECT_THROW(strategy.set_frequency_hz(std::numeric_limits<double>::infinity()), std::invalid_argument);
}
TEST(low_latency_strategy_test, clamps_extremely_small_frequency_interval_without_overflow) {
Low_Latency_Test_Time_Source time_source;
Low_Latency_Test_Observer observer;
auto strategy = make_low_latency_test_strategy(time_source, observer);
strategy.set_frequency_hz(1e-300);
{
auto frame = strategy.acquire_painter();
frame->value = 1;
}
{
auto frame = strategy.acquire_renderer();
ASSERT_TRUE(frame);
}
EXPECT_EQ(strategy.state().target_interval_ns, std::numeric_limits<std::uint64_t>::max());
}