以下代码展示了当使用std::sort()
对std::vector
进行排序时,std::vector
的访问行为会变慢。
#include <cstdio>
#include <chrono>
#include <random>
#include <cstdlib>
#include <cstring>
#include <algorithm>
constexpr auto NUM_KEYS(24000000);
constexpr auto CLOCK_MILI(CLOCKS_PER_SEC/1000);
constexpr auto CHARS("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
static int x = 0;
void insert(void* obj) {
std::size_t len = std::strlen((const char*)obj);
for(std::size_t i=0; i<len; ++i)
for(std::size_t j=0; j<len; ++j)
++x;
}
int main(void) {
std::vector<void*> list;
auto seed = std::chrono::system_clock::now().time_since_epoch().count();
std::mt19937_64 rng(seed);
std::uniform_int_distribution<int> rand_ch(0, 25);
std::uniform_int_distribution<std::size_t> rand_len(8, 16);
// Generate random string
for(std::size_t i=0; i<NUM_KEYS; ++i) {
std::size_t len = rand_len(rng);
char* buf = new char[len+1]();
for(std::size_t j=0; j<len; ++j)
buf[j] = CHARS[rand_ch(rng)];
list.push_back(buf);
}
// First traverse the list
std::clock_t cl = std::clock();
for(auto obj : list)
insert(obj);
printf("Time 1 = %ld miliseconds\n", (clock()-cl)/CLOCK_MILI);
// Sorting the list
std::sort(list.begin(), list.end(),
[](const void* a, const void* b) {
return std::strcmp((const char*)a, (const char*)b)<0;
});
// Second traverse the list
cl = std::clock();
for(auto obj : list)
insert(obj);
printf("Time 2 = %ld miliseconds\n", (clock()-cl)/CLOCK_MILI);
// Destroy the strings
for(auto obj : list)
delete[] (char*)obj;
return EXIT_SUCCESS;
}
遍历 list
并调用 insert()
的过程中尝试了 2 次迭代。函数 insert()
不会修改数据。第一次迭代在没有使用std::sort()
的情况下完成,第二次迭代在使用std::sort()
后完成。
在 GCC 11.1.0 中以选项 -std=c++17 -O3
执行时得到的结果:
Time 1 = 101 miliseconds
Time 2 = 909 miliseconds
同样地,当在运行时省略
std::sort()
时的结果:Time 1 = 102 miliseconds
Time 2 = 101 miliseconds
使用std::sort()
修改list
时,访问list
会慢9倍。 当std::sort()
被std :: random_shuffle()
或一些修改list
的代码替换时,会出现类似的结果。
- 那么到底发生了什么?
- 为什么在修改后遍历
std :: vector
会变慢?
insert(obj)
优化为等效的len = strlen(obj); x + = len * len;
。https://godbolt.org/z/8WeG9fYrT - Nate Eldredge