Всегда было интересно, насколько дорого обходятся умные указатели, потому решил написать простенький тест, чтобы проверить насколько все хорошо или плохо. Для сравнение взял три разные структуры: классическиую структуру, структуру с raw-pointers и структуру с shared_ptr.
Сценарий такой: создаем и инициализируем массив обьектов, сортируем его, потом считываем его для вычисления средних значений и печатаем результаты и замеры времени исполнения. Код структур:
struct StudentId { char str[20]; }; struct StudentMarks { int math; int it; int lit; int pe; }; struct RecommendLetters { int amount; }; // plain struct struct StudentInfo { StudentId id; StudentMarks marks; RecommendLetters letters; }; // raw-pointer struct struct StudentInfoRP { StudentInfoRP() { id = new StudentId(); marks = new StudentMarks(); letters = new RecommendLetters(); } ~StudentInfoRP() { delete id; delete marks; delete letters; } StudentId* id; StudentMarks* marks; RecommendLetters* letters; }; // shared pointers struct struct StudentInfoSP { std::shared_ptr<StudentId> id = std::make_shared<StudentId>(); std::shared_ptr<StudentMarks> marks = std::make_shared<StudentMarks>(); std::shared_ptr<RecommendLetters> letters = std::make_shared<RecommendLetters>(); };Инициализация выглядит так:
std::vector<StudentInfo> studs(amount); for (int i = 0; i < amount; ++i) { StudentInfo& stud = studs[i]; sprintf_s(stud.id.str, sizeof(stud.id) / sizeof(char), "%03d-%014d", rand() % 100, i); stud.marks.math = rand() % 10; stud.marks.it = rand() % 10; stud.marks.lit = rand() % 10; stud.marks.pe = rand() % 10; stud.letters.amount = (rand() % 20 == 5) ? rand() % 4 : 0; }
Сортировка:
std::sort(studs.begin(), studs.end(), [](const StudentInfo& a, const StudentInfo& b) { if (a.marks.math != b.marks.math) { return a.marks.math < b.marks.math; } else if (a.marks.it != b.marks.it) { return a.marks.it < b.marks.it; } else if (a.marks.lit != b.marks.lit) { return a.marks.lit < b.marks.lit; } else if (a.marks.pe != b.marks.pe) { return a.marks.pe < b.marks.pe; } else if (a.letters.amount != b.letters.amount) { return a.letters.amount < b.letters.amount; } else if (strcmp(a.id.str, b.id.str) != 0) { return strcmp(a.id.str, b.id.str) < 0; } });
В общем, ничего необычного. Аналогично все и для других двух структур, только там , посколько это указатели, обращение идет через стрелку. Время засекал очень просто:
auto start = std::chrono::steady_clock::now(); std::vector<StudentInfo> studs(amount); // do some.. auto end = std::chrono::steady_clock::now(); const std::chrono::duration<double> delta = end - start;
Как видно, вот всех дисциплинах убедительную победу одержал вариант с простыми структурами, значительно опередив вариант с raw-pointer и shared-pointer, причем сортировка была быстрее многократно, чтение значений быстрее на порядок, а удаление обьектов вообще в тысячи раз быстрее. Если ваши данные скомпонованы подряд, компактно в памяти и вы обращаетесь к ним напрямую, а не не через ссылку/указатель - то это путь к высокой скорости! Но нас интересует разница между сырыми указателями и умными указателями - она на самом деле оказалась не так и значительна: сортировка 10 миллионов элементов raw-pointer оказалась всего в 2 раза быстрее, чем сортировка shared-pointer. Создание и удаление и тех и других почти не отличается.
Вывод: отказ от умных указателей не даст вам значительного прироста производительности, ну кроме каких-то очень-очень специальных случаев.
P.S.
Код находится здесь. Тестовая платформа: Ryzen 7 3700X, 16GB RAM
Комментариев нет:
Отправить комментарий