| /* Boost.Flyweight example of performance comparison. |
| * |
| * Copyright 2006-2008 Joaquin M Lopez Munoz. |
| * Distributed under the Boost Software License, Version 1.0. |
| * (See accompanying file LICENSE_1_0.txt or copy at |
| * http://www.boost.org/LICENSE_1_0.txt) |
| * |
| * See http://www.boost.org/libs/flyweight for library home page. |
| */ |
| |
| #include <boost/flyweight/flyweight.hpp> |
| #include <boost/flyweight/hashed_factory.hpp> |
| #include <boost/flyweight/set_factory.hpp> |
| #include <boost/flyweight/static_holder.hpp> |
| #include <boost/flyweight/simple_locking.hpp> |
| #include <boost/flyweight/refcounted.hpp> |
| #include <boost/flyweight/no_tracking.hpp> |
| #include <boost/mpl/bool.hpp> |
| #include <boost/tokenizer.hpp> |
| #include <algorithm> |
| #include <cstddef> |
| #include <cstdlib> |
| #include <ctime> |
| #include <fstream> |
| #include <iostream> |
| #include <numeric> |
| #include <sstream> |
| #include <string> |
| #include <vector> |
| |
| #if defined(BOOST_NO_STDC_NAMESPACE) |
| namespace std{ |
| using ::clock; |
| using ::clock_t; |
| using ::exit; |
| } |
| #endif |
| |
| using namespace boost::flyweights; |
| |
| /* Instrumented allocator family keeping track of the memory in |
| * current use. |
| */ |
| |
| std::size_t count_allocator_mem=0; |
| |
| template<typename T> |
| class count_allocator |
| { |
| public: |
| typedef std::size_t size_type; |
| typedef std::ptrdiff_t difference_type; |
| typedef T* pointer; |
| typedef const T* const_pointer; |
| typedef T& reference; |
| typedef const T& const_reference; |
| typedef T value_type; |
| template<class U>struct rebind{typedef count_allocator<U> other;}; |
| |
| count_allocator(){} |
| count_allocator(const count_allocator<T>&){} |
| template<class U>count_allocator(const count_allocator<U>&,int=0){} |
| |
| pointer address(reference x)const{return &x;} |
| const_pointer address(const_reference x)const{return &x;} |
| |
| pointer allocate(size_type n,const void* =0) |
| { |
| pointer p=(T*)(new char[n*sizeof(T)]); |
| count_allocator_mem+=n*sizeof(T); |
| return p; |
| } |
| |
| void deallocate(void* p,size_type n) |
| { |
| count_allocator_mem-=n*sizeof(T); |
| delete [](char *)p; |
| } |
| |
| size_type max_size() const{return (size_type )(-1);} |
| void construct(pointer p,const T& val){new(p)T(val);} |
| void destroy(pointer p){p->~T();} |
| |
| friend bool operator==(const count_allocator&,const count_allocator&) |
| { |
| return true; |
| } |
| |
| friend bool operator!=(const count_allocator&,const count_allocator&) |
| { |
| return false; |
| } |
| }; |
| |
| template<> |
| class count_allocator<void> |
| { |
| public: |
| typedef void* pointer; |
| typedef const void* const_pointer; |
| typedef void value_type; |
| template<class U>struct rebind{typedef count_allocator<U> other;}; |
| }; |
| |
| /* Define some count_allocator-based types and Boost.Flyweight components */ |
| |
| typedef std::basic_string< |
| char,std::char_traits<char>,count_allocator<char> |
| > count_string; |
| |
| typedef hashed_factory< |
| boost::hash<boost::mpl::_2>, |
| std::equal_to<boost::mpl::_2>, |
| count_allocator<boost::mpl::_1> |
| > count_hashed_factory; |
| |
| typedef set_factory< |
| std::less<boost::mpl::_2>, |
| count_allocator<boost::mpl::_1> |
| > count_set_factory; |
| |
| /* Some additional utilities used by the test routine */ |
| |
| class timer |
| { |
| public: |
| timer(){restart();} |
| |
| void restart(){t=std::clock();} |
| |
| void time(const char* str) |
| { |
| std::cout<<str<<": "<<(double)(std::clock()-t)/CLOCKS_PER_SEC<<" s\n"; |
| } |
| |
| private: |
| std::clock_t t; |
| }; |
| |
| template<typename T> |
| struct is_flyweight: |
| boost::mpl::false_{}; |
| |
| template< |
| typename T, |
| typename Arg1,typename Arg2,typename Arg3,typename Arg4,typename Arg5 |
| > |
| struct is_flyweight<flyweight<T,Arg1,Arg2,Arg3,Arg4,Arg5> >: |
| boost::mpl::true_{}; |
| |
| struct length_adder |
| { |
| std::size_t operator()(std::size_t n,const count_string& x)const |
| { |
| return n+x.size(); |
| } |
| }; |
| |
| /* Measure time and memory performance for a String, which is assumed |
| * to be either a plain string type or a string flyweight. |
| */ |
| |
| template<typename String> |
| struct test |
| { |
| static std::size_t run(const std::string& file) |
| { |
| typedef std::vector<String,count_allocator<String> > count_vector; |
| |
| /* Define a tokenizer on std::istreambuf. */ |
| |
| typedef std::istreambuf_iterator<char> char_iterator; |
| typedef boost::tokenizer< |
| boost::char_separator<char>, |
| char_iterator |
| > tokenizer; |
| |
| std::ifstream ifs(file.c_str()); |
| if(!ifs){ |
| std::cout<<"can't open "<<file<<std::endl; |
| std::exit(EXIT_FAILURE); |
| } |
| |
| /* Initialization; tokenize using space and common punctuaction as |
| * separators, and keeping the separators. |
| */ |
| |
| timer t; |
| |
| tokenizer tok=tokenizer( |
| char_iterator(ifs),char_iterator(), |
| boost::char_separator<char>( |
| "", |
| "\t\n\r !\"#$%&'()*+,-./:;<=>?@[\\]^_`{|}~")); |
| count_vector txt; |
| for(tokenizer::iterator it=tok.begin();it!=tok.end();++it){ |
| txt.push_back(String(it->c_str())); |
| } |
| |
| t.time("initialization time"); |
| |
| /* Assignment */ |
| |
| t.restart(); |
| |
| count_vector txt2; |
| for(int i=0;i<10;++i){ |
| txt2.insert(txt2.end(),txt.begin(),txt.end()); |
| } |
| |
| t.time("assignment time"); |
| |
| /* Equality comparison */ |
| |
| t.restart(); |
| |
| std::size_t c=0; |
| for(int i=0;i<100;++i){ |
| c+=std::count(txt.begin(),txt.end(),txt[c%txt.size()]); |
| } |
| |
| t.time("equality comparison time"); |
| |
| /* Value access */ |
| |
| t.restart(); |
| |
| std::size_t s=0; |
| for(int i=0;i<20;++i){ |
| s+=std::accumulate(txt2.begin(),txt2.end(),s,length_adder()); |
| } |
| |
| t.time("value access time"); |
| |
| std::cout<<"bytes used: "<<count_allocator_mem; |
| if(is_flyweight<String>::value){ |
| std::size_t flyweight_mem=(txt.capacity()+txt2.capacity())*sizeof(String); |
| std::cout<<"= flyweights("<<flyweight_mem |
| <<")+values("<<count_allocator_mem-flyweight_mem<<")"; |
| } |
| std::cout<<"\n"; |
| |
| return c+s; |
| } |
| }; |
| |
| /* table of test cases for the user to select from */ |
| |
| struct test_case |
| { |
| const char* name; |
| std::size_t (*test)(const std::string&); |
| }; |
| |
| test_case test_table[]= |
| { |
| { |
| "simple string", |
| test<count_string>::run |
| }, |
| { |
| "flyweight, hashed factory", |
| test<flyweight<count_string,count_hashed_factory> >::run |
| }, |
| { |
| "flyweight, hashed factory, no tracking", |
| test<flyweight<count_string,count_hashed_factory,no_tracking> >::run |
| }, |
| { |
| "flyweight, set-based factory", |
| test<flyweight<count_string,count_set_factory> >::run |
| }, |
| { |
| "flyweight, set-based factory, no tracking", |
| test<flyweight<count_string,count_set_factory,no_tracking> >::run |
| } |
| }; |
| |
| const int num_test_cases=sizeof(test_table)/sizeof(test_case); |
| |
| int main() |
| { |
| try{ |
| for(int i=0;i<num_test_cases;++i){ |
| std::cout<<i+1<<". "<<test_table[i].name<<"\n"; |
| } |
| int option=-1; |
| for(;;){ |
| std::cout<<"select option, enter to exit: "; |
| std::string str; |
| std::getline(std::cin,str); |
| if(str.empty())std::exit(EXIT_SUCCESS); |
| std::istringstream istr(str); |
| istr>>option; |
| if(option>=1&&option<=num_test_cases){ |
| --option; /* pass from 1-based menu to 0-based test_table */ |
| break; |
| } |
| } |
| |
| std::cout<<"enter file name: "; |
| std::string file; |
| std::getline(std::cin,file); |
| std::size_t result=0; |
| result=test_table[option].test(file); |
| } |
| catch(const std::exception& e){ |
| std::cout<<"error: "<<e.what()<<"\n"; |
| } |
| |
| return 0; |
| } |