Stxxl
1.4.0
|
00001 /*************************************************************************** 00002 * include/stxxl/bits/containers/pq_ext_merger.h 00003 * 00004 * Part of the STXXL. See http://stxxl.sourceforge.net 00005 * 00006 * Copyright (C) 1999 Peter Sanders <sanders@mpi-sb.mpg.de> 00007 * Copyright (C) 2003, 2004, 2007 Roman Dementiev <dementiev@mpi-sb.mpg.de> 00008 * Copyright (C) 2007-2009 Johannes Singler <singler@ira.uka.de> 00009 * Copyright (C) 2007-2010 Andreas Beckmann <beckmann@cs.uni-frankfurt.de> 00010 * 00011 * Distributed under the Boost Software License, Version 1.0. 00012 * (See accompanying file LICENSE_1_0.txt or copy at 00013 * http://www.boost.org/LICENSE_1_0.txt) 00014 **************************************************************************/ 00015 00016 #ifndef STXXL_PQ_EXT_MERGER_HEADER 00017 #define STXXL_PQ_EXT_MERGER_HEADER 00018 00019 #include <stxxl/bits/mng/mng.h> 00020 00021 __STXXL_BEGIN_NAMESPACE 00022 00023 //! \addtogroup stlcontinternals 00024 //! 00025 //! \{ 00026 00027 /*! \internal 00028 */ 00029 namespace priority_queue_local 00030 { 00031 template <typename Iterator> 00032 class short_sequence : public std::pair<Iterator, Iterator> 00033 { 00034 typedef std::pair<Iterator, Iterator> pair; 00035 00036 public: 00037 typedef Iterator iterator; 00038 typedef const iterator const_iterator; 00039 typedef typename std::iterator_traits<iterator>::value_type value_type; 00040 typedef typename std::iterator_traits<iterator>::difference_type size_type; 00041 typedef value_type & reference; 00042 typedef const value_type & const_reference; 00043 typedef unsigned_type origin_type; 00044 00045 private: 00046 origin_type m_origin; 00047 00048 public: 00049 short_sequence(Iterator first, Iterator last, origin_type origin) : 00050 pair(first, last), m_origin(origin) 00051 { } 00052 00053 iterator begin() 00054 { 00055 return this->first; 00056 } 00057 00058 const_iterator begin() const 00059 { 00060 return this->first; 00061 } 00062 00063 const_iterator cbegin() const 00064 { 00065 return begin(); 00066 } 00067 00068 iterator end() 00069 { 00070 return this->second; 00071 } 00072 00073 const_iterator end() const 00074 { 00075 return this->second; 00076 } 00077 00078 const_iterator cend() const 00079 { 00080 return end(); 00081 } 00082 00083 reference front() 00084 { 00085 return *begin(); 00086 } 00087 00088 const_reference front() const 00089 { 00090 return *begin(); 00091 } 00092 00093 reference back() 00094 { 00095 return *(end() - 1); 00096 } 00097 00098 const_reference back() const 00099 { 00100 return *(end() - 1); 00101 } 00102 00103 size_type size() const 00104 { 00105 return end() - begin(); 00106 } 00107 00108 bool empty() const 00109 { 00110 return size() == 0; 00111 } 00112 00113 origin_type origin() const 00114 { 00115 return m_origin; 00116 } 00117 }; 00118 00119 /** 00120 *! \brief External merger, based on the loser tree data structure. 00121 *! \param Arity_ maximum arity of merger, does not need to be a power of 2 00122 */ 00123 template <class BlockType_, 00124 class Cmp_, 00125 unsigned Arity_, 00126 class AllocStr_ = STXXL_DEFAULT_ALLOC_STRATEGY> 00127 class ext_merger : private noncopyable 00128 { 00129 public: 00130 typedef stxxl::uint64 size_type; 00131 typedef BlockType_ block_type; 00132 typedef typename block_type::bid_type bid_type; 00133 typedef typename block_type::value_type value_type; 00134 typedef Cmp_ comparator_type; 00135 typedef AllocStr_ alloc_strategy; 00136 typedef read_write_pool<block_type> pool_type; 00137 00138 // arity_bound / 2 < arity <= arity_bound 00139 enum { arity = Arity_, arity_bound = 1UL << (LOG2<Arity_>::ceil) }; 00140 00141 protected: 00142 comparator_type cmp; 00143 00144 bool is_sentinel(const value_type & a) const 00145 { 00146 return !(cmp(cmp.min_value(), a)); // a <= cmp.min_value() 00147 } 00148 00149 bool not_sentinel(const value_type & a) const 00150 { 00151 return cmp(cmp.min_value(), a); // a > cmp.min_value() 00152 } 00153 00154 struct sequence_state : private noncopyable 00155 { 00156 block_type * block; //current block 00157 unsigned_type current; //current index in current block 00158 std::list<bid_type> * bids; //list of blocks forming this sequence 00159 comparator_type cmp; 00160 ext_merger * merger; 00161 bool allocated; 00162 00163 //! \returns current element 00164 const value_type & operator * () const 00165 { 00166 return (*block)[current]; 00167 } 00168 00169 sequence_state() : bids(NULL), allocated(false) 00170 { } 00171 00172 ~sequence_state() 00173 { 00174 STXXL_VERBOSE2("ext_merger sequence_state::~sequence_state()"); 00175 if (bids != NULL) 00176 { 00177 block_manager * bm = block_manager::get_instance(); 00178 bm->delete_blocks(bids->begin(), bids->end()); 00179 delete bids; 00180 } 00181 } 00182 00183 void make_inf() 00184 { 00185 current = 0; 00186 (*block)[0] = cmp.min_value(); 00187 } 00188 00189 bool is_sentinel(const value_type & a) const 00190 { 00191 return !(cmp(cmp.min_value(), a)); 00192 } 00193 00194 bool not_sentinel(const value_type & a) const 00195 { 00196 return cmp(cmp.min_value(), a); 00197 } 00198 00199 void swap(sequence_state & obj) 00200 { 00201 if (&obj != this) 00202 { 00203 std::swap(current, obj.current); 00204 std::swap(block, obj.block); 00205 std::swap(bids, obj.bids); 00206 assert(merger == obj.merger); 00207 std::swap(allocated, obj.allocated); 00208 } 00209 } 00210 00211 sequence_state & operator ++ () 00212 { 00213 assert(not_sentinel((*block)[current])); 00214 assert(current < block->size); 00215 00216 ++current; 00217 00218 if (current == block->size) 00219 { 00220 STXXL_VERBOSE2("ext_merger sequence_state operator++ crossing block border "); 00221 // go to the next block 00222 assert(bids != NULL); 00223 if (bids->empty()) // if there is no next block 00224 { 00225 STXXL_VERBOSE2("ext_merger sequence_state operator++ it was the last block in the sequence "); 00226 delete bids; 00227 bids = NULL; 00228 make_inf(); 00229 } 00230 else 00231 { 00232 STXXL_VERBOSE2("ext_merger sequence_state operator++ there is another block "); 00233 bid_type bid = bids->front(); 00234 bids->pop_front(); 00235 merger->pool->hint(bid); 00236 if (!(bids->empty())) 00237 { 00238 STXXL_VERBOSE2("ext_merger sequence_state operator++ more blocks exist in a sequence, hinting the next"); 00239 merger->pool->hint(bids->front()); 00240 } 00241 merger->pool->read(block, bid)->wait(); 00242 STXXL_VERBOSE2("first element of read block " << bid << " " << *(block->begin()) << " cached in " << block); 00243 if (!(bids->empty())) 00244 merger->pool->hint(bids->front()); // re-hint, reading might have made a block free 00245 block_manager::get_instance()->delete_block(bid); 00246 current = 0; 00247 } 00248 } 00249 return *this; 00250 } 00251 }; 00252 00253 00254 #if STXXL_PQ_EXTERNAL_LOSER_TREE 00255 struct Entry 00256 { 00257 value_type key; // key of loser element (winner for 0) 00258 unsigned_type index; // the number of the losing segment 00259 }; 00260 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE 00261 00262 size_type size_; // total number of elements stored 00263 unsigned_type log_k; // log of current tree size 00264 unsigned_type k; // invariant (k == 1 << log_k), always a power of 2 00265 // only entries 0 .. arity-1 may hold actual sequences, the other 00266 // entries arity .. arity_bound-1 are sentinels to make the size of the tree 00267 // a power of 2 always 00268 00269 // stack of empty segment indices 00270 internal_bounded_stack<unsigned_type, arity> free_segments; 00271 00272 #if STXXL_PQ_EXTERNAL_LOSER_TREE 00273 // upper levels of loser trees 00274 // entry[0] contains the winner info 00275 Entry entry[arity_bound]; 00276 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE 00277 00278 // leaf information 00279 // note that Knuth uses indices k..k-1 00280 // while we use 0..k-1 00281 sequence_state states[arity_bound]; // sequence including current position, dereference gives current element 00282 00283 pool_type * pool; 00284 00285 block_type * sentinel_block; 00286 00287 public: 00288 ext_merger() : 00289 size_(0), log_k(0), k(1), pool(0) 00290 { 00291 init(); 00292 } 00293 00294 ext_merger(pool_type * pool_) : 00295 size_(0), log_k(0), k(1), 00296 pool(pool_) 00297 { 00298 init(); 00299 } 00300 00301 virtual ~ext_merger() 00302 { 00303 STXXL_VERBOSE1("ext_merger::~ext_merger()"); 00304 for (unsigned_type i = 0; i < arity; ++i) 00305 { 00306 delete states[i].block; 00307 } 00308 delete sentinel_block; 00309 } 00310 00311 void set_pool(pool_type * pool_) 00312 { 00313 pool = pool_; 00314 } 00315 00316 private: 00317 void init() 00318 { 00319 STXXL_VERBOSE2("ext_merger::init()"); 00320 assert(!cmp(cmp.min_value(), cmp.min_value())); // verify strict weak ordering 00321 00322 sentinel_block = NULL; 00323 if (arity < arity_bound) 00324 { 00325 sentinel_block = new block_type; 00326 for (unsigned_type i = 0; i < block_type::size; ++i) 00327 (*sentinel_block)[i] = cmp.min_value(); 00328 if (arity + 1 == arity_bound) { 00329 // same memory consumption, but smaller merge width, better use arity = arity_bound 00330 STXXL_ERRMSG("inefficient PQ parameters for ext_merger: arity + 1 == arity_bound"); 00331 } 00332 } 00333 00334 for (unsigned_type i = 0; i < arity_bound; ++i) 00335 { 00336 states[i].merger = this; 00337 if (i < arity) 00338 states[i].block = new block_type; 00339 else 00340 states[i].block = sentinel_block; 00341 00342 states[i].make_inf(); 00343 } 00344 00345 assert(k == 1); 00346 free_segments.push(0); //total state: one free sequence 00347 00348 rebuild_loser_tree(); 00349 #if STXXL_PQ_EXTERNAL_LOSER_TREE 00350 assert(is_sentinel(*states[entry[0].index])); 00351 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE 00352 } 00353 00354 // rebuild loser tree information from the values in current 00355 void rebuild_loser_tree() 00356 { 00357 #if STXXL_PQ_EXTERNAL_LOSER_TREE 00358 unsigned_type winner = init_winner(1); 00359 entry[0].index = winner; 00360 entry[0].key = *(states[winner]); 00361 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE 00362 } 00363 00364 00365 #if STXXL_PQ_EXTERNAL_LOSER_TREE 00366 // given any values in the leaves this 00367 // routing recomputes upper levels of the tree 00368 // from scratch in linear time 00369 // initialize entry[root].index and the subtree rooted there 00370 // return winner index 00371 unsigned_type init_winner(unsigned_type root) 00372 { 00373 if (root >= k) 00374 { // leaf reached 00375 return root - k; 00376 } 00377 else 00378 { 00379 unsigned_type left = init_winner(2 * root); 00380 unsigned_type right = init_winner(2 * root + 1); 00381 value_type lk = *(states[left]); 00382 value_type rk = *(states[right]); 00383 if (!(cmp(lk, rk))) 00384 { // right subtree looses 00385 entry[root].index = right; 00386 entry[root].key = rk; 00387 return left; 00388 } 00389 else 00390 { 00391 entry[root].index = left; 00392 entry[root].key = lk; 00393 return right; 00394 } 00395 } 00396 } 00397 00398 // first go up the tree all the way to the root 00399 // hand down old winner for the respective subtree 00400 // based on new value, and old winner and loser 00401 // update each node on the path to the root top down. 00402 // This is implemented recursively 00403 void update_on_insert( 00404 unsigned_type node, 00405 const value_type & new_key, 00406 unsigned_type new_index, 00407 value_type * winner_key, 00408 unsigned_type * winner_index, // old winner 00409 unsigned_type * mask) // 1 << (ceil(log KNK) - dist-from-root) 00410 { 00411 if (node == 0) 00412 { // winner part of root 00413 *mask = 1 << (log_k - 1); 00414 *winner_key = entry[0].key; 00415 *winner_index = entry[0].index; 00416 if (cmp(entry[node].key, new_key)) 00417 { 00418 entry[node].key = new_key; 00419 entry[node].index = new_index; 00420 } 00421 } 00422 else 00423 { 00424 update_on_insert(node >> 1, new_key, new_index, winner_key, winner_index, mask); 00425 value_type loserKey = entry[node].key; 00426 unsigned_type loserIndex = entry[node].index; 00427 if ((*winner_index & *mask) != (new_index & *mask)) 00428 { // different subtrees 00429 if (cmp(loserKey, new_key)) 00430 { // new_key will have influence here 00431 if (cmp(*winner_key, new_key)) 00432 { // old winner loses here 00433 entry[node].key = *winner_key; 00434 entry[node].index = *winner_index; 00435 } 00436 else 00437 { // new entry looses here 00438 entry[node].key = new_key; 00439 entry[node].index = new_index; 00440 } 00441 } 00442 *winner_key = loserKey; 00443 *winner_index = loserIndex; 00444 } 00445 // note that nothing needs to be done if 00446 // the winner came from the same subtree 00447 // a) new_key <= winner_key => even more reason for the other tree to lose 00448 // b) new_key > winner_key => the old winner will beat the new 00449 // entry further down the tree 00450 // also the same old winner is handed down the tree 00451 00452 *mask >>= 1; // next level 00453 } 00454 } 00455 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE 00456 00457 // make the tree two times as wide 00458 void double_k() 00459 { 00460 STXXL_VERBOSE1("ext_merger::double_k (before) k=" << k << " log_k=" << log_k << " arity_bound=" << arity_bound << " arity=" << arity << " #free=" << free_segments.size()); 00461 assert(k > 0); 00462 assert(k < arity); 00463 assert(free_segments.empty()); // stack was free (probably not needed) 00464 00465 // make all new entries free 00466 // and push them on the free stack 00467 for (unsigned_type i = 2 * k - 1; i >= k; i--) //backwards 00468 { 00469 states[i].make_inf(); 00470 if (i < arity) 00471 free_segments.push(i); 00472 } 00473 00474 // double the size 00475 k *= 2; 00476 log_k++; 00477 00478 STXXL_VERBOSE1("ext_merger::double_k (after) k=" << k << " log_k=" << log_k << " arity_bound=" << arity_bound << " arity=" << arity << " #free=" << free_segments.size()); 00479 assert(!free_segments.empty()); 00480 00481 // recompute loser tree information 00482 rebuild_loser_tree(); 00483 } 00484 00485 00486 // compact nonempty segments in the left half of the tree 00487 void compact_tree() 00488 { 00489 STXXL_VERBOSE1("ext_merger::compact_tree (before) k=" << k << " log_k=" << log_k << " #free=" << free_segments.size()); 00490 assert(log_k > 0); 00491 00492 // compact all nonempty segments to the left 00493 00494 unsigned_type target = 0; 00495 for (unsigned_type from = 0; from < k; from++) 00496 { 00497 if (!is_segment_empty(from)) 00498 { 00499 assert(is_segment_allocated(from)); 00500 if (from != target) 00501 { 00502 assert(!is_segment_allocated(target)); 00503 states[target].swap(states[from]); 00504 } 00505 ++target; 00506 } 00507 } 00508 00509 // half degree as often as possible 00510 while (k > 1 && target <= (k / 2)) 00511 { 00512 k /= 2; 00513 log_k--; 00514 } 00515 00516 // overwrite garbage and compact the stack of free segment indices 00517 free_segments.clear(); // none free 00518 for ( ; target < k; target++) 00519 { 00520 assert(!is_segment_allocated(target)); 00521 states[target].make_inf(); 00522 if (target < arity) 00523 free_segments.push(target); 00524 } 00525 00526 STXXL_VERBOSE1("ext_merger::compact_tree (after) k=" << k << " log_k=" << log_k << " #free=" << free_segments.size()); 00527 assert(k > 0); 00528 00529 // recompute loser tree information 00530 rebuild_loser_tree(); 00531 } 00532 00533 00534 #if 0 00535 void swap(ext_merger & obj) 00536 { 00537 std::swap(cmp, obj.cmp); 00538 std::swap(free_segments, obj.free_segments); 00539 std::swap(size_, obj.size_); 00540 std::swap(log_k, obj.log_k); 00541 std::swap(k, obj.k); 00542 swap_1D_arrays(entry, obj.entry, arity_bound); 00543 swap_1D_arrays(states, obj.states, arity_bound); 00544 00545 // std::swap(pool,obj.pool); 00546 } 00547 #endif 00548 00549 public: 00550 unsigned_type mem_cons() const // only rough estimation 00551 { 00552 return (STXXL_MIN<unsigned_type>(arity + 1, arity_bound) * block_type::raw_size); 00553 } 00554 00555 // delete the (length = end-begin) smallest elements and write them to [begin..end) 00556 // empty segments are deallocated 00557 // requires: 00558 // - there are at least length elements 00559 // - segments are ended by sentinels 00560 template <class OutputIterator> 00561 void multi_merge(OutputIterator begin, OutputIterator end) 00562 { 00563 size_type length = end - begin; 00564 00565 STXXL_VERBOSE1("ext_merger::multi_merge from " << k << " sequence(s), length = " << length); 00566 00567 if (length == 0) 00568 return; 00569 00570 assert(k > 0); 00571 assert(length <= size_); 00572 00573 //This is the place to make statistics about external multi_merge calls. 00574 00575 #if STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL 00576 typedef stxxl::int64 diff_type; 00577 typedef std::pair<typename block_type::iterator, typename block_type::iterator> sequence; 00578 00579 std::vector<sequence> seqs; 00580 std::vector<unsigned_type> orig_seq_index; 00581 00582 Cmp_ cmp; 00583 priority_queue_local::invert_order<Cmp_, value_type, value_type> inv_cmp(cmp); 00584 00585 for (unsigned_type i = 0; i < k; ++i) //initialize sequences 00586 { 00587 if (states[i].current == states[i].block->size || is_sentinel(*states[i])) 00588 continue; 00589 00590 seqs.push_back(std::make_pair(states[i].block->begin() + states[i].current, states[i].block->end())); 00591 orig_seq_index.push_back(i); 00592 00593 #if STXXL_CHECK_ORDER_IN_SORTS 00594 if (!is_sentinel(*seqs.back().first) && !stxxl::is_sorted(seqs.back().first, seqs.back().second, inv_cmp)) 00595 { 00596 STXXL_VERBOSE1("length " << i << " " << (seqs.back().second - seqs.back().first)); 00597 for (value_type * v = seqs.back().first + 1; v < seqs.back().second; ++v) 00598 { 00599 if (inv_cmp(*v, *(v - 1))) 00600 { 00601 STXXL_VERBOSE1("Error at position " << i << "/" << (v - seqs.back().first - 1) << "/" << (v - seqs.back().first) << " " << *(v - 1) << " " << *v); 00602 } 00603 if (is_sentinel(*v)) 00604 { 00605 STXXL_VERBOSE1("Wrong sentinel at position " << (v - seqs.back().first)); 00606 } 00607 } 00608 assert(false); 00609 } 00610 #endif 00611 00612 //Hint first non-internal (actually second) block of this sequence. 00613 if (states[i].bids != NULL && !states[i].bids->empty()) 00614 pool->hint(states[i].bids->front()); 00615 } 00616 00617 assert(seqs.size() > 0); 00618 00619 #if STXXL_CHECK_ORDER_IN_SORTS 00620 value_type last_elem; 00621 #endif 00622 00623 diff_type rest = length; //elements still to merge for this output block 00624 00625 while (rest > 0) 00626 { 00627 value_type min_last = cmp.min_value(); // minimum of the sequences' last elements 00628 diff_type total_size = 0; 00629 00630 for (unsigned_type i = 0; i < seqs.size(); ++i) 00631 { 00632 diff_type seq_i_size = seqs[i].second - seqs[i].first; 00633 if (seq_i_size > 0) 00634 { 00635 total_size += seq_i_size; 00636 if (inv_cmp(*(seqs[i].second - 1), min_last)) 00637 min_last = *(seqs[i].second - 1); 00638 00639 STXXL_VERBOSE1("front block of seq " << i << ": front=" << *(seqs[i].first) << " back=" << *(seqs[i].second - 1) << " len=" << seq_i_size); 00640 } else { 00641 STXXL_VERBOSE1("front block of seq " << i << ": empty"); 00642 } 00643 } 00644 00645 assert(total_size > 0); 00646 assert(!is_sentinel(min_last)); 00647 00648 STXXL_VERBOSE1("min_last " << min_last << " total size " << total_size << " num_seq " << seqs.size()); 00649 00650 diff_type less_equal_than_min_last = 0; 00651 //locate this element in all sequences 00652 for (unsigned_type i = 0; i < seqs.size(); ++i) 00653 { 00654 //assert(seqs[i].first < seqs[i].second); 00655 00656 typename block_type::iterator position = 00657 std::upper_bound(seqs[i].first, seqs[i].second, min_last, inv_cmp); 00658 00659 //no element larger than min_last is merged 00660 00661 STXXL_VERBOSE1("seq " << i << ": " << (position - seqs[i].first) << " greater equal than " << min_last); 00662 00663 less_equal_than_min_last += (position - seqs[i].first); 00664 } 00665 00666 diff_type output_size = STXXL_MIN(less_equal_than_min_last, rest); // at most rest elements 00667 00668 STXXL_VERBOSE1("output_size=" << output_size << " = min(leq_t_ml=" << less_equal_than_min_last << ", rest=" << rest << ")"); 00669 00670 assert(output_size > 0); 00671 00672 //main call 00673 00674 begin = parallel::multiway_merge(seqs.begin(), seqs.end(), begin, inv_cmp, output_size); //sequence iterators are progressed appropriately 00675 00676 rest -= output_size; 00677 size_ -= output_size; 00678 00679 for (unsigned_type i = 0; i < seqs.size(); ++i) 00680 { 00681 sequence_state & state = states[orig_seq_index[i]]; 00682 00683 state.current = seqs[i].first - state.block->begin(); 00684 00685 assert(seqs[i].first <= seqs[i].second); 00686 00687 if (seqs[i].first == seqs[i].second) //has run empty 00688 { 00689 assert(state.current == state.block->size); 00690 if (state.bids == NULL || state.bids->empty()) // if there is no next block 00691 { 00692 STXXL_VERBOSE1("seq " << i << ": ext_merger::multi_merge(...) it was the last block in the sequence "); 00693 state.make_inf(); 00694 } 00695 else 00696 { 00697 #if STXXL_CHECK_ORDER_IN_SORTS 00698 last_elem = *(seqs[i].second - 1); 00699 #endif 00700 STXXL_VERBOSE1("seq " << i << ": ext_merger::multi_merge(...) there is another block "); 00701 bid_type bid = state.bids->front(); 00702 state.bids->pop_front(); 00703 pool->hint(bid); 00704 if (!(state.bids->empty())) 00705 { 00706 STXXL_VERBOSE2("seq " << i << ": ext_merger::multi_merge(...) more blocks exist, hinting the next"); 00707 pool->hint(state.bids->front()); 00708 } 00709 pool->read(state.block, bid)->wait(); 00710 STXXL_VERBOSE1("seq " << i << ": first element of read block " << bid << " " << *(state.block->begin()) << " cached in " << state.block); 00711 if (!(state.bids->empty())) 00712 pool->hint(state.bids->front()); // re-hint, reading might have made a block free 00713 state.current = 0; 00714 seqs[i] = std::make_pair(state.block->begin() + state.current, state.block->end()); 00715 block_manager::get_instance()->delete_block(bid); 00716 00717 #if STXXL_CHECK_ORDER_IN_SORTS 00718 STXXL_VERBOSE1("before " << last_elem << " after " << *seqs[i].first << " newly loaded block " << bid); 00719 if (!stxxl::is_sorted(seqs[i].first, seqs[i].second, inv_cmp)) 00720 { 00721 STXXL_VERBOSE1("length " << i << " " << (seqs[i].second - seqs[i].first)); 00722 for (value_type * v = seqs[i].first + 1; v < seqs[i].second; ++v) 00723 { 00724 if (inv_cmp(*v, *(v - 1))) 00725 { 00726 STXXL_VERBOSE1("Error at position " << i << "/" << (v - seqs[i].first - 1) << "/" << (v - seqs[i].first) << " " << *(v - 1) << " " << *v); 00727 } 00728 if (is_sentinel(*v)) 00729 { 00730 STXXL_VERBOSE1("Wrong sentinel at position " << (v - seqs[i].first)); 00731 } 00732 } 00733 assert(false); 00734 } 00735 #endif 00736 } 00737 } 00738 } 00739 } //while (rest > 1) 00740 00741 for (unsigned_type i = 0; i < seqs.size(); ++i) 00742 { 00743 unsigned_type seg = orig_seq_index[i]; 00744 if (is_segment_empty(seg)) 00745 { 00746 STXXL_VERBOSE1("deallocated " << seg); 00747 deallocate_segment(seg); 00748 } 00749 } 00750 00751 #else // STXXL_PARALLEL && STXXL_PARALLEL_PQ_MULTIWAY_MERGE_EXTERNAL 00752 00753 //Hint first non-internal (actually second) block of each sequence. 00754 for (unsigned_type i = 0; i < k; ++i) 00755 { 00756 if (states[i].bids != NULL && !states[i].bids->empty()) 00757 pool->hint(states[i].bids->front()); 00758 } 00759 00760 switch (log_k) { 00761 case 0: 00762 assert(k == 1); 00763 assert(entry[0].index == 0); 00764 assert(free_segments.empty()); 00765 //memcpy(target, states[0], length * sizeof(value_type)); 00766 //std::copy(states[0],states[0]+length,target); 00767 for (size_type i = 0; i < length; ++i, ++(states[0]), ++begin) 00768 *begin = *(states[0]); 00769 00770 entry[0].key = **states; 00771 if (is_segment_empty(0)) 00772 deallocate_segment(0); 00773 00774 break; 00775 case 1: 00776 assert(k == 2); 00777 merge_iterator(states[0], states[1], begin, length, cmp); 00778 rebuild_loser_tree(); 00779 if (is_segment_empty(0) && is_segment_allocated(0)) 00780 deallocate_segment(0); 00781 00782 if (is_segment_empty(1) && is_segment_allocated(1)) 00783 deallocate_segment(1); 00784 00785 break; 00786 case 2: 00787 assert(k == 4); 00788 if (is_segment_empty(3)) 00789 merge3_iterator(states[0], states[1], states[2], begin, length, cmp); 00790 else 00791 merge4_iterator(states[0], states[1], states[2], states[3], begin, length, cmp); 00792 rebuild_loser_tree(); 00793 if (is_segment_empty(0) && is_segment_allocated(0)) 00794 deallocate_segment(0); 00795 00796 if (is_segment_empty(1) && is_segment_allocated(1)) 00797 deallocate_segment(1); 00798 00799 if (is_segment_empty(2) && is_segment_allocated(2)) 00800 deallocate_segment(2); 00801 00802 if (is_segment_empty(3) && is_segment_allocated(3)) 00803 deallocate_segment(3); 00804 00805 break; 00806 case 3: multi_merge_f<OutputIterator, 3>(begin, end); 00807 break; 00808 case 4: multi_merge_f<OutputIterator, 4>(begin, end); 00809 break; 00810 case 5: multi_merge_f<OutputIterator, 5>(begin, end); 00811 break; 00812 case 6: multi_merge_f<OutputIterator, 6>(begin, end); 00813 break; 00814 case 7: multi_merge_f<OutputIterator, 7>(begin, end); 00815 break; 00816 case 8: multi_merge_f<OutputIterator, 8>(begin, end); 00817 break; 00818 case 9: multi_merge_f<OutputIterator, 9>(begin, end); 00819 break; 00820 case 10: multi_merge_f<OutputIterator, 10>(begin, end); 00821 break; 00822 default: multi_merge_k(begin, end); 00823 break; 00824 } 00825 00826 size_ -= length; 00827 #endif 00828 00829 // compact tree if it got considerably smaller 00830 { 00831 const unsigned_type num_segments_used = std::min<unsigned_type>(arity, k) - free_segments.size(); 00832 const unsigned_type num_segments_trigger = k - (3 * k / 5); 00833 // using k/2 would be worst case inefficient (for large k) 00834 // for k \in {2, 4, 8} the trigger is k/2 which is good 00835 // because we have special mergers for k \in {1, 2, 4} 00836 // there is also a special 3-way-merger, that will be 00837 // triggered if k == 4 && is_segment_empty(3) 00838 STXXL_VERBOSE3("ext_merger compact? k=" << k << " #used=" << num_segments_used 00839 << " <= #trigger=" << num_segments_trigger << " ==> " 00840 << ((k > 1 && num_segments_used <= num_segments_trigger) ? "yes" : "no ") 00841 << " || " 00842 << ((k == 4 && !free_segments.empty() && !is_segment_empty(3)) ? "yes" : "no ") 00843 << " #free=" << free_segments.size()); 00844 if (k > 1 && ((num_segments_used <= num_segments_trigger) || 00845 (k == 4 && !free_segments.empty() && !is_segment_empty(3)))) 00846 { 00847 compact_tree(); 00848 } 00849 } 00850 } 00851 00852 private: 00853 #if STXXL_PQ_EXTERNAL_LOSER_TREE 00854 // multi-merge for arbitrary K 00855 template <class OutputIterator> 00856 void multi_merge_k(OutputIterator begin, OutputIterator end) 00857 { 00858 Entry * current_pos; 00859 value_type current_key; 00860 unsigned_type current_index; // leaf pointed to by current entry 00861 unsigned_type kReg = k; 00862 OutputIterator done = end; 00863 OutputIterator target = begin; 00864 unsigned_type winner_index = entry[0].index; 00865 value_type winner_key = entry[0].key; 00866 00867 while (target != done) 00868 { 00869 // write result 00870 *target = *(states[winner_index]); 00871 00872 // advance winner segment 00873 ++(states[winner_index]); 00874 00875 winner_key = *(states[winner_index]); 00876 00877 // remove winner segment if empty now 00878 if (is_sentinel(winner_key)) // 00879 deallocate_segment(winner_index); 00880 00881 00882 // go up the entry-tree 00883 for (unsigned_type i = (winner_index + kReg) >> 1; i > 0; i >>= 1) 00884 { 00885 current_pos = entry + i; 00886 current_key = current_pos->key; 00887 if (cmp(winner_key, current_key)) 00888 { 00889 current_index = current_pos->index; 00890 current_pos->key = winner_key; 00891 current_pos->index = winner_index; 00892 winner_key = current_key; 00893 winner_index = current_index; 00894 } 00895 } 00896 00897 ++target; 00898 } 00899 entry[0].index = winner_index; 00900 entry[0].key = winner_key; 00901 } 00902 00903 template <class OutputIterator, unsigned LogK> 00904 void multi_merge_f(OutputIterator begin, OutputIterator end) 00905 { 00906 OutputIterator done = end; 00907 OutputIterator target = begin; 00908 unsigned_type winner_index = entry[0].index; 00909 Entry * reg_entry = entry; 00910 sequence_state * reg_states = states; 00911 value_type winner_key = entry[0].key; 00912 00913 assert(log_k >= LogK); 00914 while (target != done) 00915 { 00916 // write result 00917 *target = *(reg_states[winner_index]); 00918 00919 // advance winner segment 00920 ++(reg_states[winner_index]); 00921 00922 winner_key = *(reg_states[winner_index]); 00923 00924 00925 // remove winner segment if empty now 00926 if (is_sentinel(winner_key)) 00927 deallocate_segment(winner_index); 00928 00929 00930 ++target; 00931 00932 // update loser tree 00933 #define TreeStep(L) \ 00934 if (1 << LogK >= 1 << L) { \ 00935 Entry * pos ## L = reg_entry + ((winner_index + (1 << LogK)) >> (((int(LogK - L) + 1) >= 0) ? ((LogK - L) + 1) : 0)); \ 00936 value_type key ## L = pos ## L->key; \ 00937 if (cmp(winner_key, key ## L)) { \ 00938 unsigned_type index ## L = pos ## L->index; \ 00939 pos ## L->key = winner_key; \ 00940 pos ## L->index = winner_index; \ 00941 winner_key = key ## L; \ 00942 winner_index = index ## L; \ 00943 } \ 00944 } 00945 TreeStep(10); 00946 TreeStep(9); 00947 TreeStep(8); 00948 TreeStep(7); 00949 TreeStep(6); 00950 TreeStep(5); 00951 TreeStep(4); 00952 TreeStep(3); 00953 TreeStep(2); 00954 TreeStep(1); 00955 #undef TreeStep 00956 } 00957 reg_entry[0].index = winner_index; 00958 reg_entry[0].key = winner_key; 00959 } 00960 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE 00961 00962 public: 00963 bool is_space_available() const // for new segment 00964 { 00965 return k < arity || !free_segments.empty(); 00966 } 00967 00968 00969 // insert segment beginning at target 00970 // require: is_space_available() == 1 00971 template <class Merger> 00972 void insert_segment(Merger & another_merger, size_type segment_size) 00973 { 00974 STXXL_VERBOSE1("ext_merger::insert_segment(merger,...)" << this); 00975 00976 if (segment_size > 0) 00977 { 00978 // get a free slot 00979 if (free_segments.empty()) 00980 { // tree is too small 00981 double_k(); 00982 } 00983 assert(!free_segments.empty()); 00984 unsigned_type free_slot = free_segments.top(); 00985 free_segments.pop(); 00986 00987 00988 // link new segment 00989 assert(segment_size); 00990 unsigned_type nblocks = segment_size / block_type::size; 00991 //assert(nblocks); // at least one block 00992 STXXL_VERBOSE1("ext_merger::insert_segment nblocks=" << nblocks); 00993 if (nblocks == 0) 00994 { 00995 STXXL_VERBOSE1("ext_merger::insert_segment(merger,...) WARNING: inserting a segment with " << 00996 nblocks << " blocks"); 00997 STXXL_VERBOSE1("THIS IS INEFFICIENT: TRY TO CHANGE PRIORITY QUEUE PARAMETERS"); 00998 } 00999 unsigned_type first_size = segment_size % block_type::size; 01000 if (first_size == 0) 01001 { 01002 first_size = block_type::size; 01003 --nblocks; 01004 } 01005 block_manager * bm = block_manager::get_instance(); 01006 std::list<bid_type> * bids = new std::list<bid_type>(nblocks); 01007 bm->new_blocks(alloc_strategy(), bids->begin(), bids->end()); 01008 block_type * first_block = new block_type; 01009 01010 another_merger.multi_merge( 01011 first_block->begin() + (block_type::size - first_size), 01012 first_block->end()); 01013 01014 STXXL_VERBOSE1("last element of first block " << *(first_block->end() - 1)); 01015 assert(!cmp(*(first_block->begin() + (block_type::size - first_size)), *(first_block->end() - 1))); 01016 01017 assert(pool->size_write() > 0); 01018 01019 for (typename std::list<bid_type>::iterator curbid = bids->begin(); curbid != bids->end(); ++curbid) 01020 { 01021 block_type * b = pool->steal(); 01022 another_merger.multi_merge(b->begin(), b->end()); 01023 STXXL_VERBOSE1("first element of following block " << *curbid << " " << *(b->begin())); 01024 STXXL_VERBOSE1("last element of following block " << *curbid << " " << *(b->end() - 1)); 01025 assert(!cmp(*(b->begin()), *(b->end() - 1))); 01026 pool->write(b, *curbid); 01027 STXXL_VERBOSE1("written to block " << *curbid << " cached in " << b); 01028 } 01029 01030 insert_segment(bids, first_block, first_size, free_slot); 01031 01032 size_ += segment_size; 01033 01034 #if STXXL_PQ_EXTERNAL_LOSER_TREE 01035 // propagate new information up the tree 01036 value_type dummyKey; 01037 unsigned_type dummyIndex; 01038 unsigned_type dummyMask; 01039 update_on_insert((free_slot + k) >> 1, *(states[free_slot]), free_slot, 01040 &dummyKey, &dummyIndex, &dummyMask); 01041 #endif //STXXL_PQ_EXTERNAL_LOSER_TREE 01042 } 01043 else 01044 { 01045 // deallocate memory ? 01046 STXXL_VERBOSE1("Merged segment with zero size."); 01047 } 01048 } 01049 01050 size_type size() const { return size_; } 01051 01052 protected: 01053 /*! \param bidlist list of blocks to insert 01054 \param first_block the first block of the sequence, before bidlist 01055 \param first_size number of elements in the first block 01056 \param slot slot to insert into 01057 */ 01058 void insert_segment(std::list<bid_type> * bidlist, block_type * first_block, 01059 unsigned_type first_size, unsigned_type slot) 01060 { 01061 STXXL_VERBOSE1("ext_merger::insert_segment(bidlist,...) " << this << " " << bidlist->size() << " " << slot); 01062 assert(!is_segment_allocated(slot)); 01063 assert(first_size > 0); 01064 01065 sequence_state & new_sequence = states[slot]; 01066 new_sequence.current = block_type::size - first_size; 01067 std::swap(new_sequence.block, first_block); 01068 delete first_block; 01069 std::swap(new_sequence.bids, bidlist); 01070 if (bidlist) // the old list 01071 { 01072 assert(bidlist->empty()); 01073 delete bidlist; 01074 } 01075 new_sequence.allocated = true; 01076 assert(is_segment_allocated(slot)); 01077 } 01078 01079 // free an empty segment . 01080 void deallocate_segment(unsigned_type slot) 01081 { 01082 STXXL_VERBOSE1("ext_merger::deallocate_segment() deleting segment " << slot << " allocated=" << int(is_segment_allocated(slot))); 01083 assert(is_segment_allocated(slot)); 01084 states[slot].allocated = false; 01085 states[slot].make_inf(); 01086 01087 // push on the stack of free segment indices 01088 free_segments.push(slot); 01089 } 01090 01091 // is this segment empty ? 01092 bool is_segment_empty(unsigned_type slot) const 01093 { 01094 return is_sentinel(*(states[slot])); 01095 } 01096 01097 // Is this segment allocated? Otherwise it's empty, 01098 // already on the stack of free segment indices and can be reused. 01099 bool is_segment_allocated(unsigned_type slot) const 01100 { 01101 return states[slot].allocated; 01102 } 01103 }; // ext_merger 01104 } // priority_queue_local 01105 01106 //! \} 01107 01108 __STXXL_END_NAMESPACE 01109 01110 #endif // !STXXL_PQ_EXT_MERGER_HEADER 01111 // vim: et:ts=4:sw=4