Skip to content

Commit 8ffe8bc

Browse files
committed
Remove gfx::Compare, use the provided function directly
gfx::Compare performed several unneeded operations and there are no places in timsort where we can actually take advantage of a three-way comparison, so it's generally better to use the provided comparison function directly (see PR #26 for more rationale).
1 parent bfca750 commit 8ffe8bc

File tree

2 files changed

+53
-106
lines changed

2 files changed

+53
-106
lines changed

include/gfx/timsort.hpp

Lines changed: 53 additions & 85 deletions
Original file line numberDiff line numberDiff line change
@@ -102,55 +102,20 @@ inline void timsort(RandomAccessIterator const first, RandomAccessIterator const
102102
/**
103103
* Same as std::stable_sort(first, last, c).
104104
*/
105-
template <typename RandomAccessIterator, typename LessFunction>
106-
inline void timsort(RandomAccessIterator const first, RandomAccessIterator const last, LessFunction compare);
105+
template <typename RandomAccessIterator, typename Compare>
106+
inline void timsort(RandomAccessIterator const first, RandomAccessIterator const last, Compare compare);
107107

108108
// ---------------------------------------
109109
// Implementation
110110
// ---------------------------------------
111111

112-
template <typename Value, typename LessFunction> class Compare {
113-
public:
114-
typedef Value value_type;
115-
typedef LessFunction func_type;
116-
117-
Compare(LessFunction f) : less_(f) {
118-
}
119-
Compare(const Compare<value_type, func_type> &other) : less_(other.less_) {
120-
}
121-
122-
bool lt(value_type x, value_type y) {
123-
return less_(x, y);
124-
}
125-
bool le(value_type x, value_type y) {
126-
return less_(x, y) || !less_(y, x);
127-
}
128-
bool gt(value_type x, value_type y) {
129-
return !less_(x, y) && less_(y, x);
130-
}
131-
bool ge(value_type x, value_type y) {
132-
return !less_(x, y);
133-
}
134-
135-
func_type &less_function() {
136-
return less_;
137-
}
138-
139-
private:
140-
func_type less_;
141-
};
142-
143-
template <typename RandomAccessIterator, typename LessFunction> class TimSort {
112+
template <typename RandomAccessIterator, typename Compare> class TimSort {
144113
typedef RandomAccessIterator iter_t;
145114
typedef typename std::iterator_traits<iter_t>::value_type value_t;
146115
typedef typename std::iterator_traits<iter_t>::reference ref_t;
147116
typedef typename std::iterator_traits<iter_t>::difference_type diff_t;
148-
typedef Compare<const value_t &, LessFunction> compare_t;
149117

150118
static const int MIN_MERGE = 32;
151-
152-
compare_t comp_;
153-
154119
static const int MIN_GALLOP = 7;
155120

156121
int minGallop_; // default to MIN_GALLOP
@@ -167,7 +132,7 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
167132
};
168133
std::vector<run> pending_;
169134

170-
static void sort(iter_t const lo, iter_t const hi, compare_t c) {
135+
static void sort(iter_t const lo, iter_t const hi, Compare compare) {
171136
GFX_TIMSORT_ASSERT(lo <= hi);
172137

173138
diff_t nRemaining = (hi - lo);
@@ -176,40 +141,40 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
176141
}
177142

178143
if (nRemaining < MIN_MERGE) {
179-
diff_t const initRunLen = countRunAndMakeAscending(lo, hi, c);
144+
diff_t const initRunLen = countRunAndMakeAscending(lo, hi, compare);
180145
GFX_TIMSORT_LOG("initRunLen: " << initRunLen);
181-
binarySort(lo, hi, lo + initRunLen, c);
146+
binarySort(lo, hi, lo + initRunLen, compare);
182147
return;
183148
}
184149

185-
TimSort ts(c);
150+
TimSort ts;
186151
diff_t const minRun = minRunLength(nRemaining);
187152
iter_t cur = lo;
188153
do {
189-
diff_t runLen = countRunAndMakeAscending(cur, hi, c);
154+
diff_t runLen = countRunAndMakeAscending(cur, hi, compare);
190155

191156
if (runLen < minRun) {
192157
diff_t const force = std::min(nRemaining, minRun);
193-
binarySort(cur, cur + force, cur + runLen, c);
158+
binarySort(cur, cur + force, cur + runLen, compare);
194159
runLen = force;
195160
}
196161

197162
ts.pushRun(cur, runLen);
198-
ts.mergeCollapse();
163+
ts.mergeCollapse(compare);
199164

200165
cur += runLen;
201166
nRemaining -= runLen;
202167
} while (nRemaining != 0);
203168

204169
GFX_TIMSORT_ASSERT(cur == hi);
205-
ts.mergeForceCollapse();
170+
ts.mergeForceCollapse(compare);
206171
GFX_TIMSORT_ASSERT(ts.pending_.size() == 1);
207172

208173
GFX_TIMSORT_LOG("size: " << (hi - lo) << " tmp_.size(): " << ts.tmp_.size()
209174
<< " pending_.size(): " << ts.pending_.size());
210175
} // sort()
211176

212-
static void binarySort(iter_t const lo, iter_t const hi, iter_t start, compare_t compare) {
177+
static void binarySort(iter_t const lo, iter_t const hi, iter_t start, Compare compare) {
213178
GFX_TIMSORT_ASSERT(lo <= start && start <= hi);
214179
if (start == lo) {
215180
++start;
@@ -218,29 +183,29 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
218183
GFX_TIMSORT_ASSERT(lo <= start);
219184
/*const*/ value_t pivot = GFX_TIMSORT_MOVE(*start);
220185

221-
iter_t const pos = std::upper_bound(lo, start, pivot, compare.less_function());
186+
iter_t const pos = std::upper_bound(lo, start, pivot, compare);
222187
for (iter_t p = start; p > pos; --p) {
223188
*p = GFX_TIMSORT_MOVE(*(p - 1));
224189
}
225190
*pos = GFX_TIMSORT_MOVE(pivot);
226191
}
227192
}
228193

229-
static diff_t countRunAndMakeAscending(iter_t const lo, iter_t const hi, compare_t compare) {
194+
static diff_t countRunAndMakeAscending(iter_t const lo, iter_t const hi, Compare compare) {
230195
GFX_TIMSORT_ASSERT(lo < hi);
231196

232197
iter_t runHi = lo + 1;
233198
if (runHi == hi) {
234199
return 1;
235200
}
236201

237-
if (compare.lt(*(runHi++), *lo)) { // descending
238-
while (runHi < hi && compare.lt(*runHi, *(runHi - 1))) {
202+
if (compare(*(runHi++), *lo)) { // descending
203+
while (runHi < hi && compare(*runHi, *(runHi - 1))) {
239204
++runHi;
240205
}
241206
std::reverse(lo, runHi);
242207
} else { // ascending
243-
while (runHi < hi && compare.ge(*runHi, *(runHi - 1))) {
208+
while (runHi < hi && !compare(*runHi, *(runHi - 1))) {
244209
++runHi;
245210
}
246211
}
@@ -259,14 +224,14 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
259224
return n + r;
260225
}
261226

262-
TimSort(compare_t c) : comp_(c), minGallop_(MIN_GALLOP) {
227+
TimSort() : minGallop_(MIN_GALLOP) {
263228
}
264229

265230
void pushRun(iter_t const runBase, diff_t const runLen) {
266231
pending_.push_back(run(runBase, runLen));
267232
}
268233

269-
void mergeCollapse() {
234+
void mergeCollapse(Compare compare) {
270235
while (pending_.size() > 1) {
271236
diff_t n = pending_.size() - 2;
272237

@@ -275,27 +240,27 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
275240
if (pending_[n - 1].len < pending_[n + 1].len) {
276241
--n;
277242
}
278-
mergeAt(n);
243+
mergeAt(n, compare);
279244
} else if (pending_[n].len <= pending_[n + 1].len) {
280-
mergeAt(n);
245+
mergeAt(n, compare);
281246
} else {
282247
break;
283248
}
284249
}
285250
}
286251

287-
void mergeForceCollapse() {
252+
void mergeForceCollapse(Compare compare) {
288253
while (pending_.size() > 1) {
289254
diff_t n = pending_.size() - 2;
290255

291256
if (n > 0 && pending_[n - 1].len < pending_[n + 1].len) {
292257
--n;
293258
}
294-
mergeAt(n);
259+
mergeAt(n, compare);
295260
}
296261
}
297262

298-
void mergeAt(diff_t const i) {
263+
void mergeAt(diff_t const i, Compare compare) {
299264
diff_t const stackSize = pending_.size();
300265
GFX_TIMSORT_ASSERT(stackSize >= 2);
301266
GFX_TIMSORT_ASSERT(i >= 0);
@@ -317,7 +282,7 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
317282

318283
pending_.pop_back();
319284

320-
diff_t const k = gallopRight(*base2, base1, len1, 0);
285+
diff_t const k = gallopRight(*base2, base1, len1, 0, compare);
321286
GFX_TIMSORT_ASSERT(k >= 0);
322287

323288
base1 += k;
@@ -327,28 +292,29 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
327292
return;
328293
}
329294

330-
len2 = gallopLeft(*(base1 + (len1 - 1)), base2, len2, len2 - 1);
295+
len2 = gallopLeft(*(base1 + (len1 - 1)), base2, len2, len2 - 1, compare);
331296
GFX_TIMSORT_ASSERT(len2 >= 0);
332297
if (len2 == 0) {
333298
return;
334299
}
335300

336301
if (len1 <= len2) {
337-
mergeLo(base1, len1, base2, len2);
302+
mergeLo(base1, len1, base2, len2, compare);
338303
} else {
339-
mergeHi(base1, len1, base2, len2);
304+
mergeHi(base1, len1, base2, len2, compare);
340305
}
341306
}
342307

343-
template <typename Iter> diff_t gallopLeft(ref_t key, Iter const base, diff_t const len, diff_t const hint) {
308+
template <typename Iter>
309+
diff_t gallopLeft(ref_t key, Iter const base, diff_t const len, diff_t const hint, Compare compare) {
344310
GFX_TIMSORT_ASSERT(len > 0 && hint >= 0 && hint < len);
345311

346312
diff_t lastOfs = 0;
347313
diff_t ofs = 1;
348314

349-
if (comp_.gt(key, *(base + hint))) {
315+
if (compare(*(base + hint), key)) {
350316
diff_t const maxOfs = len - hint;
351-
while (ofs < maxOfs && comp_.gt(key, *(base + (hint + ofs)))) {
317+
while (ofs < maxOfs && compare(*(base + (hint + ofs)), key)) {
352318
lastOfs = ofs;
353319
ofs = (ofs << 1) + 1;
354320

@@ -364,7 +330,7 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
364330
ofs += hint;
365331
} else {
366332
diff_t const maxOfs = hint + 1;
367-
while (ofs < maxOfs && comp_.le(key, *(base + (hint - ofs)))) {
333+
while (ofs < maxOfs && !compare(*(base + (hint - ofs)), key)) {
368334
lastOfs = ofs;
369335
ofs = (ofs << 1) + 1;
370336

@@ -382,18 +348,19 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
382348
}
383349
GFX_TIMSORT_ASSERT(-1 <= lastOfs && lastOfs < ofs && ofs <= len);
384350

385-
return std::lower_bound(base + (lastOfs + 1), base + ofs, key, comp_.less_function()) - base;
351+
return std::lower_bound(base + (lastOfs + 1), base + ofs, key, compare) - base;
386352
}
387353

388-
template <typename Iter> diff_t gallopRight(ref_t key, Iter const base, diff_t const len, diff_t const hint) {
354+
template <typename Iter>
355+
diff_t gallopRight(ref_t key, Iter const base, diff_t const len, diff_t const hint, Compare compare) {
389356
GFX_TIMSORT_ASSERT(len > 0 && hint >= 0 && hint < len);
390357

391358
diff_t ofs = 1;
392359
diff_t lastOfs = 0;
393360

394-
if (comp_.lt(key, *(base + hint))) {
361+
if (compare(key, *(base + hint))) {
395362
diff_t const maxOfs = hint + 1;
396-
while (ofs < maxOfs && comp_.lt(key, *(base + (hint - ofs)))) {
363+
while (ofs < maxOfs && compare(key, *(base + (hint - ofs)))) {
397364
lastOfs = ofs;
398365
ofs = (ofs << 1) + 1;
399366

@@ -410,7 +377,7 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
410377
ofs = hint - tmp;
411378
} else {
412379
diff_t const maxOfs = len - hint;
413-
while (ofs < maxOfs && comp_.ge(key, *(base + (hint + ofs)))) {
380+
while (ofs < maxOfs && !compare(key, *(base + (hint + ofs)))) {
414381
lastOfs = ofs;
415382
ofs = (ofs << 1) + 1;
416383

@@ -427,10 +394,10 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
427394
}
428395
GFX_TIMSORT_ASSERT(-1 <= lastOfs && lastOfs < ofs && ofs <= len);
429396

430-
return std::upper_bound(base + (lastOfs + 1), base + ofs, key, comp_.less_function()) - base;
397+
return std::upper_bound(base + (lastOfs + 1), base + ofs, key, compare) - base;
431398
}
432399

433-
void mergeLo(iter_t const base1, diff_t len1, iter_t const base2, diff_t len2) {
400+
void mergeLo(iter_t const base1, diff_t len1, iter_t const base2, diff_t len2, Compare compare) {
434401
GFX_TIMSORT_ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2);
435402

436403
copy_to_tmp(base1, len1);
@@ -460,7 +427,7 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
460427
do {
461428
GFX_TIMSORT_ASSERT(len1 > 1 && len2 > 0);
462429

463-
if (comp_.lt(*cursor2, *cursor1)) {
430+
if (compare(*cursor2, *cursor1)) {
464431
*(dest++) = GFX_TIMSORT_MOVE(*(cursor2++));
465432
++count2;
466433
count1 = 0;
@@ -480,7 +447,7 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
480447
do {
481448
GFX_TIMSORT_ASSERT(len1 > 1 && len2 > 0);
482449

483-
count1 = gallopRight(*cursor2, cursor1, len1, 0);
450+
count1 = gallopRight(*cursor2, cursor1, len1, 0, compare);
484451
if (count1 != 0) {
485452
GFX_TIMSORT_MOVE_BACKWARD(cursor1, cursor1 + count1, dest + count1);
486453
dest += count1;
@@ -496,7 +463,7 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
496463
goto epilogue;
497464
}
498465

499-
count2 = gallopLeft(*cursor1, cursor2, len2, 0);
466+
count2 = gallopLeft(*cursor1, cursor2, len2, 0, compare);
500467
if (count2 != 0) {
501468
GFX_TIMSORT_MOVE_RANGE(cursor2, cursor2 + count2, dest);
502469
dest += count2;
@@ -536,7 +503,7 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
536503
}
537504
}
538505

539-
void mergeHi(iter_t const base1, diff_t len1, iter_t const base2, diff_t len2) {
506+
void mergeHi(iter_t const base1, diff_t len1, iter_t const base2, diff_t len2, Compare compare) {
540507
GFX_TIMSORT_ASSERT(len1 > 0 && len2 > 0 && base1 + len1 == base2);
541508

542509
copy_to_tmp(base2, len2);
@@ -574,7 +541,7 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
574541
do {
575542
GFX_TIMSORT_ASSERT(len1 > 0 && len2 > 1);
576543

577-
if (comp_.lt(*cursor2, *cursor1)) {
544+
if (compare(*cursor2, *cursor1)) {
578545
*(dest--) = GFX_TIMSORT_MOVE(*cursor1);
579546
++count1;
580547
count2 = 0;
@@ -597,7 +564,7 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
597564
do {
598565
GFX_TIMSORT_ASSERT(len1 > 0 && len2 > 1);
599566

600-
count1 = len1 - gallopRight(*cursor2, base1, len1, len1 - 1);
567+
count1 = len1 - gallopRight(*cursor2, base1, len1, len1 - 1, compare);
601568
if (count1 != 0) {
602569
dest -= count1;
603570
cursor1 -= count1;
@@ -613,7 +580,7 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
613580
goto epilogue;
614581
}
615582

616-
count2 = len2 - gallopLeft(*(cursor1 - 1), tmp_.begin(), len2, len2 - 1);
583+
count2 = len2 - gallopLeft(*(cursor1 - 1), tmp_.begin(), len2, len2 - 1, compare);
617584
if (count2 != 0) {
618585
dest -= count2;
619586
cursor2 -= count2;
@@ -661,7 +628,8 @@ template <typename RandomAccessIterator, typename LessFunction> class TimSort {
661628
}
662629

663630
// the only interface is the friend timsort() function
664-
template <typename IterT, typename LessT> friend void timsort(IterT first, IterT last, LessT c);
631+
template <typename IterT, typename LessT>
632+
friend void timsort(IterT first, IterT last, LessT c);
665633
};
666634

667635
template <typename RandomAccessIterator>
@@ -670,9 +638,9 @@ inline void timsort(RandomAccessIterator const first, RandomAccessIterator const
670638
timsort(first, last, std::less<value_type>());
671639
}
672640

673-
template <typename RandomAccessIterator, typename LessFunction>
674-
inline void timsort(RandomAccessIterator const first, RandomAccessIterator const last, LessFunction compare) {
675-
TimSort<RandomAccessIterator, LessFunction>::sort(first, last, compare);
641+
template <typename RandomAccessIterator, typename Compare>
642+
inline void timsort(RandomAccessIterator const first, RandomAccessIterator const last, Compare compare) {
643+
TimSort<RandomAccessIterator, Compare>::sort(first, last, compare);
676644
}
677645

678646
} // namespace gfx

0 commit comments

Comments
 (0)