STXXL  1.4.0
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
ksort.h
Go to the documentation of this file.
1 /***************************************************************************
2  * include/stxxl/bits/algo/ksort.h
3  *
4  * Part of the STXXL. See http://stxxl.sourceforge.net
5  *
6  * Copyright (C) 2002 Roman Dementiev <[email protected]>
7  * Copyright (C) 2008-2011 Andreas Beckmann <[email protected]>
8  * Copyright (C) 2013 Timo Bingmann <[email protected]>
9  *
10  * Distributed under the Boost Software License, Version 1.0.
11  * (See accompanying file LICENSE_1_0.txt or copy at
12  * http://www.boost.org/LICENSE_1_0.txt)
13  **************************************************************************/
14 
15 #ifndef STXXL_ALGO_KSORT_HEADER
16 #define STXXL_ALGO_KSORT_HEADER
17 
19 #include <stxxl/bits/common/rand.h>
20 #include <stxxl/bits/mng/adaptor.h>
35 
36 
37 //#define INTERLEAVED_ALLOC
38 
39 #define OPT_MERGING
40 
42 
43 //! \defgroup stllayer STL-User Layer
44 //! Layer which groups STL compatible algorithms and containers
45 
46 //! \defgroup stlalgo Algorithms
47 //! \ingroup stllayer
48 //! Algorithms with STL-compatible interface
49 //! \{
50 
51 /*! \internal
52  */
53 namespace ksort_local {
54 
55 template <typename BIDType, typename KeyType>
57 {
58  typedef BIDType bid_type;
59  typedef KeyType key_type;
60 
63 
64  operator bid_type ()
65  {
66  return bid;
67  }
68 };
69 
70 
71 template <typename BIDType, typename KeyType>
72 inline bool operator < (const trigger_entry<BIDType, KeyType>& a,
74 {
75  return (a.key < b.key);
76 }
77 
78 template <typename BIDType, typename KeyType>
81 {
82  return (a.key > b.key);
83 }
84 
85 template <typename type, typename key_type1>
86 struct type_key
87 {
88  typedef key_type1 key_type;
90  type* ptr;
91 
92  type_key() { }
93  type_key(key_type k, type* p) : key(k), ptr(p)
94  { }
95 };
96 
97 template <typename type, typename key1>
98 bool operator < (const type_key<type, key1>& a, const type_key<type, key1>& b)
99 {
100  return a.key < b.key;
101 }
102 
103 template <typename type, typename key1>
105 {
106  return a.key > b.key;
107 }
108 
109 
110 template <typename block_type, typename bid_type>
112 {
113  block_type* block;
114  bid_type bid;
116  void operator () (request* /*completed_req*/)
117  {
118  * req = block->read(bid);
119  }
120 };
121 
122 template <typename type_key_,
123  typename block_type,
124  typename run_type,
125  typename input_bid_iterator,
126  typename key_extractor>
127 inline void write_out(
128  type_key_* begin,
129  type_key_* end,
130  block_type*& cur_blk,
131  const block_type* end_blk,
132  int_type& out_block,
133  int_type& out_pos,
134  run_type& run,
136  typename block_type::bid_type*& bids,
137  request_ptr* write_reqs,
138  request_ptr* read_reqs,
139  input_bid_iterator& it,
140  key_extractor keyobj)
141 {
142  typedef typename block_type::type type;
143 
144  type* elem = cur_blk->elem;
145  for (type_key_* p = begin; p < end; p++)
146  {
147  elem[out_pos++] = *(p->ptr);
148 
149  if (out_pos >= block_type::size)
150  {
151  run[out_block].key = keyobj(*(cur_blk->elem));
152 
153  if (cur_blk < end_blk)
154  {
155  next_read->block = cur_blk;
156  next_read->req = read_reqs + out_block;
157  read_reqs[out_block] = NULL;
158  bids[out_block] = next_read->bid = *(it++);
159 
160  write_reqs[out_block] = cur_blk->write(
161  run[out_block].bid,
162  // postpone read of block from next run
163  // after write of block from this run
164  *(next_read++));
165  }
166  else
167  {
168  write_reqs[out_block] = cur_blk->write(run[out_block].bid);
169  }
170 
171  cur_blk++;
172  elem = cur_blk->elem;
173  out_block++;
174  out_pos = 0;
175  }
176  }
177 }
178 
179 template <
180  typename block_type,
181  typename run_type,
182  typename input_bid_iterator,
183  typename key_extractor>
184 void
186  input_bid_iterator it,
187  run_type** runs,
188  const unsigned_type nruns,
189  const unsigned_type m2,
190  key_extractor keyobj)
191 {
192  typedef typename block_type::value_type type;
193  typedef typename block_type::bid_type bid_type;
194  typedef typename key_extractor::key_type key_type;
195  typedef type_key<type, key_type> type_key_;
196 
197  block_manager* bm = block_manager::get_instance();
198  block_type* Blocks1 = new block_type[m2];
199  block_type* Blocks2 = new block_type[m2];
200  bid_type* bids = new bid_type[m2];
201  type_key_* refs1 = new type_key_[m2 * Blocks1->size];
202  type_key_* refs2 = new type_key_[m2 * Blocks1->size];
203  request_ptr* read_reqs = new request_ptr[m2];
204  request_ptr* write_reqs = new request_ptr[m2];
207 
208  run_type* run;
209  run = *runs;
210  int_type run_size = (*runs)->size();
211  key_type offset = 0;
212  const int log_k1 = ilog2_ceil((m2 * block_type::size * sizeof(type_key_) / STXXL_L2_SIZE) ?
213  (m2 * block_type::size * sizeof(type_key_) / STXXL_L2_SIZE) : 2);
214  const int log_k2 = ilog2_floor(m2 * Blocks1->size) - log_k1 - 1;
215  STXXL_VERBOSE("log_k1: " << log_k1 << " log_k2:" << log_k2);
216  const int_type k1 = int_type(1) << log_k1;
217  const int_type k2 = int_type(1) << log_k2;
218  int_type* bucket1 = new int_type[k1];
219  int_type* bucket2 = new int_type[k2];
220  int_type i;
221 
222  disk_queues::get_instance()->set_priority_op(request_queue::WRITE);
223 
224  for (i = 0; i < run_size; i++)
225  {
226  bids[i] = *(it++);
227  read_reqs[i] = Blocks1[i].read(bids[i]);
228  }
229 
230  unsigned_type k = 0;
231  const int shift1 = (int)(sizeof(key_type) * 8 - log_k1);
232  const int shift2 = shift1 - log_k2;
233  STXXL_VERBOSE("shift1: " << shift1 << " shift2:" << shift2);
234 
235  for ( ; k < nruns; k++)
236  {
237  run = runs[k];
238  run_size = run->size();
239 
240  std::fill(bucket1, bucket1 + k1, 0);
241 
242  type_key_* ref_ptr = refs1;
243  for (i = 0; i < run_size; i++)
244  {
245  if (k)
246  write_reqs[i]->wait();
247 
248  read_reqs[i]->wait();
249  bm->delete_block(bids[i]);
250 
251  classify_block(Blocks1[i].begin(), Blocks1[i].end(), ref_ptr, bucket1, offset, shift1, keyobj);
252  }
253 
254  exclusive_prefix_sum(bucket1, k1);
255  classify(refs1, refs1 + run_size * Blocks1->size, refs2, bucket1,
256  offset, shift1);
257 
258  int_type out_block = 0;
259  int_type out_pos = 0;
260  unsigned_type next_run_size = (k < nruns - 1) ? (runs[k + 1]->size()) : 0;
261 
262  // recurse on each bucket
263  type_key_* c = refs2;
264  type_key_* d = refs1;
265  block_type* cur_blk = Blocks2;
266  block_type* end_blk = Blocks2 + next_run_size;
267  write_completion_handler<block_type, bid_type>* next_read = next_run_reads;
268 
269  for (i = 0; i < k1; i++)
270  {
271  type_key_* cEnd = refs2 + bucket1[i];
272  type_key_* dEnd = refs1 + bucket1[i];
273 
274  l1sort(c, cEnd, d, bucket2, k2,
275  offset + (key_type(1) << key_type(shift1)) * key_type(i), shift2); // key_type,key_type,... paranoia
276 
277  write_out(
278  d, dEnd, cur_blk, end_blk,
279  out_block, out_pos, *run, next_read, bids,
280  write_reqs, read_reqs, it, keyobj);
281 
282  c = cEnd;
283  d = dEnd;
284  }
285 
286  std::swap(Blocks1, Blocks2);
287  }
288 
289  wait_all(write_reqs, m2);
290 
291  delete[] bucket1;
292  delete[] bucket2;
293  delete[] refs1;
294  delete[] refs2;
295  delete[] Blocks1;
296  delete[] Blocks2;
297  delete[] bids;
298  delete[] next_run_reads;
299  delete[] read_reqs;
300  delete[] write_reqs;
301 }
302 
303 template <typename block_type,
304  typename prefetcher_type,
305  typename key_extractor>
306 struct run_cursor2_cmp : public std::binary_function<run_cursor2<block_type, prefetcher_type>, run_cursor2<block_type, prefetcher_type>, bool>
307 {
309  key_extractor keyobj;
310  run_cursor2_cmp(key_extractor keyobj_)
311  {
312  keyobj = keyobj_;
313  }
314  inline bool operator () (const cursor_type& a, const cursor_type& b) const
315  {
316  if (UNLIKELY(b.empty()))
317  return true;
318  // sentinel emulation
319  if (UNLIKELY(a.empty()))
320  return false;
321  //sentinel emulation
322 
323  return (keyobj(a.current()) < keyobj(b.current()));
324  }
325 
326 private:
328 };
329 
330 
331 template <typename record_type, typename key_extractor>
332 class key_comparison : public std::binary_function<record_type, record_type, bool>
333 {
334  key_extractor ke;
335 
336 public:
338  key_comparison(key_extractor ke_) : ke(ke_) { }
339  bool operator () (const record_type& a, const record_type& b) const
340  {
341  return ke(a) < ke(b);
342  }
343 };
344 
345 
346 template <typename block_type, typename run_type, typename key_ext_>
347 bool check_ksorted_runs(run_type** runs,
348  unsigned_type nruns,
349  unsigned_type m,
350  key_ext_ keyext)
351 {
352  typedef typename block_type::value_type value_type;
353 
354  STXXL_MSG("check_ksorted_runs Runs: " << nruns);
355  unsigned_type irun = 0;
356  for (irun = 0; irun < nruns; ++irun)
357  {
358  const unsigned_type nblocks_per_run = runs[irun]->size();
359  unsigned_type blocks_left = nblocks_per_run;
360  block_type* blocks = new block_type[m];
361  request_ptr* reqs = new request_ptr[m];
362  value_type last = keyext.min_value();
363 
364  for (unsigned_type off = 0; off < nblocks_per_run; off += m)
365  {
366  const unsigned_type nblocks = STXXL_MIN(blocks_left, m);
367  const unsigned_type nelements = nblocks * block_type::size;
368  blocks_left -= nblocks;
369 
370  for (unsigned_type j = 0; j < nblocks; ++j)
371  {
372  reqs[j] = blocks[j].read((*runs[irun])[off + j].bid);
373  }
374  wait_all(reqs, reqs + nblocks);
375 
376  if (off && (keyext(blocks[0][0]) < keyext(last)))
377  {
378  STXXL_MSG("check_sorted_runs wrong first value in the run " << irun);
379  STXXL_MSG(" first value: " << blocks[0][0] << " with key" << keyext(blocks[0][0]));
380  STXXL_MSG(" last value: " << last << " with key" << keyext(last));
381  for (unsigned_type k = 0; k < block_type::size; ++k)
382  STXXL_MSG("Element " << k << " in the block is :" << blocks[0][k] << " key: " << keyext(blocks[0][k]));
383 
384  delete[] reqs;
385  delete[] blocks;
386  return false;
387  }
388 
389  for (unsigned_type j = 0; j < nblocks; ++j)
390  {
391  if (keyext(blocks[j][0]) != (*runs[irun])[off + j].key)
392  {
393  STXXL_MSG("check_sorted_runs wrong trigger in the run " << irun << " block " << (off + j));
394  STXXL_MSG(" trigger value: " << (*runs[irun])[off + j].key);
395  STXXL_MSG("Data in the block:");
396  for (unsigned_type k = 0; k < block_type::size; ++k)
397  STXXL_MSG("Element " << k << " in the block is :" << blocks[j][k] << " with key: " << keyext(blocks[j][k]));
398 
399  STXXL_MSG("BIDS:");
400  for (unsigned_type k = 0; k < nblocks; ++k)
401  {
402  if (k == j)
403  STXXL_MSG("Bad one comes next.");
404  STXXL_MSG("BID " << (off + k) << " is: " << ((*runs[irun])[off + k].bid));
405  }
406 
407  delete[] reqs;
408  delete[] blocks;
409  return false;
410  }
411  }
412  if (!stxxl::is_sorted(make_element_iterator(blocks, 0),
413  make_element_iterator(blocks, nelements),
415  {
416  STXXL_MSG("check_sorted_runs wrong order in the run " << irun);
417  STXXL_MSG("Data in blocks:");
418  for (unsigned_type j = 0; j < nblocks; ++j)
419  {
420  for (unsigned_type k = 0; k < block_type::size; ++k)
421  STXXL_MSG(" Element " << k << " in block " << (off + j) << " is :" << blocks[j][k] << " with key: " << keyext(blocks[j][k]));
422  }
423  STXXL_MSG("BIDS:");
424  for (unsigned_type k = 0; k < nblocks; ++k)
425  {
426  STXXL_MSG("BID " << (k + off) << " is: " << ((*runs[irun])[k + off].bid));
427  }
428 
429  delete[] reqs;
430  delete[] blocks;
431  return false;
432  }
433  last = blocks[nblocks - 1][block_type::size - 1];
434  }
435 
436  assert(blocks_left == 0);
437  delete[] reqs;
438  delete[] blocks;
439  }
440 
441  return true;
442 }
443 
444 
445 template <typename block_type, typename run_type, typename key_extractor>
446 void merge_runs(run_type** in_runs, unsigned_type nruns, run_type* out_run, unsigned_type _m, key_extractor keyobj)
447 {
449  typedef run_cursor2<block_type, prefetcher_type> run_cursor_type;
450 
451  unsigned_type i;
452  run_type consume_seq(out_run->size());
453 
454  int_type* prefetch_seq = new int_type[out_run->size()];
455 
456  typename run_type::iterator copy_start = consume_seq.begin();
457  for (i = 0; i < nruns; i++)
458  {
459  // TODO: try to avoid copy
460  copy_start = std::copy(
461  in_runs[i]->begin(),
462  in_runs[i]->end(),
463  copy_start);
464  }
465  std::stable_sort(consume_seq.begin(), consume_seq.end() _STXXL_SORT_TRIGGER_FORCE_SEQUENTIAL);
466 
467  size_t disks_number = config::get_instance()->disks_number();
468 
469 #ifdef PLAY_WITH_OPT_PREF
470  const int_type n_write_buffers = 4 * disks_number;
471 #else
472  const int_type n_prefetch_buffers = STXXL_MAX(int_type(2 * disks_number), (3 * (int_type(_m) - int_type(nruns)) / 4));
473  STXXL_VERBOSE("Prefetch buffers " << n_prefetch_buffers);
474  const int_type n_write_buffers = STXXL_MAX(int_type(2 * disks_number), int_type(_m) - int_type(nruns) - int_type(n_prefetch_buffers));
475  STXXL_VERBOSE("Write buffers " << n_write_buffers);
476  // heuristic
477  const int_type n_opt_prefetch_buffers = 2 * int_type(disks_number) + (3 * (int_type(n_prefetch_buffers) - int_type(2 * disks_number))) / 10;
478  STXXL_VERBOSE("Prefetch buffers " << n_opt_prefetch_buffers);
479 #endif
480 
481 #if STXXL_SORT_OPTIMAL_PREFETCHING
483  consume_seq,
484  prefetch_seq,
485  n_opt_prefetch_buffers,
486  disks_number);
487 #else
488  for (i = 0; i < out_run->size(); i++)
489  prefetch_seq[i] = i;
490 
491 #endif
492 
493 
494  prefetcher_type prefetcher(consume_seq.begin(),
495  consume_seq.end(),
496  prefetch_seq,
497  nruns + n_prefetch_buffers);
498 
499  buffered_writer<block_type> writer(n_write_buffers, n_write_buffers / 2);
500 
501  unsigned_type out_run_size = out_run->size();
502 
504  loser_tree<
505  run_cursor_type,
507  losers(&prefetcher, nruns, cmp);
508 
509  block_type* out_buffer = writer.get_free_block();
510 
511  for (i = 0; i < out_run_size; i++)
512  {
513  losers.multi_merge(out_buffer->elem, out_buffer->elem + block_type::size);
514  (*out_run)[i].key = keyobj(out_buffer->elem[0]);
515  out_buffer = writer.write(out_buffer, (*out_run)[i].bid);
516  }
517 
518  delete[] prefetch_seq;
519 
520  block_manager* bm = block_manager::get_instance();
521  for (i = 0; i < nruns; i++)
522  {
523  unsigned_type sz = in_runs[i]->size();
524  for (unsigned_type j = 0; j < sz; j++)
525  bm->delete_block((*in_runs[i])[j].bid);
526 
527  delete in_runs[i];
528  }
529 }
530 
531 
532 template <typename block_type,
533  typename alloc_strategy,
534  typename input_bid_iterator,
535  typename key_extractor>
536 
538 ksort_blocks(input_bid_iterator input_bids, unsigned_type _n, unsigned_type _m, key_extractor keyobj)
539 {
540  typedef typename block_type::value_type type;
541  typedef typename key_extractor::key_type key_type;
542  typedef typename block_type::bid_type bid_type;
544  typedef simple_vector<trigger_entry_type> run_type;
545  typedef typename interleaved_alloc_traits<alloc_strategy>::strategy interleaved_alloc_strategy;
546 
547  unsigned_type m2 = div_ceil(_m, 2);
548  const unsigned_type m2_rf = m2 * block_type::raw_size /
549  (block_type::raw_size + block_type::size * sizeof(type_key<type, key_type>));
550  STXXL_VERBOSE("Reducing number of blocks in a run from " << m2 << " to " <<
551  m2_rf << " due to key size: " << sizeof(typename key_extractor::key_type) << " bytes");
552  m2 = m2_rf;
553  unsigned_type full_runs = _n / m2;
554  unsigned_type partial_runs = ((_n % m2) ? 1 : 0);
555  unsigned_type nruns = full_runs + partial_runs;
556  unsigned_type i;
557 
558  block_manager* mng = block_manager::get_instance();
559 
560  STXXL_VERBOSE("n=" << _n << " nruns=" << nruns << "=" << full_runs << "+" << partial_runs);
561 
562  double begin = timestamp(), after_runs_creation, end;
563 
564  run_type** runs = new run_type*[nruns];
565 
566  for (i = 0; i < full_runs; i++)
567  runs[i] = new run_type(m2);
568 
569 
570 #ifdef INTERLEAVED_ALLOC
571  if (partial_runs)
572  {
573  unsigned_type last_run_size = _n - full_runs * m2;
574  runs[i] = new run_type(last_run_size);
575 
576  mng->new_blocks(interleaved_alloc_strategy(nruns, alloc_strategy()),
578  (runs, 0, nruns, last_run_size),
580  (runs, _n, nruns, last_run_size));
581  }
582  else
583  mng->new_blocks(interleaved_alloc_strategy(nruns, alloc_strategy()),
585  (runs, 0, nruns),
587  (runs, _n, nruns));
588 
589 #else
590  if (partial_runs)
591  runs[i] = new run_type(_n - full_runs * m2);
592 
593  for (i = 0; i < nruns; i++)
594  {
595  mng->new_blocks(alloc_strategy(), make_bid_iterator(runs[i]->begin()), make_bid_iterator(runs[i]->end()));
596  }
597 #endif
598 
599  create_runs<block_type,
600  run_type,
601  input_bid_iterator,
602  key_extractor>(input_bids, runs, nruns, m2, keyobj);
603 
604  after_runs_creation = timestamp();
605 
606  double io_wait_after_rf = stats::get_instance()->get_io_wait_time();
607 
608  disk_queues::get_instance()->set_priority_op(request_queue::WRITE);
609 
610  const int_type merge_factor = optimal_merge_factor(nruns, _m);
611  run_type** new_runs;
612 
613  while (nruns > 1)
614  {
615  int_type new_nruns = div_ceil(nruns, merge_factor);
616  STXXL_VERBOSE("Starting new merge phase: nruns: " << nruns <<
617  " opt_merge_factor: " << merge_factor << " m:" << _m << " new_nruns: " << new_nruns);
618 
619  new_runs = new run_type*[new_nruns];
620 
621  int_type runs_left = nruns;
622  int_type cur_out_run = 0;
623  int_type blocks_in_new_run = 0;
624 
625  while (runs_left > 0)
626  {
627  int_type runs2merge = STXXL_MIN(runs_left, merge_factor);
628  blocks_in_new_run = 0;
629  for (unsigned_type i = nruns - runs_left; i < (nruns - runs_left + runs2merge); i++)
630  blocks_in_new_run += runs[i]->size();
631 
632  // allocate run
633  new_runs[cur_out_run++] = new run_type(blocks_in_new_run);
634  runs_left -= runs2merge;
635  }
636  // allocate blocks in the new runs
637  if (cur_out_run == 1 && blocks_in_new_run == int_type(_n) && !input_bids->is_managed())
638  {
639  // if we sort a file we can reuse the input bids for the output
640  input_bid_iterator cur = input_bids;
641  for (int_type i = 0; cur != (input_bids + _n); ++cur)
642  {
643  (*new_runs[0])[i++].bid = *cur;
644  }
645 
646  bid_type& firstBID = (*new_runs[0])[0].bid;
647  if (firstBID.is_managed())
648  {
649  // the first block does not belong to the file
650  // need to reallocate it
651  mng->new_block(FR(), firstBID);
652  }
653  bid_type& lastBID = (*new_runs[0])[_n - 1].bid;
654  if (lastBID.is_managed())
655  {
656  // the first block does not belong to the file
657  // need to reallocate it
658  mng->new_block(FR(), lastBID);
659  }
660  }
661  else
662  {
663  mng->new_blocks(interleaved_alloc_strategy(new_nruns, alloc_strategy()),
664  runs2bid_array_adaptor2<block_type::raw_size, run_type>(new_runs, 0, new_nruns, blocks_in_new_run),
665  runs2bid_array_adaptor2<block_type::raw_size, run_type>(new_runs, _n, new_nruns, blocks_in_new_run));
666  }
667 
668 
669  // merge all
670  runs_left = nruns;
671  cur_out_run = 0;
672  while (runs_left > 0)
673  {
674  int_type runs2merge = STXXL_MIN(runs_left, merge_factor);
675 #if STXXL_CHECK_ORDER_IN_SORTS
676  assert((check_ksorted_runs<block_type, run_type, key_extractor>(runs + nruns - runs_left, runs2merge, m2, keyobj)));
677 #endif
678  STXXL_VERBOSE("Merging " << runs2merge << " runs");
679  merge_runs<block_type, run_type, key_extractor>(runs + nruns - runs_left,
680  runs2merge, *(new_runs + (cur_out_run++)), _m, keyobj);
681  runs_left -= runs2merge;
682  }
683 
684  nruns = new_nruns;
685  delete[] runs;
686  runs = new_runs;
687  }
688 
689  run_type* result = *runs;
690  delete[] runs;
691 
692  end = timestamp();
693 
694  STXXL_VERBOSE("Elapsed time : " << end - begin << " s. Run creation time: " <<
695  after_runs_creation - begin << " s");
696  STXXL_VERBOSE("Time in I/O wait(rf): " << io_wait_after_rf << " s");
697  STXXL_VERBOSE(*stats::get_instance());
698  STXXL_UNUSED(begin + after_runs_creation + end + io_wait_after_rf);
699 
700  return result;
701 }
702 
703 } // namespace ksort_local
704 
705 /*!
706  * \brief Sort records with integer keys, see \ref design_algo_ksort.
707  *
708  * stxxl::ksort sorts the elements in [first, last) into ascending order,
709  * meaning that if \c i and \c j are any two valid iterators in [first, last)
710  * such that \c i precedes \c j, then \c *j is not less than \c *i. Note: as
711  * std::sort and stxxl::sort, stxxl::ksort is not guaranteed to be stable. That
712  * is, suppose that \c *i and \c *j are equivalent: neither one is less than
713  * the other. It is not guaranteed that the relative order of these two
714  * elements will be preserved by stxxl::ksort.
715  *
716  * The two versions of stxxl::ksort differ in how they define whether one
717  * element is less than another. The first version assumes that the elements
718  * have \c key() member function that returns an integral key (32 or 64 bit),
719  * as well as the minimum and the maximum element values. The second version
720  * compares objects extracting the keys using \c keyobj object, that is in turn
721  * provides min and max element values.
722  *
723  * The sorter's internal memory consumption is bounded by \c M bytes.
724  *
725  * \param first object of model of \c ext_random_access_iterator concept
726  * \param last object of model of \c ext_random_access_iterator concept
727  * \param keyobj \link design_algo_ksort_key_extractor key extractor \endlink object
728  * \param M amount of memory for internal use (in bytes)
729  */
730 template <typename ExtIterator, typename KeyExtractor>
731 void ksort(ExtIterator first, ExtIterator last, KeyExtractor keyobj, unsigned_type M)
732 {
733  typedef simple_vector<ksort_local::trigger_entry<typename ExtIterator::bid_type,
734  typename KeyExtractor::key_type> > run_type;
735  typedef typename ExtIterator::vector_type::value_type value_type;
736  typedef typename ExtIterator::block_type block_type;
737 
738 
739  unsigned_type n = 0;
740  block_manager* mng = block_manager::get_instance();
741 
742  first.flush();
743 
744  if ((last - first) * sizeof(value_type) < M)
745  {
747  }
748  else
749  {
750  assert(2 * block_type::raw_size <= M);
751 
752  if (first.block_offset())
753  {
754  if (last.block_offset()) // first and last element reside
755  // not in the beginning of the block
756  {
757  typename ExtIterator::block_type * first_block = new typename ExtIterator::block_type;
758  typename ExtIterator::block_type * last_block = new typename ExtIterator::block_type;
759  typename ExtIterator::bid_type first_bid, last_bid;
760  request_ptr req;
761 
762  req = first_block->read(*first.bid());
763  mng->new_block(FR(), first_bid); // try to overlap
764  mng->new_block(FR(), last_bid);
765  req->wait();
766 
767 
768  req = last_block->read(*last.bid());
769 
770  unsigned_type i = 0;
771  for ( ; i < first.block_offset(); i++)
772  {
773  first_block->elem[i] = keyobj.min_value();
774  }
775 
776  req->wait();
777 
778  req = first_block->write(first_bid);
779  for (i = last.block_offset(); i < block_type::size; i++)
780  {
781  last_block->elem[i] = keyobj.max_value();
782  }
783 
784  req->wait();
785 
786  req = last_block->write(last_bid);
787 
788  n = last.bid() - first.bid() + 1;
789 
790  std::swap(first_bid, *first.bid());
791  std::swap(last_bid, *last.bid());
792 
793  req->wait();
794 
795  delete first_block;
796  delete last_block;
797 
798  run_type* out =
800  typename ExtIterator::block_type,
801  typename ExtIterator::vector_type::alloc_strategy_type,
802  typename ExtIterator::bids_container_iterator,
803  KeyExtractor>
804  (first.bid(), n, M / block_type::raw_size, keyobj);
805 
806 
807  first_block = new typename ExtIterator::block_type;
808  last_block = new typename ExtIterator::block_type;
809  typename ExtIterator::block_type * sorted_first_block = new typename ExtIterator::block_type;
810  typename ExtIterator::block_type * sorted_last_block = new typename ExtIterator::block_type;
811  request_ptr* reqs = new request_ptr[2];
812 
813  reqs[0] = first_block->read(first_bid);
814  reqs[1] = sorted_first_block->read((*(out->begin())).bid);
815  wait_all(reqs, 2);
816 
817  reqs[0] = last_block->read(last_bid);
818  reqs[1] = sorted_last_block->read(((*out)[out->size() - 1]).bid);
819 
820  for (i = first.block_offset(); i < block_type::size; i++)
821  {
822  first_block->elem[i] = sorted_first_block->elem[i];
823  }
824  wait_all(reqs, 2);
825 
826  req = first_block->write(first_bid);
827 
828  for (i = 0; i < last.block_offset(); i++)
829  {
830  last_block->elem[i] = sorted_last_block->elem[i];
831  }
832 
833  req->wait();
834 
835 
836  req = last_block->write(last_bid);
837 
838  mng->delete_block(out->begin()->bid);
839  mng->delete_block((*out)[out->size() - 1].bid);
840 
841  *first.bid() = first_bid;
842  *last.bid() = last_bid;
843 
844  typename run_type::iterator it = out->begin();
845  it++;
846  typename ExtIterator::bids_container_iterator cur_bid = first.bid();
847  cur_bid++;
848 
849  for ( ; cur_bid != last.bid(); cur_bid++, it++)
850  {
851  *cur_bid = (*it).bid;
852  }
853 
854  delete first_block;
855  delete sorted_first_block;
856  delete sorted_last_block;
857  delete[] reqs;
858  delete out;
859 
860  req->wait();
861 
862  delete last_block;
863  }
864  else
865  {
866  // first element resides
867  // not in the beginning of the block
868 
869  typename ExtIterator::block_type * first_block = new typename ExtIterator::block_type;
870  typename ExtIterator::bid_type first_bid;
871  request_ptr req;
872 
873  req = first_block->read(*first.bid());
874  mng->new_block(FR(), first_bid); // try to overlap
875  req->wait();
876 
877 
878  unsigned_type i = 0;
879  for ( ; i < first.block_offset(); i++)
880  {
881  first_block->elem[i] = keyobj.min_value();
882  }
883 
884  req = first_block->write(first_bid);
885 
886  n = last.bid() - first.bid();
887 
888  std::swap(first_bid, *first.bid());
889 
890  req->wait();
891 
892  delete first_block;
893 
894  run_type* out =
896  typename ExtIterator::block_type,
897  typename ExtIterator::vector_type::alloc_strategy_type,
898  typename ExtIterator::bids_container_iterator,
899  KeyExtractor>
900  (first.bid(), n, M / block_type::raw_size, keyobj);
901 
902 
903  first_block = new typename ExtIterator::block_type;
904 
905  typename ExtIterator::block_type * sorted_first_block = new typename ExtIterator::block_type;
906 
907  request_ptr* reqs = new request_ptr[2];
908 
909  reqs[0] = first_block->read(first_bid);
910  reqs[1] = sorted_first_block->read((*(out->begin())).bid);
911  wait_all(reqs, 2);
912 
913  for (i = first.block_offset(); i < block_type::size; i++)
914  {
915  first_block->elem[i] = sorted_first_block->elem[i];
916  }
917 
918  req = first_block->write(first_bid);
919 
920  mng->delete_block(out->begin()->bid);
921 
922  *first.bid() = first_bid;
923 
924  typename run_type::iterator it = out->begin();
925  it++;
926  typename ExtIterator::bids_container_iterator cur_bid = first.bid();
927  cur_bid++;
928 
929  for ( ; cur_bid != last.bid(); cur_bid++, it++)
930  {
931  *cur_bid = (*it).bid;
932  }
933 
934  *cur_bid = (*it).bid;
935 
936  delete sorted_first_block;
937  delete[] reqs;
938  delete out;
939 
940  req->wait();
941 
942  delete first_block;
943  }
944  }
945  else
946  {
947  if (last.block_offset()) // last element resides
948  // not in the beginning of the block
949  {
950  typename ExtIterator::block_type * last_block = new typename ExtIterator::block_type;
951  typename ExtIterator::bid_type last_bid;
952  request_ptr req;
953  unsigned_type i;
954 
955  req = last_block->read(*last.bid());
956  mng->new_block(FR(), last_bid);
957  req->wait();
958 
959  for (i = last.block_offset(); i < block_type::size; i++)
960  {
961  last_block->elem[i] = keyobj.max_value();
962  }
963 
964  req = last_block->write(last_bid);
965 
966  n = last.bid() - first.bid() + 1;
967 
968  std::swap(last_bid, *last.bid());
969 
970  req->wait();
971 
972  delete last_block;
973 
974  run_type* out =
976  typename ExtIterator::block_type,
977  typename ExtIterator::vector_type::alloc_strategy_type,
978  typename ExtIterator::bids_container_iterator,
979  KeyExtractor>
980  (first.bid(), n, M / block_type::raw_size, keyobj);
981 
982 
983  last_block = new typename ExtIterator::block_type;
984  typename ExtIterator::block_type * sorted_last_block = new typename ExtIterator::block_type;
985  request_ptr* reqs = new request_ptr[2];
986 
987  reqs[0] = last_block->read(last_bid);
988  reqs[1] = sorted_last_block->read(((*out)[out->size() - 1]).bid);
989  wait_all(reqs, 2);
990 
991  for (i = 0; i < last.block_offset(); i++)
992  {
993  last_block->elem[i] = sorted_last_block->elem[i];
994  }
995 
996  req = last_block->write(last_bid);
997 
998  mng->delete_block((*out)[out->size() - 1].bid);
999 
1000  *last.bid() = last_bid;
1001 
1002  typename run_type::iterator it = out->begin();
1003  typename ExtIterator::bids_container_iterator cur_bid = first.bid();
1004 
1005  for ( ; cur_bid != last.bid(); cur_bid++, it++)
1006  {
1007  *cur_bid = (*it).bid;
1008  }
1009 
1010  delete sorted_last_block;
1011  delete[] reqs;
1012  delete out;
1013 
1014  req->wait();
1015 
1016  delete last_block;
1017  }
1018  else
1019  {
1020  // first and last element reside in the beginning of blocks
1021  n = last.bid() - first.bid();
1022 
1023  run_type* out =
1025  typename ExtIterator::block_type,
1026  typename ExtIterator::vector_type::alloc_strategy_type,
1027  typename ExtIterator::bids_container_iterator,
1028  KeyExtractor>
1029  (first.bid(), n, M / block_type::raw_size, keyobj);
1030 
1031  typename run_type::iterator it = out->begin();
1032  typename ExtIterator::bids_container_iterator cur_bid = first.bid();
1033 
1034  for ( ; cur_bid != last.bid(); cur_bid++, it++)
1035  {
1036  *cur_bid = (*it).bid;
1037  }
1038 
1039  delete out;
1040  }
1041  }
1042  }
1043 
1044 #if STXXL_CHECK_ORDER_IN_SORTS
1045  typedef typename ExtIterator::const_iterator const_iterator;
1046  STXXL_ASSERT(stxxl::is_sorted(const_iterator(first), const_iterator(last),
1048 #endif
1049 }
1050 
1051 template <typename record_type>
1053 {
1054  typedef typename record_type::key_type key_type;
1055  key_type operator () (const record_type& obj) const
1056  {
1057  return obj.key();
1058  }
1059  record_type max_value() const
1060  {
1061  return record_type::max_value();
1062  }
1063  record_type min_value() const
1064  {
1065  return record_type::min_value();
1066  }
1067 };
1068 
1069 /*!
1070  * \brief Sort records with integer keys, see \ref design_algo_ksort.
1071  *
1072  * stxxl::ksort sorts the elements in [first, last) into ascending order,
1073  * meaning that if \c i and \c j are any two valid iterators in [first, last)
1074  * such that \c i precedes \c j, then \c *j is not less than \c *i. Note: as
1075  * std::sort and stxxl::sort, stxxl::ksort is not guaranteed to be stable. That
1076  * is, suppose that \c *i and \c *j are equivalent: neither one is less than
1077  * the other. It is not guaranteed that the relative order of these two
1078  * elements will be preserved by stxxl::ksort.
1079  *
1080  * \param first object of model of \c ext_random_access_iterator concept
1081  * \param last object of model of \c ext_random_access_iterator concept
1082  * \param M amount of buffers for internal use
1083  * \remark Order in the result is non-stable
1084  */
1085 template <typename ExtIterator>
1086 void ksort(ExtIterator first, ExtIterator last, unsigned_type M)
1087 {
1088  ksort(first, last,
1090 }
1091 
1092 //! \}
1093 
1095 
1096 #endif // !STXXL_ALGO_KSORT_HEADER
1097 // vim: et:ts=4:sw=4
#define STXXL_VERBOSE(x)
Definition: verbose.h:102
const Tp & STXXL_MIN(const Tp &a, const Tp &b)
Definition: utils.h:147
run_cursor2_cmp(key_extractor keyobj_)
Definition: ksort.h:310
#define STXXL_ASSERT(condition)
Definition: verbose.h:157
key_comparison(key_extractor ke_)
Definition: ksort.h:338
Fully randomized disk allocation scheme functor.
Definition: block_alloc.h:68
Block manager class.
Definition: block_manager.h:63
bool is_sorted(_ForwardIter __first, _ForwardIter __last)
Definition: is_sorted.h:54
void stl_in_memory_sort(ExtIterator_ first, ExtIterator_ last, StrictWeakOrdering_ cmp)
Definition: inmemsort.h:30
trigger_entry_iterator< Iterator > make_bid_iterator(Iterator iter)
Definition: adaptor.h:241
record_type max_value() const
Definition: ksort.h:1059
void delete_block(const BID< BLK_SIZE > &bid)
Deallocates a block.
Definition: ksort.h:56
block_type::const_reference current() const
Definition: run_cursor.h:31
#define STXXL_L2_SIZE
Definition: sort_base.h:35
void ksort(ExtIterator first, ExtIterator last, KeyExtractor keyobj, unsigned_type M)
Sort records with integer keys, see stxxl::ksort -- Sorting Integer Keys.
Definition: ksort.h:731
element_iterator_traits< BlockType >::element_iterator make_element_iterator(BlockType *blocks, unsigned_type offset)
Definition: adaptor.h:631
void wait_all(request_iterator_ reqs_begin, request_iterator_ reqs_end)
Collection of functions to track statuses of a number of requests.
KeyType key_type
Definition: ksort.h:59
type_key(key_type k, type *p)
Definition: ksort.h:93
key_type key
Definition: ksort.h:62
record_type::key_type key_type
Definition: ksort.h:1054
void write_out(type_key_ *begin, type_key_ *end, block_type *&cur_blk, const block_type *end_blk, int_type &out_block, int_type &out_pos, run_type &run, write_completion_handler< block_type, typename block_type::bid_type > *&next_read, typename block_type::bid_type *&bids, request_ptr *write_reqs, request_ptr *read_reqs, input_bid_iterator &it, key_extractor keyobj)
Definition: ksort.h:127
virtual void wait(bool measure_time=true)=0
Suspends calling thread until completion of the request.
Encapsulates asynchronous prefetching engine.
double timestamp()
Returns number of seconds since the epoch, high resolution.
Definition: timer.h:42
const Tp & STXXL_MAX(const Tp &a, const Tp &b)
Definition: utils.h:154
#define _STXXL_SORT_TRIGGER_FORCE_SEQUENTIAL
Definition: parallel.h:59
choose_int_types< my_pointer_size >::int_type int_type
Definition: types.h:66
simple_vector< trigger_entry< typename block_type::bid_type, typename key_extractor::key_type > > * ksort_blocks(input_bid_iterator input_bids, unsigned_type _n, unsigned_type _m, key_extractor keyobj)
Definition: ksort.h:538
bool operator>(const uint_pair &b) const
greater comparison operator
Definition: uint_types.h:219
run_cursor2< block_type, prefetcher_type > cursor_type
Definition: ksort.h:308
#define UNLIKELY(c)
Definition: utils.h:226
#define STXXL_BEGIN_NAMESPACE
Definition: namespace.h:16
void STXXL_UNUSED(const U &)
Definition: unused.h:23
static void classify(type_key *a, type_key *aEnd, type_key *b, int_type *bucket, typename type_key::key_type offset, unsigned shift)
Definition: intksort.h:65
bid_type bid
Definition: ksort.h:61
void new_block(const DiskAssignFunctor &functor, BID< BLK_SIZE > &bid, unsigned_type offset=0)
Allocates a new block according to the strategy given by functor and stores the block identifier to b...
void l1sort(type_key *a, type_key *aEnd, type_key *b, int_type *bucket, int_type K, typename type_key::key_type offset, int shift)
Definition: intksort.h:323
unsigned int ilog2_floor(IntegerType i)
calculate the log2 floor of an integer type (by repeated bit shifts)
Definition: utils.h:179
static void exclusive_prefix_sum(int_type *bucket, int_type K)
Definition: intksort.h:50
void new_blocks(const DiskAssignFunctor &functor, BIDIteratorClass bidbegin, BIDIteratorClass bidend, unsigned_type offset=0)
Allocates new blocks.
Simpler non-growing vector without initialization.
Definition: simple_vector.h:37
void create_runs(input_bid_iterator it, run_type **runs, const unsigned_type nruns, const unsigned_type m2, key_extractor keyobj)
Definition: ksort.h:185
choose_int_types< my_pointer_size >::unsigned_type unsigned_type
Definition: types.h:67
unsigned int ilog2_ceil(const IntegerType &i)
calculate the log2 ceiling of an integer type (by repeated bit shifts)
Definition: utils.h:189
compat::remove_const< Integral >::type div_ceil(Integral __n, Integral2 __d)
Definition: utils.h:200
void compute_prefetch_schedule(const int_type *first, const int_type *last, int_type *out_first, int_type m, int_type D)
#define STXXL_MSG(x)
Definition: verbose.h:72
unsigned_type optimal_merge_factor(unsigned_type num_runs, unsigned_type max_concurrent_runs)
Definition: sort_base.h:41
bool check_ksorted_runs(run_type **runs, unsigned_type nruns, unsigned_type m, key_ext_ keyext)
Definition: ksort.h:347
Encapsulates asynchronous buffered block writing engine.
Definition: buf_writer.h:38
void classify_block(type *begin, type *end, type_key *&out, int_type *bucket, typename key_extractor::key_type offset, unsigned shift, key_extractor keyobj)
Definition: intksort.h:334
Request with basic properties like file and offset.
Definition: request.h:39
record_type min_value() const
Definition: ksort.h:1063
BIDType bid_type
Definition: ksort.h:58
#define STXXL_END_NAMESPACE
Definition: namespace.h:17
bool empty() const
Definition: run_cursor.h:85
void merge_runs(run_type **in_runs, unsigned_type nruns, run_type *out_run, unsigned_type _m, key_extractor keyobj)
Definition: ksort.h:446