В продолжение вчерашней темы про очередь из std::function-объектов. С подачи ув.тов.Timur Rakhmatullaev в код были внесены две правки, которые существенно ускорили работу варианта с std::function.
Первая правка заключалась в использовании метода queue::emplace вместо queue::push. Огромное спасибо Тимуру за напоминание об этом методе -- в C++11 расширился API стандартных контейнеров, но я об этом постоянно забываю.
Вторая же правка, которая, по-моему, и привела к существенному ускорению работы, заключалась в том, чтобы хитрым образом забирать значение объекта из головы очереди. Вместо копирования в старом варианте кода сейчас задействован перенос значения объекта (посредством move-конструктора):
void fn_test() { fn_demand_queue_t queue; fn_demand_generator_t generator; generator.on_type_1( queue ); for( size_t i = 0; i != ITERATIONS; ++i ) { fn_demand_t d = std::move(queue.front()); queue.pop(); d(); } } |
Результаты замеров существенно изменились. Так, если под GCC 4.8.2 я получал в старом варианте вот такие результаты:
fn_demands: 31.302s
oo_demands: 21.397s
То в новом варианте получается уже вот так:
fn_demands: 17.039s
oo_demands: 19.798s
Интересный эксперимент получился. И да, сутью эксперимента было посмотреть, какие накладные расходы будет нести средней руки разработчик, если начнет использовать очереди из std::function-объектов.
Под катом обновленный код теста.
#include <iostream> #include <queue> #include <memory> #include <functional> #include <ctime> const size_t ITERATIONS = 100000000; // // Functional demands // typedef std::function< void(void) > fn_demand_t; typedef std::queue< fn_demand_t > fn_demand_queue_t; class fn_demand_generator_t { public : void on_type_1( fn_demand_queue_t & queue ) { queue.emplace( [this, &queue] { this->on_type_2( queue ); } ); } void on_type_2( fn_demand_queue_t & queue ) { queue.emplace( [this, &queue] { this->on_type_3( queue ); } ); } void on_type_3( fn_demand_queue_t & queue ) { queue.emplace( [this, &queue] { this->on_type_1( queue ); } ); } }; void fn_test() { fn_demand_queue_t queue; fn_demand_generator_t generator; generator.on_type_1( queue ); for( size_t i = 0; i != ITERATIONS; ++i ) { fn_demand_t d = std::move(queue.front()); queue.pop(); d(); } } // // Object-oriented demands. // class oo_demand_t { public : virtual ~oo_demand_t() {} virtual void handle() = 0; }; typedef std::shared_ptr< oo_demand_t > oo_demand_shptr_t; typedef std::queue< oo_demand_shptr_t > oo_demand_queue_t; class oo_demand_generator_t { public : void on_type_1( oo_demand_queue_t & queue ); void on_type_2( oo_demand_queue_t & queue ); void on_type_3( oo_demand_queue_t & queue ); }; class oo_demand_type_1 : public oo_demand_t { private : oo_demand_generator_t & m_generator; oo_demand_queue_t & m_queue; public : oo_demand_type_1( oo_demand_generator_t & generator, oo_demand_queue_t & queue ) : m_generator( generator ) , m_queue( queue ) {} virtual void handle() { m_generator.on_type_1( m_queue ); } }; class oo_demand_type_2 : public oo_demand_t { private : oo_demand_generator_t & m_generator; oo_demand_queue_t & m_queue; public : oo_demand_type_2( oo_demand_generator_t & generator, oo_demand_queue_t & queue ) : m_generator( generator ) , m_queue( queue ) {} virtual void handle() { m_generator.on_type_2( m_queue ); } }; class oo_demand_type_3 : public oo_demand_t { private : oo_demand_generator_t & m_generator; oo_demand_queue_t & m_queue; public : oo_demand_type_3( oo_demand_generator_t & generator, oo_demand_queue_t & queue ) : m_generator( generator ) , m_queue( queue ) {} virtual void handle() { m_generator.on_type_3( m_queue ); } }; inline void oo_demand_generator_t::on_type_1( oo_demand_queue_t & queue ) { queue.emplace( std::make_shared< oo_demand_type_2 >( *this, queue ) ); } inline void oo_demand_generator_t::on_type_2( oo_demand_queue_t & queue ) { queue.emplace( std::make_shared< oo_demand_type_3 >( *this, queue ) ); } inline void oo_demand_generator_t::on_type_3( oo_demand_queue_t & queue ) { queue.emplace( std::make_shared< oo_demand_type_1 >( *this, queue ) ); } void oo_test() { oo_demand_queue_t queue; oo_demand_generator_t generator; generator.on_type_1( queue ); for( size_t i = 0; i != ITERATIONS; ++i ) { oo_demand_shptr_t d = std::move(queue.front()); queue.pop(); d->handle(); } } // // Benchmarking // template< class TEST_CASE > void run_and_measure( const char * test_name, TEST_CASE test_case ) { const std::clock_t begin = std::clock(); test_case(); const std::clock_t end = std::clock(); const double duration = std::difftime(end,begin) / CLOCKS_PER_SEC; std::cout << test_name << ": " << duration << "s" << std::endl; } int main() { run_and_measure( "fn_demands", fn_test ); run_and_measure( "oo_demands", oo_test ); } |
Комментариев нет:
Отправить комментарий