PageRenderTime 51ms CodeModel.GetById 19ms RepoModel.GetById 1ms app.codeStats 0ms

/package/boost_1_58_0/libs/multi_index/perf/test_perf.cpp

https://gitlab.com/cdeclare/intcrypt
C++ | 545 lines | 390 code | 89 blank | 66 comment | 22 complexity | 808892cb2cfe9ccc7352c31b885bc1ff MD5 | raw file
  1. /* Boost.MultiIndex performance test.
  2. *
  3. * Copyright 2003-2013 Joaquin M Lopez Munoz.
  4. * Distributed under the Boost Software License, Version 1.0.
  5. * (See accompanying file LICENSE_1_0.txt or copy at
  6. * http://www.boost.org/LICENSE_1_0.txt)
  7. *
  8. * See http://www.boost.org/libs/multi_index for library home page.
  9. */
  10. #include <boost/config.hpp> /* keep it first to prevent nasty warns in MSVC */
  11. #include <algorithm>
  12. #include <assert.h>
  13. #include <boost/multi_index_container.hpp>
  14. #include <boost/multi_index/identity.hpp>
  15. #include <boost/multi_index/ordered_index.hpp>
  16. #include <boost/multi_index/sequenced_index.hpp>
  17. #include <boost/next_prior.hpp>
  18. #include <climits>
  19. #include <ctime>
  20. #include <iomanip>
  21. #include <iostream>
  22. #include <list>
  23. #include <set>
  24. #include <string>
  25. #include <vector>
  26. using namespace std;
  27. using namespace boost::multi_index;
  28. /* Measurement harness by Andrew Koenig, extracted from companion code to
  29. * Stroustrup, B.: "Wrapping C++ Member Function Calls", The C++ Report,
  30. * June 2000, Vol 12/No 6.
  31. * Original code retrievable at: http://www.research.att.com/~bs/wrap_code.cpp
  32. */
  33. // How many clock units does it take to interrogate the clock?
  34. static double clock_overhead()
  35. {
  36. clock_t k = clock(), start, limit;
  37. // Wait for the clock to tick
  38. do start = clock();
  39. while (start == k);
  40. // interrogate the clock until it has advanced at least a second
  41. // (for reasonable accuracy)
  42. limit = start + CLOCKS_PER_SEC;
  43. unsigned long r = 0;
  44. while ((k = clock()) < limit)
  45. ++r;
  46. return double(k - start) / r;
  47. }
  48. // We'd like the odds to be factor:1 that the result is
  49. // within percent% of the median
  50. const int factor = 10;
  51. const int percent = 20;
  52. // Measure a function (object) factor*2 times,
  53. // appending the measurements to the second argument
  54. template<class F>
  55. void measure_aux(F f, vector<double>& mv)
  56. {
  57. static double ovhd = clock_overhead();
  58. // Ensure we don't reallocate in mid-measurement
  59. mv.reserve(mv.size() + factor*2);
  60. // Wait for the clock to tick
  61. clock_t k = clock();
  62. clock_t start;
  63. do start = clock();
  64. while (start == k);
  65. // Do 2*factor measurements
  66. for (int i = 2*factor; i; --i) {
  67. unsigned long count = 0, limit = 1, tcount = 0;
  68. // Original code used CLOCKS_PER_SEC/100
  69. const clock_t clocklimit = start + CLOCKS_PER_SEC/10;
  70. clock_t t;
  71. do {
  72. while (count < limit) {
  73. f();
  74. ++count;
  75. }
  76. limit *= 2;
  77. ++tcount;
  78. } while ((t = clock()) < clocklimit);
  79. // Wait for the clock to tick again;
  80. clock_t t2;
  81. do ++tcount;
  82. while ((t2 = clock()) == t);
  83. // Append the measurement to the vector
  84. mv.push_back(((t2 - start) - (tcount * ovhd)) / count);
  85. // Establish a new starting point
  86. start = t2;
  87. }
  88. }
  89. // Returns the number of clock units per iteration
  90. // With odds of factor:1, the measurement is within percent% of
  91. // the value returned, which is also the median of all measurements.
  92. template<class F>
  93. double measure(F f)
  94. {
  95. vector<double> mv;
  96. int n = 0; // iteration counter
  97. do {
  98. ++n;
  99. // Try 2*factor measurements
  100. measure_aux(f, mv);
  101. assert(mv.size() == 2*n*factor);
  102. // Compute the median. We know the size is even, so we cheat.
  103. sort(mv.begin(), mv.end());
  104. double median = (mv[n*factor] + mv[n*factor-1])/2;
  105. // If the extrema are within threshold of the median, we're done
  106. if (mv[n] > (median * (100-percent))/100 &&
  107. mv[mv.size() - n - 1] < (median * (100+percent))/100)
  108. return median;
  109. } while (mv.size() < factor * 200);
  110. // Give up!
  111. clog << "Help!\n\n";
  112. exit(1);
  113. }
  114. /* dereferencing compare predicate */
  115. template <typename Iterator,typename Compare>
  116. struct it_compare
  117. {
  118. bool operator()(const Iterator& x,const Iterator& y)const{return comp(*x,*y);}
  119. private:
  120. Compare comp;
  121. };
  122. /* list_wrapper and multiset_wrapper adapt std::lists and std::multisets
  123. * to make them conform to a set-like insert interface which test
  124. * routines do assume.
  125. */
  126. template <typename List>
  127. struct list_wrapper:List
  128. {
  129. typedef typename List::value_type value_type;
  130. typedef typename List::iterator iterator;
  131. pair<iterator,bool> insert(const value_type& v)
  132. {
  133. List::push_back(v);
  134. return pair<iterator,bool>(boost::prior(List::end()),true);
  135. }
  136. };
  137. template <typename Multiset>
  138. struct multiset_wrapper:Multiset
  139. {
  140. typedef typename Multiset::value_type value_type;
  141. typedef typename Multiset::iterator iterator;
  142. pair<iterator,bool> insert(const value_type& v)
  143. {
  144. return pair<iterator,bool>(Multiset::insert(v),true);
  145. }
  146. };
  147. /* space comsumption of manual simulations is determined by checking
  148. * the node sizes of the containers involved. This cannot be done in a
  149. * portable manner, so node_size has to be written on a per stdlibrary
  150. * basis. Add your own versions if necessary.
  151. */
  152. #if defined(BOOST_DINKUMWARE_STDLIB)
  153. template<typename Container>
  154. size_t node_size(const Container&)
  155. {
  156. return sizeof(*Container().begin()._Mynode());
  157. }
  158. #elif defined(__GLIBCPP__) || defined(__GLIBCXX__)
  159. template<typename Container>
  160. size_t node_size(const Container&)
  161. {
  162. typedef typename Container::iterator::_Link_type node_ptr;
  163. node_ptr p=0;
  164. return sizeof(*p);
  165. }
  166. template<typename Value,typename Allocator>
  167. size_t node_size(const list<Value,Allocator>&)
  168. {
  169. return sizeof(typename list<Value,Allocator>::iterator::_Node);
  170. }
  171. template<typename List>
  172. size_t node_size(const list_wrapper<List>&)
  173. {
  174. return sizeof(typename List::iterator::_Node);
  175. }
  176. #else
  177. /* default version returns 0 by convention */
  178. template<typename Container>
  179. size_t node_size(const Container&)
  180. {
  181. return 0;
  182. }
  183. #endif
  184. /* mono_container runs the tested routine on multi_index and manual
  185. * simulations comprised of one standard container.
  186. * bi_container and tri_container run the equivalent routine for manual
  187. * compositions of two and three standard containers, respectively.
  188. */
  189. template <typename Container>
  190. struct mono_container
  191. {
  192. mono_container(int n_):n(n_){}
  193. void operator()()
  194. {
  195. typedef typename Container::iterator iterator;
  196. Container c;
  197. for(int i=0;i<n;++i)c.insert(i);
  198. for(iterator it=c.begin();it!=c.end();)c.erase(it++);
  199. }
  200. static size_t multi_index_node_size()
  201. {
  202. return sizeof(*Container().begin().get_node());
  203. }
  204. static size_t node_size()
  205. {
  206. return ::node_size(Container());
  207. }
  208. private:
  209. int n;
  210. };
  211. template <typename Container1,typename Container2>
  212. struct bi_container
  213. {
  214. bi_container(int n_):n(n_){}
  215. void operator()()
  216. {
  217. typedef typename Container1::iterator iterator1;
  218. typedef typename Container2::iterator iterator2;
  219. Container1 c1;
  220. Container2 c2;
  221. for(int i=0;i<n;++i){
  222. iterator1 it1=c1.insert(i).first;
  223. c2.insert(it1);
  224. }
  225. for(iterator2 it2=c2.begin();it2!=c2.end();)
  226. {
  227. c1.erase(*it2);
  228. c2.erase(it2++);
  229. }
  230. }
  231. static size_t node_size()
  232. {
  233. return ::node_size(Container1())+::node_size(Container2());
  234. }
  235. private:
  236. int n;
  237. };
  238. template <typename Container1,typename Container2,typename Container3>
  239. struct tri_container
  240. {
  241. tri_container(int n_):n(n_){}
  242. void operator()()
  243. {
  244. typedef typename Container1::iterator iterator1;
  245. typedef typename Container2::iterator iterator2;
  246. typedef typename Container3::iterator iterator3;
  247. Container1 c1;
  248. Container2 c2;
  249. Container3 c3;
  250. for(int i=0;i<n;++i){
  251. iterator1 it1=c1.insert(i).first;
  252. iterator2 it2=c2.insert(it1).first;
  253. c3.insert(it2);
  254. }
  255. for(iterator3 it3=c3.begin();it3!=c3.end();)
  256. {
  257. c1.erase(**it3);
  258. c2.erase(*it3);
  259. c3.erase(it3++);
  260. }
  261. }
  262. static size_t node_size()
  263. {
  264. return ::node_size(Container1())+
  265. ::node_size(Container2())+::node_size(Container3());
  266. }
  267. private:
  268. int n;
  269. };
  270. /* measure and compare two routines for several numbers of elements
  271. * and also estimates relative memory consumption.
  272. */
  273. template <typename IndexedTest,typename ManualTest>
  274. void run_tests(const char* title)
  275. {
  276. cout<<fixed<<setprecision(2);
  277. cout<<title<<endl;
  278. int n=1000;
  279. for(int i=0;i<3;++i){
  280. double indexed_t=measure(IndexedTest(n));
  281. double manual_t=measure(ManualTest(n));
  282. cout<<" 10^"<<i+3<<" elmts: "
  283. <<setw(6)<<100.0*indexed_t/manual_t<<"% "
  284. <<"("
  285. <<setw(6)<<1000.0*indexed_t/CLOCKS_PER_SEC<<" ms / "
  286. <<setw(6)<<1000.0*manual_t/CLOCKS_PER_SEC<<" ms)"
  287. <<endl;
  288. n*=10;
  289. }
  290. size_t indexed_t_node_size=IndexedTest::multi_index_node_size();
  291. size_t manual_t_node_size=ManualTest::node_size();
  292. if(manual_t_node_size){
  293. cout<<" space gain: "
  294. <<setw(6)<<100.0*indexed_t_node_size/manual_t_node_size<<"%"<<endl;
  295. }
  296. }
  297. /* compare_structures accept a multi_index_container instantiation and
  298. * several standard containers, builds a manual simulation out of the
  299. * latter and run the tests.
  300. */
  301. template <typename IndexedType,typename ManualType>
  302. void compare_structures(const char* title)
  303. {
  304. run_tests<
  305. mono_container<IndexedType>,
  306. mono_container<ManualType>
  307. >(title);
  308. }
  309. template <typename IndexedType,typename ManualType1,typename ManualType2>
  310. void compare_structures2(const char* title)
  311. {
  312. run_tests<
  313. mono_container<IndexedType>,
  314. bi_container<ManualType1,ManualType2>
  315. >(title);
  316. }
  317. template <
  318. typename IndexedType,
  319. typename ManualType1,typename ManualType2,typename ManualType3
  320. >
  321. void compare_structures3(const char* title)
  322. {
  323. run_tests<
  324. mono_container<IndexedType>,
  325. tri_container<ManualType1,ManualType2,ManualType3>
  326. >(title);
  327. }
  328. int main()
  329. {
  330. /* some stdlibs provide the discussed but finally rejected std::identity */
  331. using boost::multi_index::identity;
  332. {
  333. /* 1 ordered index */
  334. typedef multi_index_container<int> indexed_t;
  335. typedef set<int> manual_t;
  336. compare_structures<indexed_t,manual_t>(
  337. "1 ordered index");
  338. }
  339. {
  340. /* 1 sequenced index */
  341. typedef list_wrapper<
  342. multi_index_container<
  343. int,
  344. indexed_by<sequenced<> >
  345. >
  346. > indexed_t;
  347. typedef list_wrapper<list<int> > manual_t;
  348. compare_structures<indexed_t,manual_t>(
  349. "1 sequenced index");
  350. }
  351. {
  352. /* 2 ordered indices */
  353. typedef multi_index_container<
  354. int,
  355. indexed_by<
  356. ordered_unique<identity<int> >,
  357. ordered_non_unique<identity<int> >
  358. >
  359. > indexed_t;
  360. typedef set<int> manual_t1;
  361. typedef multiset<
  362. manual_t1::iterator,
  363. it_compare<
  364. manual_t1::iterator,
  365. manual_t1::key_compare
  366. >
  367. > manual_t2;
  368. compare_structures2<indexed_t,manual_t1,manual_t2>(
  369. "2 ordered indices");
  370. }
  371. {
  372. /* 1 ordered index + 1 sequenced index */
  373. typedef multi_index_container<
  374. int,
  375. indexed_by<
  376. boost::multi_index::ordered_unique<identity<int> >,
  377. sequenced<>
  378. >
  379. > indexed_t;
  380. typedef list_wrapper<
  381. list<int>
  382. > manual_t1;
  383. typedef multiset<
  384. manual_t1::iterator,
  385. it_compare<
  386. manual_t1::iterator,
  387. std::less<int>
  388. >
  389. > manual_t2;
  390. compare_structures2<indexed_t,manual_t1,manual_t2>(
  391. "1 ordered index + 1 sequenced index");
  392. }
  393. {
  394. /* 3 ordered indices */
  395. typedef multi_index_container<
  396. int,
  397. indexed_by<
  398. ordered_unique<identity<int> >,
  399. ordered_non_unique<identity<int> >,
  400. ordered_non_unique<identity<int> >
  401. >
  402. > indexed_t;
  403. typedef set<int> manual_t1;
  404. typedef multiset_wrapper<
  405. multiset<
  406. manual_t1::iterator,
  407. it_compare<
  408. manual_t1::iterator,
  409. manual_t1::key_compare
  410. >
  411. >
  412. > manual_t2;
  413. typedef multiset<
  414. manual_t2::iterator,
  415. it_compare<
  416. manual_t2::iterator,
  417. manual_t2::key_compare
  418. >
  419. > manual_t3;
  420. compare_structures3<indexed_t,manual_t1,manual_t2,manual_t3>(
  421. "3 ordered indices");
  422. }
  423. {
  424. /* 2 ordered indices + 1 sequenced index */
  425. typedef multi_index_container<
  426. int,
  427. indexed_by<
  428. ordered_unique<identity<int> >,
  429. ordered_non_unique<identity<int> >,
  430. sequenced<>
  431. >
  432. > indexed_t;
  433. typedef list_wrapper<
  434. list<int>
  435. > manual_t1;
  436. typedef multiset_wrapper<
  437. multiset<
  438. manual_t1::iterator,
  439. it_compare<
  440. manual_t1::iterator,
  441. std::less<int>
  442. >
  443. >
  444. > manual_t2;
  445. typedef multiset<
  446. manual_t2::iterator,
  447. it_compare<
  448. manual_t2::iterator,
  449. manual_t2::key_compare
  450. >
  451. > manual_t3;
  452. compare_structures3<indexed_t,manual_t1,manual_t2,manual_t3>(
  453. "2 ordered indices + 1 sequenced index");
  454. }
  455. return 0;
  456. }