Я реализую пользовательский итератор для контейнерного типа, отличного от STL, и столкнулся со следующим поведением, которое на данном этапе кажется мне немного неожиданным.
Похоже, что при определении «пустого» dtor происходит значительное снижение производительности? Почему ??
Чтобы попытаться разобраться в этом, я реализовал простой итератор для std :: vector, чтобы я мог сравнивать производительность напрямую со стандартным итератором STL. Ради справедливости теста я просто скопировал упрощенную реализацию из "vector.hpp" и экспериментировал с добавлением дополнительного "пустого" dtor:
template <typename _Myvec>
class my_slow_iterator // not inheriting from anything!!
{
public :
_Myvec::pointer _ptr; // pointer to vector element
/* All of the standard stuff - essentially from "vector.hpp" */
/* An additional empty dtor */
~my_slow_iterator () {}
};
Затем я изменил std :: vector, чтобы он мог возвращать мой новый тип итератора, и использовал следующее для сравнения: сортируйте вектор из 2000000 случайных целых чисел, усредненных по трем прогонам:
std::vector vec;
// fill via rand();
int tt = clock();
std::sort(vec.begin(), vec.end());
tt = clock() - tt; // elapsed time in ms
Я получил следующие результаты (VS2010, сборка выпуска, _ITERATOR_DEBUG_LEVEL 0 и т. Д.):
- Использование стандартного итератора STL: 550 мс.
- Использование
my_slow_iterator
с удаленным пустым dtor: 560 мс.
- Использование
my_slow_iterator
с включенным пустым dtor: 900 мс.
Похоже, что в этом случае пустой dtor вызывает замедление примерно на 40%.
Очевидно, что если dtor пуст, тогда зачем его иметь, но я ожидал, что такие простые «пустые» функции, как эта, будут встроены и оптимизированы во время компиляции. Если это не так, то я хотел бы понять, что происходит, если этот тип проблемы имеет последствия в более сложных случаях.
РЕДАКТИРОВАТЬ: составлено с оптимизацией O2.
РЕДАКТИРОВАТЬ: копать немного дальше, похоже, что аналогичный эффект происходит с копией ctor. Первоначально (и в тестах выше) my_slow_iterator
не определен copy-ctor, поэтому используется сгенерированный компилятором по умолчанию.
Если я определю следующий copy-ctor (который не делает больше, чем я ожидаю, сгенерированный компилятором):
my_slow_iterator (
const my_slow_iterator<_Myvec> &_src
) : _ptr(_src._ptr) {}
Я вижу следующие результаты для того же теста, что и выше:
- Использование
my_slow_iterator
, dtor удален, copy-ctor включен: 690ms
- Использование
my_slow_iterator
, включая dtor, включая copy-ctor: 980ms
Что является еще одним (хотя и не столь значительным) ударом по производительности.
Почему / как функции компилятора по умолчанию намного эффективнее ?? Делают ли пользовательские ctor / dtor'ы неявным образом что-то в фоновом режиме ??