STXXL  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
pq_ext_merger.h
Go to the documentation of this file.
1 /***************************************************************************
2  * include/stxxl/bits/containers/pq_ext_merger.h
3  *
4  * Part of the STXXL. See http://stxxl.sourceforge.net
5  *
6  * Copyright (C) 1999 Peter Sanders <[email protected]>
7  * Copyright (C) 2003, 2004, 2007 Roman Dementiev <[email protected]>
8  * Copyright (C) 2007-2009 Johannes Singler <[email protected]>
9  * Copyright (C) 2007-2010 Andreas Beckmann <[email protected]>
10  *
11  * Distributed under the Boost Software License, Version 1.0.
12  * (See accompanying file LICENSE_1_0.txt or copy at
13  * http://www.boost.org/LICENSE_1_0.txt)
14  **************************************************************************/
15 
16 #ifndef STXXL_CONTAINERS_PQ_EXT_MERGER_HEADER
17 #define STXXL_CONTAINERS_PQ_EXT_MERGER_HEADER
18 
20 
22 
23 //! \addtogroup stlcontinternals
24 //!
25 //! \{
26 
27 /*! \internal
28  */
29 namespace priority_queue_local {
30 
31 template <typename Iterator>
32 class short_sequence : public std::pair<Iterator, Iterator>
33 {
34  typedef std::pair<Iterator, Iterator> pair;
35 
36 public:
37  typedef Iterator iterator;
38  typedef const iterator const_iterator;
39  typedef typename std::iterator_traits<iterator>::value_type value_type;
40  typedef typename std::iterator_traits<iterator>::difference_type size_type;
42  typedef const value_type& const_reference;
44 
45 private:
47 
48 public:
49  short_sequence(Iterator first, Iterator last, origin_type origin) :
50  pair(first, last), m_origin(origin)
51  { }
52 
54  {
55  return this->first;
56  }
57 
59  {
60  return this->first;
61  }
62 
64  {
65  return begin();
66  }
67 
69  {
70  return this->second;
71  }
72 
74  {
75  return this->second;
76  }
77 
79  {
80  return end();
81  }
82 
84  {
85  return *begin();
86  }
87 
89  {
90  return *begin();
91  }
92 
94  {
95  return *(end() - 1);
96  }
97 
99  {
100  return *(end() - 1);
101  }
102 
103  size_type size() const
104  {
105  return end() - begin();
106  }
107 
108  bool empty() const
109  {
110  return size() == 0;
111  }
112 
114  {
115  return m_origin;
116  }
117 };
118 
119 /*!
120  * External merger, based on the loser tree data structure.
121  * \param Arity_ maximum arity of merger, does not need to be a power of 2
122  */
123 template <class BlockType_,
124  class Cmp_,
125  unsigned Arity_,
126  class AllocStr_ = STXXL_DEFAULT_ALLOC_STRATEGY>
127 class ext_merger : private noncopyable
128 {
129 public:
131  typedef BlockType_ block_type;
132  typedef typename block_type::bid_type bid_type;
133  typedef typename block_type::value_type value_type;
134  typedef Cmp_ comparator_type;
135  typedef AllocStr_ alloc_strategy;
137 
138  // arity_bound / 2 < arity <= arity_bound
139  enum { arity = Arity_, arity_bound = 1UL << (LOG2<Arity_>::ceil) };
140 
141 protected:
143 
144  bool is_sentinel(const value_type& a) const
145  {
146  return !(cmp(cmp.min_value(), a)); // a <= cmp.min_value()
147  }
148 
149  bool not_sentinel(const value_type& a) const
150  {
151  return cmp(cmp.min_value(), a); // a > cmp.min_value()
152  }
153 
154  struct sequence_state : private noncopyable
155  {
156  block_type* block; //current block
157  unsigned_type current; //current index in current block
158  std::list<bid_type>* bids; //list of blocks forming this sequence
161  bool allocated;
162 
163  //! \returns current element
164  const value_type& operator * () const
165  {
166  return (*block)[current];
167  }
168 
170  : block(NULL), current(0),
171  bids(NULL), merger(NULL),
172  allocated(false)
173  { }
174 
176  {
177  STXXL_VERBOSE2("ext_merger sequence_state::~sequence_state()");
178  if (bids != NULL)
179  {
180  block_manager* bm = block_manager::get_instance();
181  bm->delete_blocks(bids->begin(), bids->end());
182  delete bids;
183  }
184  }
185 
186  void make_inf()
187  {
188  current = 0;
189  (*block)[0] = cmp.min_value();
190  }
191 
192  bool is_sentinel(const value_type& a) const
193  {
194  return !(cmp(cmp.min_value(), a));
195  }
196 
197  bool not_sentinel(const value_type& a) const
198  {
199  return cmp(cmp.min_value(), a);
200  }
201 
202  void swap(sequence_state& obj)
203  {
204  if (&obj != this)
205  {
206  std::swap(current, obj.current);
207  std::swap(block, obj.block);
208  std::swap(bids, obj.bids);
209  assert(merger == obj.merger);
210  std::swap(allocated, obj.allocated);
211  }
212  }
213 
215  {
216  assert(not_sentinel((*block)[current]));
217  assert(current < block->size);
218 
219  ++current;
220 
221  if (current == block->size)
222  {
223  STXXL_VERBOSE2("ext_merger sequence_state operator++ crossing block border ");
224  // go to the next block
225  assert(bids != NULL);
226  if (bids->empty()) // if there is no next block
227  {
228  STXXL_VERBOSE2("ext_merger sequence_state operator++ it was the last block in the sequence ");
229  delete bids;
230  bids = NULL;
231  make_inf();
232  }
233  else
234  {
235  STXXL_VERBOSE2("ext_merger sequence_state operator++ there is another block ");
236  bid_type bid = bids->front();
237  bids->pop_front();
238  merger->pool->hint(bid);
239  if (!(bids->empty()))
240  {
241  STXXL_VERBOSE2("ext_merger sequence_state operator++ more blocks exist in a sequence, hinting the next");
242  merger->pool->hint(bids->front());
243  }
244  merger->pool->read(block, bid)->wait();
245  STXXL_VERBOSE2("first element of read block " << bid << " " << *(block->begin()) << " cached in " << block);
246  if (!(bids->empty()))
247  merger->pool->hint(bids->front()); // re-hint, reading might have made a block free
248  block_manager::get_instance()->delete_block(bid);
249  current = 0;
250  }
251  }
252  return *this;
253  }
254  };
255 
256 
257 #if STXXL_PQ_EXTERNAL_LOSER_TREE
258  struct Entry
259  {
260  value_type key; // key of loser element (winner for 0)
261  unsigned_type index; // the number of the losing segment
262  };
263 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE
264 
265  size_type size_; // total number of elements stored
266  unsigned_type log_k; // log of current tree size
267  unsigned_type k; // invariant (k == 1 << log_k), always a power of 2
268  // only entries 0 .. arity-1 may hold actual sequences, the other
269  // entries arity .. arity_bound-1 are sentinels to make the size of the tree
270  // a power of 2 always
271 
272  // stack of empty segment indices
274 
275 #if STXXL_PQ_EXTERNAL_LOSER_TREE
276  // upper levels of loser trees
277  // entry[0] contains the winner info
278  Entry entry[arity_bound];
279 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE
280 
281  // leaf information
282  // note that Knuth uses indices k..k-1
283  // while we use 0..k-1
284  sequence_state states[arity_bound]; // sequence including current position, dereference gives current element
285 
287 
289 
290 public:
292  size_(0), log_k(0), k(1), pool(0)
293  {
294  init();
295  }
296 
298  size_(0), log_k(0), k(1),
299  pool(pool_)
300  {
301  init();
302  }
303 
304  virtual ~ext_merger()
305  {
306  STXXL_VERBOSE1("ext_merger::~ext_merger()");
307  for (unsigned_type i = 0; i < arity; ++i)
308  {
309  delete states[i].block;
310  }
311  delete sentinel_block;
312  }
313 
314  void set_pool(pool_type* pool_)
315  {
316  pool = pool_;
317  }
318 
319 private:
320  void init()
321  {
322  STXXL_VERBOSE2("ext_merger::init()");
323  assert(!cmp(cmp.min_value(), cmp.min_value())); // verify strict weak ordering
324 
325  sentinel_block = NULL;
326  if (arity < arity_bound)
327  {
328  sentinel_block = new block_type;
329  for (unsigned_type i = 0; i < block_type::size; ++i)
330  (*sentinel_block)[i] = cmp.min_value();
331  if (arity + 1 == arity_bound) {
332  // same memory consumption, but smaller merge width, better use arity = arity_bound
333  STXXL_ERRMSG("inefficient PQ parameters for ext_merger: arity + 1 == arity_bound");
334  }
335  }
336 
337  for (unsigned_type i = 0; i < arity_bound; ++i)
338  {
339  states[i].merger = this;
340  if (i < arity)
341  states[i].block = new block_type;
342  else
343  states[i].block = sentinel_block;
344 
345  states[i].make_inf();
346  }
347 
348  assert(k == 1);
349  free_segments.push(0); //total state: one free sequence
350 
351  rebuild_loser_tree();
352 #if STXXL_PQ_EXTERNAL_LOSER_TREE
353  assert(is_sentinel(*states[entry[0].index]));
354 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE
355  }
356 
357  // rebuild loser tree information from the values in current
359  {
360 #if STXXL_PQ_EXTERNAL_LOSER_TREE
361  unsigned_type winner = init_winner(1);
362  entry[0].index = winner;
363  entry[0].key = *(states[winner]);
364 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE
365  }
366 
367 
368 #if STXXL_PQ_EXTERNAL_LOSER_TREE
369  // given any values in the leaves this
370  // routing recomputes upper levels of the tree
371  // from scratch in linear time
372  // initialize entry[root].index and the subtree rooted there
373  // return winner index
374  unsigned_type init_winner(unsigned_type root)
375  {
376  if (root >= k || root >= arity_bound)
377  { // leaf reached
378  return root - k;
379  }
380  else
381  {
382  unsigned_type left = init_winner(2 * root);
383  unsigned_type right = init_winner(2 * root + 1);
384  value_type lk = *(states[left]);
385  value_type rk = *(states[right]);
386  assert(root < arity_bound);
387  if (!(cmp(lk, rk)))
388  { // right subtree looses
389  entry[root].index = right;
390  entry[root].key = rk;
391  return left;
392  }
393  else
394  {
395  entry[root].index = left;
396  entry[root].key = lk;
397  return right;
398  }
399  }
400  }
401 
402  // first go up the tree all the way to the root
403  // hand down old winner for the respective subtree
404  // based on new value, and old winner and loser
405  // update each node on the path to the root top down.
406  // This is implemented recursively
407  void update_on_insert(
408  unsigned_type node,
409  const value_type& new_key,
410  unsigned_type new_index,
411  value_type* winner_key,
412  unsigned_type* winner_index, // old winner
413  unsigned_type* mask) // 1 << (ceil(log KNK) - dist-from-root)
414  {
415  if (node == 0)
416  { // winner part of root
417  *mask = unsigned_type(1) << (log_k - 1);
418  *winner_key = entry[0].key;
419  *winner_index = entry[0].index;
420  if (cmp(entry[node].key, new_key))
421  {
422  entry[node].key = new_key;
423  entry[node].index = new_index;
424  }
425  }
426  else
427  {
428  update_on_insert(node >> 1, new_key, new_index, winner_key, winner_index, mask);
429  value_type loserKey = entry[node].key;
430  unsigned_type loserIndex = entry[node].index;
431  if ((*winner_index & *mask) != (new_index & *mask))
432  { // different subtrees
433  if (cmp(loserKey, new_key))
434  { // new_key will have influence here
435  if (cmp(*winner_key, new_key))
436  { // old winner loses here
437  entry[node].key = *winner_key;
438  entry[node].index = *winner_index;
439  }
440  else
441  { // new entry looses here
442  entry[node].key = new_key;
443  entry[node].index = new_index;
444  }
445  }
446  *winner_key = loserKey;
447  *winner_index = loserIndex;
448  }
449  // note that nothing needs to be done if
450  // the winner came from the same subtree
451  // a) new_key <= winner_key => even more reason for the other tree to lose
452  // b) new_key > winner_key => the old winner will beat the new
453  // entry further down the tree
454  // also the same old winner is handed down the tree
455 
456  *mask >>= 1; // next level
457  }
458  }
459 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE
460 
461  // make the tree two times as wide
462  void double_k()
463  {
464  STXXL_VERBOSE1("ext_merger::double_k (before) k=" << k << " log_k=" << log_k << " arity_bound=" << arity_bound << " arity=" << arity << " #free=" << free_segments.size());
465  assert(k > 0);
466  assert(k < arity);
467  assert(free_segments.empty()); // stack was free (probably not needed)
468 
469  // make all new entries free
470  // and push them on the free stack
471  for (unsigned_type i = 2 * k - 1; i >= k; i--) //backwards
472  {
473  states[i].make_inf();
474  if (i < arity)
475  free_segments.push(i);
476  }
477 
478  // double the size
479  k *= 2;
480  log_k++;
481 
482  STXXL_VERBOSE1("ext_merger::double_k (after) k=" << k << " log_k=" << log_k << " arity_bound=" << arity_bound << " arity=" << arity << " #free=" << free_segments.size());
483  assert(!free_segments.empty());
484  assert(k <= arity_bound);
485 
486  // recompute loser tree information
487  rebuild_loser_tree();
488  }
489 
490 
491  // compact nonempty segments in the left half of the tree
493  {
494  STXXL_VERBOSE1("ext_merger::compact_tree (before) k=" << k << " log_k=" << log_k << " #free=" << free_segments.size());
495  assert(log_k > 0);
496 
497  // compact all nonempty segments to the left
498 
499  unsigned_type target = 0;
500  for (unsigned_type from = 0; from < k; from++)
501  {
502  if (!is_segment_empty(from))
503  {
504  assert(is_segment_allocated(from));
505  if (from != target)
506  {
507  assert(!is_segment_allocated(target));
508  states[target].swap(states[from]);
509  }
510  ++target;
511  }
512  }
513 
514  // half degree as often as possible
515  while (k > 1 && target <= (k / 2))
516  {
517  k /= 2;
518  log_k--;
519  }
520 
521  // overwrite garbage and compact the stack of free segment indices
522  free_segments.clear(); // none free
523  for ( ; target < k; target++)
524  {
525  assert(!is_segment_allocated(target));
526  states[target].make_inf();
527  if (target < arity)
528  free_segments.push(target);
529  }
530 
531  STXXL_VERBOSE1("ext_merger::compact_tree (after) k=" << k << " log_k=" << log_k << " #free=" << free_segments.size());
532  assert(k > 0);
533 
534  // recompute loser tree information
535  rebuild_loser_tree();
536  }
537 
538 
539 #if 0
540  void swap(ext_merger& obj)
541  {
542  std::swap(cmp, obj.cmp);
543  std::swap(free_segments, obj.free_segments);
544  std::swap(size_, obj.size_);
545  std::swap(log_k, obj.log_k);
546  std::swap(k, obj.k);
547  swap_1D_arrays(entry, obj.entry, arity_bound);
548  swap_1D_arrays(states, obj.states, arity_bound);
549 
550  // std::swap(pool,obj.pool);
551  }
552 #endif
553 
554 public:
555  unsigned_type mem_cons() const // only rough estimation
556  {
557  return (STXXL_MIN<unsigned_type>(arity + 1, arity_bound) * block_type::raw_size);
558  }
559 
560  // delete the (length = end-begin) smallest elements and write them to [begin..end)
561  // empty segments are deallocated
562  // requires:
563  // - there are at least length elements
564  // - segments are ended by sentinels
565  template <class OutputIterator>
566  void multi_merge(OutputIterator begin, OutputIterator end)
567  {
568  size_type length = end - begin;
569 
570  STXXL_VERBOSE1("ext_merger::multi_merge from " << k << " sequence(s), length = " << length);
571 
572  if (length == 0)
573  return;
574 
575  assert(k > 0);
576  assert(length <= size_);
577 
578  //This is the place to make statistics about external multi_merge calls.
579 
580 #if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL
581  typedef stxxl::int64 diff_type;
582  typedef std::pair<typename block_type::iterator, typename block_type::iterator> sequence;
583 
584  std::vector<sequence> seqs;
585  std::vector<unsigned_type> orig_seq_index;
586 
587  Cmp_ cmp;
589 
590  for (unsigned_type i = 0; i < k; ++i) //initialize sequences
591  {
592  if (states[i].current == states[i].block->size || is_sentinel(*states[i]))
593  continue;
594 
595  seqs.push_back(std::make_pair(states[i].block->begin() + states[i].current, states[i].block->end()));
596  orig_seq_index.push_back(i);
597 
598 #if STXXL_CHECK_ORDER_IN_SORTS
599  if (!is_sentinel(*seqs.back().first) && !stxxl::is_sorted(seqs.back().first, seqs.back().second, inv_cmp))
600  {
601  STXXL_VERBOSE1("length " << i << " " << (seqs.back().second - seqs.back().first));
602  for (value_type* v = seqs.back().first + 1; v < seqs.back().second; ++v)
603  {
604  if (inv_cmp(*v, *(v - 1)))
605  {
606  STXXL_VERBOSE1("Error at position " << i << "/" << (v - seqs.back().first - 1) << "/" << (v - seqs.back().first) << " " << *(v - 1) << " " << *v);
607  }
608  if (is_sentinel(*v))
609  {
610  STXXL_VERBOSE1("Wrong sentinel at position " << (v - seqs.back().first));
611  }
612  }
613  assert(false);
614  }
615 #endif
616 
617  //Hint first non-internal (actually second) block of this sequence.
618  if (states[i].bids != NULL && !states[i].bids->empty())
619  pool->hint(states[i].bids->front());
620  }
621 
622  assert(seqs.size() > 0);
623 
624 #if STXXL_CHECK_ORDER_IN_SORTS
625  value_type last_elem;
626 #endif
627 
628  diff_type rest = length; //elements still to merge for this output block
629 
630  while (rest > 0)
631  {
632  value_type min_last = cmp.min_value(); // minimum of the sequences' last elements
633  diff_type total_size = 0;
634 
635  for (unsigned_type i = 0; i < seqs.size(); ++i)
636  {
637  diff_type seq_i_size = seqs[i].second - seqs[i].first;
638  if (seq_i_size > 0)
639  {
640  total_size += seq_i_size;
641  if (inv_cmp(*(seqs[i].second - 1), min_last))
642  min_last = *(seqs[i].second - 1);
643 
644  STXXL_VERBOSE1("front block of seq " << i << ": front=" << *(seqs[i].first) << " back=" << *(seqs[i].second - 1) << " len=" << seq_i_size);
645  } else {
646  STXXL_VERBOSE1("front block of seq " << i << ": empty");
647  }
648  }
649 
650  assert(total_size > 0);
651  assert(!is_sentinel(min_last));
652 
653  STXXL_VERBOSE1("min_last " << min_last << " total size " << total_size << " num_seq " << seqs.size());
654 
655  diff_type less_equal_than_min_last = 0;
656  //locate this element in all sequences
657  for (unsigned_type i = 0; i < seqs.size(); ++i)
658  {
659  //assert(seqs[i].first < seqs[i].second);
660 
661  typename block_type::iterator position =
662  std::upper_bound(seqs[i].first, seqs[i].second, min_last, inv_cmp);
663 
664  //no element larger than min_last is merged
665 
666  STXXL_VERBOSE1("seq " << i << ": " << (position - seqs[i].first) << " greater equal than " << min_last);
667 
668  less_equal_than_min_last += (position - seqs[i].first);
669  }
670 
671  diff_type output_size = STXXL_MIN(less_equal_than_min_last, rest); // at most rest elements
672 
673  STXXL_VERBOSE1("output_size=" << output_size << " = min(leq_t_ml=" << less_equal_than_min_last << ", rest=" << rest << ")");
674 
675  assert(output_size > 0);
676 
677  //main call
678 
679  begin = parallel::multiway_merge(seqs.begin(), seqs.end(), begin, inv_cmp, output_size); //sequence iterators are progressed appropriately
680 
681  rest -= output_size;
682  size_ -= output_size;
683 
684  for (unsigned_type i = 0; i < seqs.size(); ++i)
685  {
686  sequence_state& state = states[orig_seq_index[i]];
687 
688  state.current = seqs[i].first - state.block->begin();
689 
690  assert(seqs[i].first <= seqs[i].second);
691 
692  if (seqs[i].first == seqs[i].second) //has run empty
693  {
694  assert(state.current == state.block->size);
695  if (state.bids == NULL || state.bids->empty()) // if there is no next block
696  {
697  STXXL_VERBOSE1("seq " << i << ": ext_merger::multi_merge(...) it was the last block in the sequence ");
698  state.make_inf();
699  }
700  else
701  {
702 #if STXXL_CHECK_ORDER_IN_SORTS
703  last_elem = *(seqs[i].second - 1);
704 #endif
705  STXXL_VERBOSE1("seq " << i << ": ext_merger::multi_merge(...) there is another block ");
706  bid_type bid = state.bids->front();
707  state.bids->pop_front();
708  pool->hint(bid);
709  if (!(state.bids->empty()))
710  {
711  STXXL_VERBOSE2("seq " << i << ": ext_merger::multi_merge(...) more blocks exist, hinting the next");
712  pool->hint(state.bids->front());
713  }
714  pool->read(state.block, bid)->wait();
715  STXXL_VERBOSE1("seq " << i << ": first element of read block " << bid << " " << *(state.block->begin()) << " cached in " << state.block);
716  if (!(state.bids->empty()))
717  pool->hint(state.bids->front()); // re-hint, reading might have made a block free
718  state.current = 0;
719  seqs[i] = std::make_pair(state.block->begin() + state.current, state.block->end());
720  block_manager::get_instance()->delete_block(bid);
721 
722 #if STXXL_CHECK_ORDER_IN_SORTS
723  STXXL_VERBOSE1("before " << last_elem << " after " << *seqs[i].first << " newly loaded block " << bid);
724  if (!stxxl::is_sorted(seqs[i].first, seqs[i].second, inv_cmp))
725  {
726  STXXL_VERBOSE1("length " << i << " " << (seqs[i].second - seqs[i].first));
727  for (value_type* v = seqs[i].first + 1; v < seqs[i].second; ++v)
728  {
729  if (inv_cmp(*v, *(v - 1)))
730  {
731  STXXL_VERBOSE1("Error at position " << i << "/" << (v - seqs[i].first - 1) << "/" << (v - seqs[i].first) << " " << *(v - 1) << " " << *v);
732  }
733  if (is_sentinel(*v))
734  {
735  STXXL_VERBOSE1("Wrong sentinel at position " << (v - seqs[i].first));
736  }
737  }
738  assert(false);
739  }
740 #endif
741  }
742  }
743  }
744  } //while (rest > 1)
745 
746  for (unsigned_type i = 0; i < seqs.size(); ++i)
747  {
748  unsigned_type seg = orig_seq_index[i];
749  if (is_segment_empty(seg))
750  {
751  STXXL_VERBOSE1("deallocated " << seg);
752  deallocate_segment(seg);
753  }
754  }
755 
756 #else // STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL
757 
758  //Hint first non-internal (actually second) block of each sequence.
759  for (unsigned_type i = 0; i < k; ++i)
760  {
761  if (states[i].bids != NULL && !states[i].bids->empty())
762  pool->hint(states[i].bids->front());
763  }
764 
765  switch (log_k) {
766  case 0:
767  assert(k == 1);
768  assert(entry[0].index == 0);
769  assert(free_segments.empty());
770  //memcpy(target, states[0], length * sizeof(value_type));
771  //std::copy(states[0],states[0]+length,target);
772  for (size_type i = 0; i < length; ++i, ++(states[0]), ++begin)
773  *begin = *(states[0]);
774 
775  entry[0].key = **states;
776  if (is_segment_empty(0))
777  deallocate_segment(0);
778 
779  break;
780  case 1:
781  assert(k == 2);
782  merge_iterator(states[0], states[1], begin, length, cmp);
783  rebuild_loser_tree();
784  if (is_segment_empty(0) && is_segment_allocated(0))
785  deallocate_segment(0);
786 
787  if (is_segment_empty(1) && is_segment_allocated(1))
788  deallocate_segment(1);
789 
790  break;
791  case 2:
792  assert(k == 4);
793  if (is_segment_empty(3))
794  merge3_iterator(states[0], states[1], states[2], begin, length, cmp);
795  else
796  merge4_iterator(states[0], states[1], states[2], states[3], begin, length, cmp);
797  rebuild_loser_tree();
798  if (is_segment_empty(0) && is_segment_allocated(0))
799  deallocate_segment(0);
800 
801  if (is_segment_empty(1) && is_segment_allocated(1))
802  deallocate_segment(1);
803 
804  if (is_segment_empty(2) && is_segment_allocated(2))
805  deallocate_segment(2);
806 
807  if (is_segment_empty(3) && is_segment_allocated(3))
808  deallocate_segment(3);
809 
810  break;
811  case 3: multi_merge_f<OutputIterator, 3>(begin, end);
812  break;
813  case 4: multi_merge_f<OutputIterator, 4>(begin, end);
814  break;
815  case 5: multi_merge_f<OutputIterator, 5>(begin, end);
816  break;
817  case 6: multi_merge_f<OutputIterator, 6>(begin, end);
818  break;
819  case 7: multi_merge_f<OutputIterator, 7>(begin, end);
820  break;
821  case 8: multi_merge_f<OutputIterator, 8>(begin, end);
822  break;
823  case 9: multi_merge_f<OutputIterator, 9>(begin, end);
824  break;
825  case 10: multi_merge_f<OutputIterator, 10>(begin, end);
826  break;
827  default: multi_merge_k(begin, end);
828  break;
829  }
830 
831  size_ -= length;
832 #endif
833 
834  // compact tree if it got considerably smaller
835  {
836  const unsigned_type num_segments_used = std::min<unsigned_type>(arity, k) - free_segments.size();
837  const unsigned_type num_segments_trigger = k - (3 * k / 5);
838  // using k/2 would be worst case inefficient (for large k)
839  // for k \in {2, 4, 8} the trigger is k/2 which is good
840  // because we have special mergers for k \in {1, 2, 4}
841  // there is also a special 3-way-merger, that will be
842  // triggered if k == 4 && is_segment_empty(3)
843  STXXL_VERBOSE3("ext_merger compact? k=" << k << " #used=" << num_segments_used
844  << " <= #trigger=" << num_segments_trigger << " ==> "
845  << ((k > 1 && num_segments_used <= num_segments_trigger) ? "yes" : "no ")
846  << " || "
847  << ((k == 4 && !free_segments.empty() && !is_segment_empty(3)) ? "yes" : "no ")
848  << " #free=" << free_segments.size());
849  if (k > 1 && ((num_segments_used <= num_segments_trigger) ||
850  (k == 4 && !free_segments.empty() && !is_segment_empty(3))))
851  {
852  compact_tree();
853  }
854  }
855  }
856 
857 private:
858 #if STXXL_PQ_EXTERNAL_LOSER_TREE
859  // multi-merge for arbitrary K
860  template <class OutputIterator>
861  void multi_merge_k(OutputIterator begin, OutputIterator end)
862  {
863  Entry* current_pos;
864  value_type current_key;
865  unsigned_type current_index; // leaf pointed to by current entry
866  unsigned_type kReg = k;
867  OutputIterator done = end;
868  OutputIterator target = begin;
869  unsigned_type winner_index = entry[0].index;
870  value_type winner_key = entry[0].key;
871 
872  while (target != done)
873  {
874  // write result
875  *target = *(states[winner_index]);
876 
877  // advance winner segment
878  ++(states[winner_index]);
879 
880  winner_key = *(states[winner_index]);
881 
882  // remove winner segment if empty now
883  if (is_sentinel(winner_key)) //
884  deallocate_segment(winner_index);
885 
886 
887  // go up the entry-tree
888  for (unsigned_type i = (winner_index + kReg) >> 1; i > 0; i >>= 1)
889  {
890  current_pos = entry + i;
891  current_key = current_pos->key;
892  if (cmp(winner_key, current_key))
893  {
894  current_index = current_pos->index;
895  current_pos->key = winner_key;
896  current_pos->index = winner_index;
897  winner_key = current_key;
898  winner_index = current_index;
899  }
900  }
901 
902  ++target;
903  }
904  entry[0].index = winner_index;
905  entry[0].key = winner_key;
906  }
907 
908  template <class OutputIterator, int LogK>
909  void multi_merge_f(OutputIterator begin, OutputIterator end)
910  {
911  OutputIterator done = end;
912  OutputIterator target = begin;
913  unsigned_type winner_index = entry[0].index;
914  Entry* reg_entry = entry;
915  sequence_state* reg_states = states;
916  value_type winner_key = entry[0].key;
917 
918  assert(log_k >= LogK);
919  while (target != done)
920  {
921  // write result
922  *target = *(reg_states[winner_index]);
923 
924  // advance winner segment
925  ++(reg_states[winner_index]);
926 
927  winner_key = *(reg_states[winner_index]);
928 
929  // remove winner segment if empty now
930  if (is_sentinel(winner_key))
931  deallocate_segment(winner_index);
932 
933 
934  ++target;
935 
936  // update loser tree
937 #define TreeStep(L) \
938  if (1 << LogK >= 1 << L) { \
939  Entry* pos ## L = reg_entry + ((winner_index + (1 << LogK)) >> (((int(LogK - L) + 1) >= 0) ? ((LogK - L) + 1) : 0)); \
940  value_type key ## L = pos ## L->key; \
941  if (cmp(winner_key, key ## L)) { \
942  unsigned_type index ## L = pos ## L->index; \
943  pos ## L->key = winner_key; \
944  pos ## L->index = winner_index; \
945  winner_key = key ## L; \
946  winner_index = index ## L; \
947  } \
948  }
949  TreeStep(10);
950  TreeStep(9);
951  TreeStep(8);
952  TreeStep(7);
953  TreeStep(6);
954  TreeStep(5);
955  TreeStep(4);
956  TreeStep(3);
957  TreeStep(2);
958  TreeStep(1);
959 #undef TreeStep
960  }
961  reg_entry[0].index = winner_index;
962  reg_entry[0].key = winner_key;
963  }
964 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE
965 
966 public:
967  bool is_space_available() const // for new segment
968  {
969  return k < arity || !free_segments.empty();
970  }
971 
972  // insert segment beginning at target
973  // require: is_space_available() == 1
974  template <class Merger>
975  void insert_segment(Merger& another_merger, size_type segment_size)
976  {
977  STXXL_VERBOSE1("ext_merger::insert_segment(merger,...)" << this);
978 
979  if (segment_size > 0)
980  {
981  // get a free slot
982  if (free_segments.empty())
983  { // tree is too small
984  double_k();
985  }
986  assert(!free_segments.empty());
987  unsigned_type free_slot = free_segments.top();
988  free_segments.pop();
989 
990 
991  // link new segment
992  assert(segment_size);
993  unsigned_type nblocks = segment_size / block_type::size;
994  //assert(nblocks); // at least one block
995  STXXL_VERBOSE1("ext_merger::insert_segment nblocks=" << nblocks);
996  if (nblocks == 0)
997  {
998  STXXL_VERBOSE1("ext_merger::insert_segment(merger,...) WARNING: inserting a segment with " <<
999  nblocks << " blocks");
1000  STXXL_VERBOSE1("THIS IS INEFFICIENT: TRY TO CHANGE PRIORITY QUEUE PARAMETERS");
1001  }
1002  unsigned_type first_size = segment_size % block_type::size;
1003  if (first_size == 0)
1004  {
1005  first_size = block_type::size;
1006  --nblocks;
1007  }
1008  block_manager* bm = block_manager::get_instance();
1009  std::list<bid_type>* bids = new std::list<bid_type>(nblocks);
1010  bm->new_blocks(alloc_strategy(), bids->begin(), bids->end());
1011  block_type* first_block = new block_type;
1012 
1013  another_merger.multi_merge(
1014  first_block->begin() + (block_type::size - first_size),
1015  first_block->end());
1016 
1017  STXXL_VERBOSE1("last element of first block " << *(first_block->end() - 1));
1018  assert(!cmp(*(first_block->begin() + (block_type::size - first_size)), *(first_block->end() - 1)));
1019 
1020  assert(pool->size_write() > 0);
1021 
1022  for (typename std::list<bid_type>::iterator curbid = bids->begin(); curbid != bids->end(); ++curbid)
1023  {
1024  block_type* b = pool->steal();
1025  another_merger.multi_merge(b->begin(), b->end());
1026  STXXL_VERBOSE1("first element of following block " << *curbid << " " << *(b->begin()));
1027  STXXL_VERBOSE1("last element of following block " << *curbid << " " << *(b->end() - 1));
1028  assert(!cmp(*(b->begin()), *(b->end() - 1)));
1029  pool->write(b, *curbid);
1030  STXXL_VERBOSE1("written to block " << *curbid << " cached in " << b);
1031  }
1032 
1033  insert_segment(bids, first_block, first_size, free_slot);
1034 
1035  size_ += segment_size;
1036 
1037 #if STXXL_PQ_EXTERNAL_LOSER_TREE
1038  // propagate new information up the tree
1039  value_type dummyKey;
1040  unsigned_type dummyIndex;
1041  unsigned_type dummyMask;
1042  update_on_insert((free_slot + k) >> 1, *(states[free_slot]), free_slot,
1043  &dummyKey, &dummyIndex, &dummyMask);
1044 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE
1045  }
1046  else
1047  {
1048  // deallocate memory ?
1049  STXXL_VERBOSE1("Merged segment with zero size.");
1050  }
1051  }
1052 
1053  size_type size() const { return size_; }
1054 
1055 protected:
1056  /*!
1057  \param bidlist list of blocks to insert
1058  \param first_block the first block of the sequence, before bidlist
1059  \param first_size number of elements in the first block
1060  \param slot slot to insert into
1061  */
1062  void insert_segment(std::list<bid_type>* bidlist, block_type* first_block,
1063  unsigned_type first_size, unsigned_type slot)
1064  {
1065  STXXL_VERBOSE1("ext_merger::insert_segment(bidlist,...) " << this << " " << bidlist->size() << " " << slot);
1066  assert(!is_segment_allocated(slot));
1067  assert(first_size > 0);
1068 
1069  sequence_state& new_sequence = states[slot];
1070  new_sequence.current = block_type::size - first_size;
1071  std::swap(new_sequence.block, first_block);
1072  delete first_block;
1073  std::swap(new_sequence.bids, bidlist);
1074  if (bidlist) // the old list
1075  {
1076  assert(bidlist->empty());
1077  delete bidlist;
1078  }
1079  new_sequence.allocated = true;
1080  assert(is_segment_allocated(slot));
1081  }
1082 
1083  // free an empty segment .
1085  {
1086  STXXL_VERBOSE1("ext_merger::deallocate_segment() deleting segment " << slot << " allocated=" << int(is_segment_allocated(slot)));
1087  assert(is_segment_allocated(slot));
1088  states[slot].allocated = false;
1089  states[slot].make_inf();
1090 
1091  // push on the stack of free segment indices
1092  free_segments.push(slot);
1093  }
1094 
1095  // is this segment empty ?
1097  {
1098  return is_sentinel(*(states[slot]));
1099  }
1100 
1101  // Is this segment allocated? Otherwise it's empty,
1102  // already on the stack of free segment indices and can be reused.
1104  {
1105  return states[slot].allocated;
1106  }
1107 }; // class ext_merger
1108 
1109 } // namespace priority_queue_local
1110 
1111 //! \}
1112 
1114 
1115 #endif // !STXXL_CONTAINERS_PQ_EXT_MERGER_HEADER
1116 // vim: et:ts=4:sw=4
const Tp & STXXL_MIN(const Tp &a, const Tp &b)
Definition: utils.h:147
Block manager class.
Definition: block_manager.h:63
bool is_sorted(_ForwardIter __first, _ForwardIter __last)
Definition: is_sorted.h:54
External merger, based on the loser tree data structure.
long long int int64
Definition: types.h:40
void swap_1D_arrays(T *a, T *b, unsigned_type size)
Definition: utils.h:247
unsigned long long int uint64
Definition: types.h:41
bool is_segment_allocated(unsigned_type slot) const
bool hint(bid_type bid)
Gives a hint for prefetching a block.
Inverts the order of a comparison functor by swapping its arguments.
Definition: pq_helpers.h:189
std::pair< Iterator, Iterator > pair
Definition: pq_ext_merger.h:34
void insert_segment(std::list< bid_type > *bidlist, block_type *first_block, unsigned_type first_size, unsigned_type slot)
#define STXXL_DEFAULT_ALLOC_STRATEGY
Definition: block_alloc.h:259
std::iterator_traits< iterator >::value_type value_type
Definition: pq_ext_merger.h:39
void merge4_iterator(InputIterator &source0, InputIterator &source1, InputIterator &source2, InputIterator &source3, OutputIterator target, unsigned_type length, CompareType &cmp)
Definition: pq_mergers.h:141
request_ptr read(block_type *&block, bid_type bid)
Reads block. If this block is cached block is not read but passed from the cache. ...
short_sequence(Iterator first, Iterator last, origin_type origin)
Definition: pq_ext_merger.h:49
#define STXXL_VERBOSE2(x)
Definition: verbose.h:107
void insert_segment(Merger &another_merger, size_type segment_size)
void merge_iterator(InputIterator &source0, InputIterator &source1, OutputIterator target, unsigned_type length, CompareType &cmp)
Definition: pq_mergers.h:41
internal_bounded_stack< unsigned_type, arity > free_segments
uint_pair & operator++()
prefix increment operator (directly manipulates the integer parts)
Definition: uint_types.h:166
sequence_state states[arity_bound]
void delete_blocks(const BIDIteratorClass &bidbegin, const BIDIteratorClass &bidend)
Deallocates blocks.
read_write_pool< block_type > pool_type
virtual void wait(bool measure_time=true)=0
Suspends calling thread until completion of the request.
External sequence or deque container without random access. Introduction to sequence container: se...
Definition: sequence.h:63
void deallocate_segment(unsigned_type slot)
#define STXXL_VERBOSE3(x)
Definition: verbose.h:113
Implements dynamically resizable buffered writing and prefetched reading pool.
request_ptr write(block_type *&block, bid_type bid)
Passes a block to the pool for writing.
void merge3_iterator(InputIterator &source0, InputIterator &source1, InputIterator &source2, OutputIterator target, unsigned_type length, CompareType &cmp)
Definition: pq_mergers.h:73
#define STXXL_BEGIN_NAMESPACE
Definition: namespace.h:16
#define STXXL_VERBOSE1(x)
Definition: verbose.h:99
block_type * steal()
Take out a block from the pool.
void new_blocks(const DiskAssignFunctor &functor, BIDIteratorClass bidbegin, BIDIteratorClass bidend, unsigned_type offset=0)
Allocates new blocks.
#define STXXL_ERRMSG(x)
Definition: verbose.h:79
choose_int_types< my_pointer_size >::unsigned_type unsigned_type
Definition: types.h:67
#define TreeStep(L)
void multi_merge(OutputIterator begin, OutputIterator end)
std::iterator_traits< iterator >::difference_type size_type
Definition: pq_ext_merger.h:40
bool not_sentinel(const value_type &a) const
bool is_sentinel(const value_type &a) const
size_type size_write() const
Returns number of blocks owned by the write_pool.
#define STXXL_END_NAMESPACE
Definition: namespace.h:17
bool is_segment_empty(unsigned_type slot) const