上周,刚上线的项目中,发现推荐结果排序不稳定,如果用stable_sort()性能会比降低,然后我就修改了比较函数,如果评分相等,比较上架时间
auto cmp = [](const GidScore& a, const GidScore& b) {
if (a.score == b.score) {
return a.create_time >= b.create_time; //!!!
}
return a.score > b.score;
}
上面代码看起来没啥问题,但是测试时发现遇到特定的数据会导致出core文件,通过gdb查看core文件不能直接看不来具体的原因,打log发现,排序后有个元素内容被修改了。然后我试着把>=改为>好了。后来google发现,也有其他人也会遇到这个问题,是因为cmp函数导致的。
函数原型:
void sort (RandomAccessIterator first, RandomAccessIterator last, Compare comp);
对于参数comp的描述里有几个斜体:strict weak ordering
严格的弱序列
google后发现strict weak ordering具有以下性质:
- f(x, x) must be false.
- f(x, y) implies !f(y, x)
- f(x, y) and f(y, z) imply f(x, z).
- if x is equivalent to y and y is equivalent to z, then x is equivalent to z.
如果我们的cmp函数里使用了>=则违反了1和2。所以比较时只能用>或<.
再看下为什么会导致出core呢。
sort代码:
inline void sort(_RandomAccessIter __first, _RandomAccessIter __last,
_Compare __comp) {
if (__first != __last) {
__introsort_loop(__first, __last,
__VALUE_TYPE(__first),
__lg(__last - __first) * 2,
__comp);
__final_insertion_sort(__first, __last, __comp);
}
}
sort先是调用了__introsort_loop
然后是__final_insertion_sort
,
__introsort_loop 函数中运用了快速排序,但是快速排序存在一问题是,如果一个本来基本有序,则性能会很差,所以函数中加入了递归深度判断,如果深度大于lg(n)+1则采用堆排序,以提高性能,同时在选取中间值时用了三点中值(该算法在绝大多数数据中表现较好),经历多次调用最后会调用到__unguarded_linear_insert:
void __unguarded_insertion_sort_aux(_RandomAccessIter __first,
_RandomAccessIter __last,
_Tp*, _Compare __comp) {
for (_RandomAccessIter __i = __first; __i != __last; ++__i)
__unguarded_linear_insert(__i, _Tp(*__i), __comp);
}
void __unguarded_linear_insert(_RandomAccessIter __last, _Tp __val,
_Compare __comp) {
_RandomAccessIter __next = __last;
--__next;
while (__comp(__val, *__next)) {
*__last = *__next;
__last = __next;
--__next;
}
*__last = __val;
}
该函数中用cmp去从后往前挨个比较,如果_comp一直返回true,则一直__next--,如果使用>=,数组元素全部为相等,则返回的next指针一直递减,除非访问到非法地址才会停下来。所以在数组全部相同时,>=会导致崩溃。
网友评论