两者都应该在O(n log n)中运行,但一般排序比stable_sort快。实践中的性能差距有多大?你对此有一些经验吗?
我想对大量大小约为20字节的结构进行排序。结果的稳定性在我的情况下会很好,但它不是必须的。目前底层容器是一个普通数组,也许稍后可以将其更改为std :: deque。
答案 0 :(得分:16)
std::stable_sort
执行NlogN比较。当内存不足时,它会降级为N((logN)^ 2)比较。因此,当存储器可用时,它与std::sort
(在平均情况和最差情况下执行O(NlogN)比较)的效率大致相同。
对于那些感兴趣的人,sort()使用introsort(当递归到达某个深度时切换到heapsort的quicksort)和stable_sort()使用merge sort。
答案 1 :(得分:16)
有理由比较算法的好答案。出于好奇,我将std::sort
和std::stable_sort
与google/benchmark进行了基准比较。
提前指出是很有用的;
1 X 2500 MHz CPU
和1 GB RAM
Arch Linux 2015.08 x86-64
g++ 5.3.0
和clang++ 3.7.0
(-std=c++11
,-O3
和-pthread
)编制的基准BM_Base*
基准测试用于衡量填充std::vector<>
的时间。应该从排序结果中减去该时间以便更好地进行比较。 第一个基准测试std::vector<int>
的大小为512k
。
[ g++ ]# benchmark_sorts --benchmark_repetitions=10
Run on (1 X 2500 MHz CPU )
2016-01-08 01:37:43
Benchmark Time(ns) CPU(ns) Iterations
----------------------------------------------------------------
...
BM_BaseInt/512k_mean 24730499 24726189 28
BM_BaseInt/512k_stddev 293107 310668 0
...
BM_SortInt/512k_mean 70967679 70799990 10
BM_SortInt/512k_stddev 1300811 1301295 0
...
BM_StableSortInt/512k_mean 73487904 73481467 9
BM_StableSortInt/512k_stddev 979966 925172 0
[ clang++ ]# benchmark_sorts --benchmark_repetitions=10
Run on (1 X 2500 MHz CPU )
2016-01-08 01:39:07
Benchmark Time(ns) CPU(ns) Iterations
----------------------------------------------------------------
...
BM_BaseInt/512k_mean 26198558 26197526 27
BM_BaseInt/512k_stddev 320971 348314 0
...
BM_SortInt/512k_mean 70648019 70666660 10
BM_SortInt/512k_stddev 2030727 2033062 0
...
BM_StableSortInt/512k_mean 82004375 81999989 9
BM_StableSortInt/512k_stddev 197309 181453 0
第二个基准测试std::vector<S>
512k
尺寸(sizeof(Struct S) = 20
)排序。
[ g++ ]# benchmark_sorts --benchmark_repetitions=10
Run on (1 X 2500 MHz CPU )
2016-01-08 01:49:32
Benchmark Time(ns) CPU(ns) Iterations
----------------------------------------------------------------
...
BM_BaseStruct/512k_mean 26485063 26410254 26
BM_BaseStruct/512k_stddev 270355 128200 0
...
BM_SortStruct/512k_mean 81844178 81833325 8
BM_SortStruct/512k_stddev 240868 204088 0
...
BM_StableSortStruct/512k_mean 106945879 106857114 7
BM_StableSortStruct/512k_stddev 10446119 10341548 0
[ clang++ ]# benchmark_sorts --benchmark_repetitions=10
Run on (1 X 2500 MHz CPU )
2016-01-08 01:53:01
Benchmark Time(ns) CPU(ns) Iterations
----------------------------------------------------------------
...
BM_BaseStruct/512k_mean 27327329 27280000 25
BM_BaseStruct/512k_stddev 488318 333059 0
...
BM_SortStruct/512k_mean 78611207 78407400 9
BM_SortStruct/512k_stddev 690207 372230 0
...
BM_StableSortStruct/512k_mean 109477231 109333325 8
BM_StableSortStruct/512k_stddev 11697084 11506626 0
任何喜欢运行基准测试的人都是代码,
#include <vector>
#include <random>
#include <algorithm>
#include "benchmark/benchmark_api.h"
#define SIZE 1024 << 9
static void BM_BaseInt(benchmark::State &state) {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> dist;
while (state.KeepRunning()) {
std::vector<int> v;
v.reserve(state.range_x());
for (int i = 0; i < state.range_x(); i++) {
v.push_back(dist(mt));
}
}
}
BENCHMARK(BM_BaseInt)->Arg(SIZE);
static void BM_SortInt(benchmark::State &state) {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> dist;
while (state.KeepRunning()) {
std::vector<int> v;
v.reserve(state.range_x());
for (int i = 0; i < state.range_x(); i++) {
v.push_back(dist(mt));
}
std::sort(v.begin(), v.end());
}
}
BENCHMARK(BM_SortInt)->Arg(SIZE);
static void BM_StableSortInt(benchmark::State &state) {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> dist;
while (state.KeepRunning()) {
std::vector<int> v;
v.reserve(state.range_x());
for (int i = 0; i < state.range_x(); i++) {
v.push_back(dist(mt));
}
std::stable_sort(v.begin(), v.end());
}
}
BENCHMARK(BM_StableSortInt)->Arg(SIZE);
struct S {
int key;
int arr[4];
};
static void BM_BaseStruct(benchmark::State &state) {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> dist;
while (state.KeepRunning()) {
std::vector<S> v;
v.reserve(state.range_x());
for (int i = 0; i < state.range_x(); i++) {
v.push_back({dist(mt)});
}
}
}
BENCHMARK(BM_BaseStruct)->Arg(SIZE);
static void BM_SortStruct(benchmark::State &state) {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> dist;
while (state.KeepRunning()) {
std::vector<S> v;
v.reserve(state.range_x());
for (int i = 0; i < state.range_x(); i++) {
v.push_back({dist(mt)});
}
std::sort(v.begin(), v.end(),
[](const S &a, const S &b) { return a.key < b.key; });
}
}
BENCHMARK(BM_SortStruct)->Arg(SIZE);
static void BM_StableSortStruct(benchmark::State &state) {
std::random_device rd;
std::mt19937 mt(rd());
std::uniform_int_distribution<int> dist;
while (state.KeepRunning()) {
std::vector<S> v;
v.reserve(state.range_x());
for (int i = 0; i < state.range_x(); i++) {
v.push_back({dist(mt)});
}
std::stable_sort(v.begin(), v.end(),
[](const S &a, const S &b) { return a.key < b.key; });
}
}
BENCHMARK(BM_StableSortStruct)->Arg(SIZE);
BENCHMARK_MAIN();
答案 2 :(得分:10)
足够大,可以保证一个单独的函数可以进行稳定的排序,而不是std::sort()
透明地执行它。
答案 3 :(得分:9)
有时需要std :: stable_sort(),因为它维护了相等元素的顺序。
传统的建议是,如果维护订单不重要,则应使用std :: sort()代替。
但是,它依赖于上下文。即使您不需要维护订单,也有大量数据可以通过稳定排序进行最佳排序:
如果数据的枢轴点一直很差,Quicksort会迅速成为最糟糕的表现。
Burrows-Wheeler Transform是用作数据压缩的一部分的算法,例如bzip2。它需要对文本的所有旋转进行排序。对于大多数文本数据,合并排序(通常由std :: stable_sort()使用)比快速排序(通常由std :: sort()使用)快。
bbb是一个BWT实现,它注意到std :: stable_sort()优于此应用程序的sort()的优点。
答案 4 :(得分:2)
性能差距有多大 实践?你有一些经验吗? 关于那个?
是的,但它并没有像你期望的那样。
我采用了Burrows-Wheeler转换和C ++的C实现 - 如果它。结果比C代码慢很多(尽管代码更清晰)。所以我把定时仪器放在那里,看起来qsort的执行速度比std :: sort快。这是在VC6中运行的。然后使用stable_sort重新编译,测试运行速度比C版本快。其他优化设法推动C ++版本比C版本快25%。我认为有可能提高速度,但代码的清晰度正在消失。
答案 5 :(得分:1)
如果要对大量结构进行排序,则内存/磁盘的IO速度开始变得比渐近运行时更重要。此外,还应考虑内存使用情况。
我在2Gb数据(64B结构)上尝试过std :: stable_sort,不知道std :: stable_sort是否创建了数据的内部副本。随后的交换垃圾几乎锁定了我的电脑。
使用unstable std :: sort可将内存使用量减少2倍,这在排序大型数组时非常有用。我终止了std :: stable_sort,所以我无法确定它的速度有多慢。但是,如果不需要稳定排序,那么我认为最好使用不稳定的std :: sort。
答案 6 :(得分:1)
正在寻找类似的东西 - 但很惊讶没有人谈到辅助空间。
我相信 - stable_sort和sort的实现应该保证所有(Best,Average和Worst)案例的O(NlogN)。
但是,使用的辅助空间存在差异。 stable_sort需要一个O(N)的辅助空间。
可能性能上的差异在于获得那个空间。 :)
否则,从理论上讲 - 它们应该与w.r.t表现相同。
sort应该做你需要的 - &gt; stable_sort保留具有等效值的元素的相对顺序。