/*
	Tytuł: Boost::pool
	Autor: Igor Rutkowski
	Grupa: G1EIK
*/

/*
	Standardowy operator new jest rozwiązaniem uniwersalnym i jako takie
obciążony jest wieloma kompromisami. Nie jest to rozwiązanie najwydajniejsze,
zwłaszcza dla małych obiektów. Pewnym antidotum jest wstępne zaalokowanie
większego obszaru pamięci i dzielenie jej na mniejsze fragmenty. Zadanie to może
ułatwić nam biblioteka boost::pool. Jest ona stosunkowo prosta, nie udostępnia 
użytkownikowi zbyt wielu klas. Jej użytkowanie sprowadza się do dołączenia 
plików nagłówkowych.

*/

#include <boost/pool/pool.hpp>
#include <boost/pool/object_pool.hpp>
#include <boost/pool/singleton_pool.hpp>
#include <ctime>
#include <iostream>

void speed_test(void);
using namespace std;

//Prosta struktura, pozwalająca śledzić wywoływanie konstruktorów i destruktorów.
struct X{
	X()  {	cout << "Konstruktor    "<<endl;}
	~X() {	cout << "Destruktor    "<<endl;}
};
//Dla boost::singleton_pool
struct Tag { };


int main(int argc, char * argv[]){
	int* int_pointer; 
	int* int_tab;
	X* X_pointer1;
	X* X_pointer2;

	//Prosty alokator pamięci. Obsługa błędów przez zwrócenie wartości NULL.
	//Nie jest przystosowany do obsługi obiektów.
	{
		boost::pool<> p(sizeof(int));
		//Jeden element
		int_pointer = reinterpret_cast<int *>(p.malloc());
		//Tablica elementow
		int_tab = reinterpret_cast<int *>(p.ordered_malloc(5));
		//Zwolnienie czesci tablicy
		p.free(int_tab+2,3);
		//Zwolnienie jednego elementu
		p.free(int_pointer);
		//Zwolnienie wszystkich bloków
		//p.purge_memory();
	} //	Nie jest to jednak konieczne, gdyż następuje automatycznie wraz z
	  //wywołaniem destruktora obiektu pool.
	cout << "Boost::pool dla struktury"<<endl;
	{
		boost::pool<> p(sizeof(X));
		X_pointer1 = reinterpret_cast<X *>(p.malloc());
		X_pointer2 = reinterpret_cast<X *>(p.malloc());
		p.free(X_pointer1);
	}
	//W żadnym momencie nie nastąpiło wywołanie konstruktora ani destruktora

	cout << "Boost::object_pool"<<endl;
	//Alokator przystosowany do zarządzania obiektami ale działający również z typami prostymi.
	//Obsługa błędów j.w. Nie przewidziano możliwości alokowania tablic.
	{
		boost::object_pool<X> p;
		cout<<"malloc():"<<endl;
		X_pointer1 = p.malloc();
		cout<<"construct():"<<endl;
		X_pointer2 = p.construct();
		cout<<"free():"<<endl;
		p.free(X_pointer1);
		cout<<"destroy():"<<endl;
		p.destroy(X_pointer2);
		X_pointer1 = p.malloc();
		X_pointer2 = p.construct();
		cout <<"Wywolanie destruktora object_pool"<<endl;
	}
	//Destruktor wywołano dla obiektów stworzonych zarówno przez malloc() jak i construct()
	//Może to być przyczyną istotnych błędów
	cout<<endl;

	//Istnieje również wersja boost::pool przystosowana do wielowątkowości.
	//Implementuje ona wzorzec programowy Singleton. Obsługa błędów j.w.
	//Struktura Tag jedynie rozróżnia nam obiekty, gdyż możliwe jest istnienie kilku
	//alokatorów dla tego samego typu.
	typedef boost::singleton_pool<Tag, sizeof(int)>  single_pool;
	{
		int * intp = reinterpret_cast<int *>(single_pool::malloc());
		single_pool::free(intp);
		int_pointer = reinterpret_cast<int *>(single_pool::malloc());
		
	} //Teraz możemy zwolnić cała pamięÄ? przydzieloną - nie odbywa sie to automatycznie wraz
	// z wyjściem z bloku!
	single_pool::purge_memory();
	//Poza tym, używamy tego alokatora tak samo jak boost::pool - udostępnia on identyczną funkcjonalność.


	speed_test();
	return 0;
}

//Srednia z 10 uruchomieĹ? (kompilacja : g++ -O3)
/*
Malloc i free : 779
New i delete : 1034
Boost::pool dla int
Malloc i free : 30
Boost::object_pool dla int
Construct i destroy : 79
Malloc i free : 71
*/
//Przy braku optymalizacji (-O0) wyniki dla bibliotek boost są znacznie gorsze.
//Różnice w wydajności są bardzo wyraźne, dlatego warto rozważyć używanie tej biblioteki.



void speed_test(void){
	//Dla systemu MS Windows
	const int div =1;
	//Dla Linuxa
	//const int div=1000;
	clock_t time1,time2;
	size_t i,size = 1024*1024*10;
	int* p;
	boost::object_pool<int> obj_pool;
	boost::pool<> pool(sizeof(int));

	cout<<"Test szybkosci"<<endl;
	time1=clock();
	for (i = 0; i < size; ++i)
	{
		p = reinterpret_cast<int *>(malloc(sizeof(int)));
		free(p);
	}
	time2=clock();
	std::cout << "Malloc i free : " << (time2 - time1)/div << std::endl;

	time1=clock();
	for (i = 0; i < size; ++i)
	{
		p = new int;
		delete p;
	}
	time2=clock();
	std::cout << "New i delete : " << (time2 - time1)/div << std::endl;

	std::cout << "Boost::pool dla int\n";

	time1=clock();
	for (i = 0; i < size; ++i)
	{
		p = reinterpret_cast<int *>(pool.malloc());
		pool.free(p);
	}
	time2=clock();
	std::cout <<  "Malloc i free : "<< (time2 - time1)/div << std::endl;	

	std::cout << "Boost::object_pool dla int\n";

	
	time1=clock();
	for (i = 0; i < size; ++i)
	{
		p = obj_pool.construct();
		obj_pool.destroy(p);
	}
	time2=clock();
	std::cout << "Construct i destroy : " << (time2 - time1)/div << std::endl;

	time1=clock();
	for (i = 0; i < size; ++i)
	{
		p = obj_pool.malloc();
		obj_pool.free(p);
	}
	time2=clock();
	std::cout <<  "Malloc i free : "<< (time2 - time1)/div << std::endl;
}
