📜 ⬆️ ⬇️

Performance test of containers and pointers to objects

Introduction



This article describes the performance test of filling (push_back) containers with objects containing different types of pointers to different data types. The test is complex, combining typical tasks of creating a pointer to an object, copying an object, filling a container, freeing the memory allocated for an object by means of a smart pointer and the standard delete operator. Three containers of the standard template library will be tested - vector, list, deque, three types of pointers - std :: shared_ptr, std :: auto_ptr and a simple pointer. As test data types (for which pointers will be created), long, std :: string, char, an arbitrary class are used.

Test description


')
The source code is written in MS Visual Studio 2010, and requires the compiler to support lambda functions.
Let some class contain a description of the pointer T to the data type Type. The constructor will create an object and initialize it with the passed parameter. The copy constructor will copy the value located at the address of the specified object, as well as its name and the requirement to output messages about the life of the object to the console.

template< class T, typename Type > class test_type_and_smart_pointer { public: test_type_and_smart_pointer( std::string const& val, bool msg, Type type_val ) : _msg( msg ), _name( val ) { if( _msg ) std::cout << "created " << _name << std::endl; x.reset( new Type ); *x = type_val; } ~test_type_and_smart_pointer() { if( _msg ) std::cout << "deleted " << _name << std::endl; } test_type_and_smart_pointer(const test_type_and_smart_pointer & W) { if( W._msg ) std::cout << "copied " << W._name << std::endl; this->x.reset( new Type ); *(this->x) = *(Wx); this->_name = W._name; this->_msg = W._msg; } private: T x; bool _msg; std::string _name; }; 


Template parameters:

Members of the test_type_and_smart_pointer class:


Create a similar class for the case if the pointer T to Type is a standard.

 template< class T, typename Type > class test_type_and_simple_pointer { public: test_type_and_simple_pointer( std::string const& val, bool msg, Type type_val ) : _msg( msg ), _name( val ) { if( _msg ) std::cout << "created " << _name << std::endl; x = new Type; *x = type_val; } ~test_type_and_simple_pointer() { delete x; if( _msg ) std::cout << "deleted " << _name << std::endl; } test_type_and_simple_pointer(const test_type_and_simple_pointer & W) { if( W._msg ) std::cout << "copied " << W._name << std::endl; this->x = new Type( *(Wx) ); this->_name = W._name; this->_msg = W._msg; } private: T x; bool _msg; std::string _name; }; 


Now we need functions that create the above described objects:

 template< typename _Ptr, typename T, typename Container, bool msg > void test_smart_ptr( bool big_data, T val ) { using namespace std; test_type_and_smart_pointer< _Ptr, T > x( "a001", msg, val ); test_type_and_smart_pointer< _Ptr, T > x2( x ); Container cnt; cnt.push_back( x ); cnt.push_back( x2 ); if( big_data ) for( int i = 0; i <= 100; i++ ) { test_type_and_smart_pointer< _Ptr, T > xx( "a002", msg, val ); test_type_and_smart_pointer< _Ptr, T > xx2( xx ); cnt.push_back( xx ); cnt.push_back( xx2 ); } cnt.clear(); } template< typename T, typename Container, bool msg > void test_simple_ptr( bool big_data, T val ) { using namespace std; test_type_and_simple_pointer< T*, T> x( "b001", msg, val ); test_type_and_simple_pointer< T*, T> x2( x ); Container cnt; cnt.push_back( x ); cnt.push_back( x2 ); if( big_data ) for( int i = 0; i <= 100; i++ ) { test_type_and_simple_pointer< T*, T> xx( "b002", msg, val ); test_type_and_simple_pointer< T*, T> xx2( xx ); cnt.push_back( xx ); cnt.push_back( xx2 ); } cnt.clear(); } 


For example, these functions will work in two modes: creating a pair of pointers to objects and placing them in a container, and cyclically creating pointers to objects and filling the container.
The time spent executing the functions test_smart_ptr and test_simple_ptr will measure _time_test:

 double _time_test( std::function< void() > test_func, unsigned int times ) { using namespace std; double start = 0, end = 0; start = GetTickCount(); for( unsigned int i = 0; i < times; i++ ) { test_func(); } end = GetTickCount() - start; //cout << "Elapsed ms: " << end << // " for " << times << " times " << endl; return end; } 


Required header files:

 #include "iostream" #include "string" #include "vector" #include "list" #include "deque " #include <Windows.h> #include "functional" 


A simplest class without dynamic memory allocations is used as an object of an arbitrary type.

 class some_obj { public: some_obj() { i = 90000; s = "1231231231231231231232"; d = 39482.27392; } ~some_obj() { } private: int i; std::string s; double d; }; 


So, how was the test. 72 lambda functions are described, each of which, upon completion, brings the heap to its initial state.

 int main() { using namespace std; const long long_val = 900000000; const char ch = 'a'; const string str = "abc"; /////////////////////////////////////////////////////////////////////////////////////////////////////// auto f_01 = [&]() -> void { test_smart_ptr< shared_ptr< long >, long, vector< test_type_and_smart_pointer< shared_ptr< long >, long > >, false >( false, long_val ); }; auto f_02 = [&]() -> void { test_smart_ptr< shared_ptr< char >, char, vector< test_type_and_smart_pointer< shared_ptr< char >, char > >, false >( false, ch ); }; auto f_03 = [&]() -> void { test_smart_ptr< shared_ptr< string >, string, vector< test_type_and_smart_pointer< shared_ptr< string >, string > >, false >( false, str ); }; auto f_04 = [&]() -> void { some_obj x; test_smart_ptr< shared_ptr< some_obj >, some_obj, vector< test_type_and_smart_pointer< shared_ptr< some_obj >, some_obj > >, false >( false, x ); }; ... _time_test( f_01, 100000 ); _time_test( f_02, 100000 ); _time_test( f_03, 100000 ); _time_test( f_04, 100000 ); ... return 0; } 


The full code of the main function is not given, because it is cumbersome, but in the first block of lambda functions for the shared_ptr pointer, the general trend of pattern parameter combinations is visible.

Each f_nn function was performed 15 times and the measurement result was averaged (the launch was in the release mode not from under the studio ). The unit takes the smallest elapsed time, i.e. the remaining values ​​are in proportion.

Test results



The color indicates the fastest data type for each pointer type among all containers:



The fastest data types for each container among all pointer types:



The fastest container when filling in all data types and in all pointers :



What can be useful to extract?



First, the test results show which container fills up faster.
Secondly, it is clear how pointers on typical tasks work in proportion to speed.

Source: https://habr.com/ru/post/138550/


All Articles