Как стать автором
Обновить

Тест производительности контейнеров и указателей на объекты

Время на прочтение5 мин
Количество просмотров4.4K

Вступление



В данной статье рассмотрен тест производительности заполнения (push_back) контейнеров объектами, содержащими разные типы указателей на разные типы данных. Тест комплексный, сочетающий в себе типичные задачи создания указателя на объект, копирование объекта, заполнение контейнера, освобождение выделенной под объект памяти средствами умного указателя и стандартным оператором delete. Будут протестированы три контейнера стандартной библиотеки шаблонов – vector, list, deque, три вида указателей – std::shared_ptr, std::auto_ptr и простой указатель. В качестве испытуемых типов данных (на которые будут создаваться указатели) использованы long, std::string, char, произвольный класс.

Описание теста



Исходный код написан в среде MS Visual Studio 2010, и требует от компилятора поддержку лямбда функций.
Пусть некоторый класс содержит описание указателя T на тип данных Type. Конструктор будет создавать объект, и инициализировать переданным параметром. Конструктор копирования будет копировать значение, находящееся по адресу указанного объекта, а также его имя и требование выводить в консоль сообщения о жизни объекта.

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) = *(W.x);
	this->_name = W._name;
	this->_msg = W._msg;
}
private:
T x;
bool _msg;
std::string _name;
};


Параметры шаблона:
  • class T – подразумеваемый класс умного указателя,
  • typename Type – тип данных создаваемого объекта.

Члены класса test_type_and_smart_pointer:
  • T x — непосредственно сам указатель на объект,
  • bool _msg — флаг вывода информационных сообщений,
  • std::string _name – имя созданного объекта.


Создадим подобный класс для случая, если указатель T на тип Type будет стандартным.

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( *(W.x) );
	this->_name = W._name;
	this->_msg = W._msg;
}
private:
T x;
bool _msg;
std::string _name;
};


Теперь нужны функции, создающие выше описанные объекты:

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


Например, эти функции будут работать в двух режимах: создание пары указателей на объекты и размещение их в контейнере, и циклическое создание указателей на объекты и заполнение контейнера.
Время, затраченное на исполнение функций test_smart_ptr и test_simple_ptr будет измерять _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;
}


Необходимые заголовочные файлы:


#include "iostream"
#include "string"
#include "vector"
#include "list"
#include "deque "

#include <Windows.h>
#include "functional" 


В качестве объекта произвольного типа использован простейший класс без динамических выделений памяти.


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


Итак, как проводился тест. Описаны 72 лямбда функции, каждая из которых при завершении приводит кучу в исходное состояние.


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


Полный код функции main не приведен, ввиду громоздкости, но по первому блоку лямбда функций для указателя shared_ptr общая тенденция комбинаций параметров шаблонов видна.

Каждая функция f_nn выполнялась 15 раз и результат измерения усреднен (запуск был в релизном режиме не из под студии). За единицу принято самое малое затраченное время, т.е. остальные значения приведены в пропорции.

Результаты теста



Цветом обозначен наиболее быстрый тип данных по каждому типу указателя среди всех контейнеров:



Наиболее быстрые типы данных по каждому контейнеру среди всех типов указателей:



Наиболее быстрый контейнер при заполнении по всем типам данных и по всем указателям:



Что полезное можно извлечь?



Во-первых, по результатам теста видно, какой контейнер заполняется быстрее.
Во-вторых, понятно как в пропорциональном соотношении по скорости работают указатели на типичных задачах.
Теги:
Хабы:
+3
Комментарии44

Публикации

Изменить настройки темы

Истории

Работа

Программист C++
123 вакансии
QT разработчик
6 вакансий

Ближайшие события

Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн