#include <cstdlib>
#include <algorithm>
#include <chrono>
#include <iostream>
#include <iomanip>
#include <numeric>
#include <string>
#include <thread>
#include <vector>
struct Obj
{
Obj* next;
unsigned x;
Obj() : x(::rand()) {}
void update()
{
++x; // как-то меняем данные объекта
}
};
template<typename F>
void test(const char* name, F f)
{
std::vector<unsigned> times(100);
for (auto& dt : times)
{
std::this_thread::yield();
auto t1 = std::chrono::high_resolution_clock::now();
f();
auto t2 = std::chrono::high_resolution_clock::now();
dt = (unsigned)std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
}
std::cout << name << ": " << std::accumulate(times.begin(), times.end(), 0) / times.size() << " ms" << std::endl;
}
int main()
{
std::srand((unsigned)std::time(nullptr));
std::vector<Obj> vObj(1 * 1000 * 1000);
std::vector<Obj*> vPtr;
for (auto& o : vObj)
vPtr.push_back(&o);
std::random_shuffle(vPtr.begin(), vPtr.end()); // убиваем memory locality при последовательном доступе
Obj* lst = nullptr;
for (auto p : vPtr)
{
p->next = lst;
lst = p;
}
test("vector<Obj>", [&]{ for (auto& o : vObj) o.update(); });
test("vector<Obj*>", [&]{ for (auto& o : vPtr) o->update(); });
test("intrusive list", [&]{ for (auto o = lst; o; o = o->next) o->update(); });
}
Типичный результат на моей машине
vector<Obj>: 0 ms
vector<Obj*>: 10 ms
intrusive list: 76 ms
Почему так?
В случае с vector<Obj> объект лежат друг за другом, и эффективно читаются в кеш процессора
У vector<Obj*> перемешаны указатели, по этому кешировать доступ к данным объектов не получается.
Однако, сам vector<Obj*> прекрасно кешируется, а вот для связанного списка кеширование не работает вообще.