Задать вопрос
Lite_stream
@Lite_stream

Почему T * может работать ощутимо быстрее (~ на 25-30%) в качестве хранилища данных, чем std::byte *?

Есть некоторая шаблонная структура данных, где в качестве массива для данных T использовался T * или std::byte * .

После множества прогонов (версия: С++ 20 с флагом -O3, компилятор Clang) бенчмарков на большом количестве данных (особенно большая разница была для типов std::is_scalar (порядка 25%), и порядка 10% для пользовательских типов) заметил, что когда в качестве массива используется T *, а не std::byte *, появляется ощутимый прирост прозводительности.

При переходе на T * с std::byte * изменились только следующие участки кода:

Инициализация массива
buffer(new (std::align_val_t(alignof(T))) std::byte[initialCapacity * sizeof(T)])
// Изменилась на 
buffer(reinterpret_cast<T *>(new (std::align_val_t(alignof(T))) std::byte[initialCapacity * sizeof(T)])


И несколько сравнений
FirstComparator{}((*reinterpret_cast<T *>(&buffer[(position * sizeof(T)) * 2])), (*reinterpret_cast<T *>(&buffer[((position * 2 + 1) * sizeof(T))])))
// Поменялось на 
FirstComparator{}(buffer[position * 2], buffer[position * 2 + 1])


Откуда могла возникнуть такая разница? Может быть, компилятор, зная точный тип, а не std:: byte *, делает какие-то оптимизации ? Полагаю, что с T * у компилятора есть больше инфы (в частности, сам тип T, а не std::byte), на основе этого он делает какие-то доп. оптимизации

P.S.: посмотрел код EASTL, там тоже, например в векторе, используется T *, а не char *
  • Вопрос задан
  • 296 просмотров
Подписаться 3 Простой 14 комментариев
Решения вопроса 1
wataru
@wataru Куратор тега C++
Разработчик на С++, экс-олимпиадник.
Надо смотреть, что там компилятор нагенерировал.

Создайте 2 функции, которые отличаются только вот в этих вот местах.
Вставьте код в https://godbolt.org/

Смотрите ассемблерный код для двух функций.

Может, срабатывает strict aliasing. Видя тип T сомпилятор понимает, что эта переменная не может быть изменена какими-то другими std::byte в соседнем коде и может, например, пропустить загрузку-выгрузку данных в регистр из памяти.

Может вообще что-то другое.

Единственный вариант разобраться - это смотреть на ассемблерный код функций, которые вы и сравниваете. Не каких-то кусков, оттуда надерганных, а функций целиком.
Ответ написан
Комментировать
Пригласить эксперта
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Похожие вопросы