Какова стоимость вызова функции-члена класса через временный объект, если в классе нет переменных-членов? - PullRequest
3 голосов
/ 27 июня 2019

Недавно я изучал исходный код библиотеки ENTT и натолкнулся на нечто похожее на следующий фрагмент кода (обратите внимание, что я значительно упростил задачу, чтобы кратко изложить свой вопрос):

// Note that this class doesn't contain any member variables
class TextureLoader
{
public:

   TextureLoader() = default;
   ~TextureLoader() = default;

   std::shared_ptr<Texture> loadResource(const std::string& textureFilePath) const;
};

template<typename TResource, typename TResourceLoader, typename... Args>
std::shared_ptr<TResource> loadResource(Args&&... args)
{
   // Note how a temporary TResourceLoader is created to invoke its loadResource member function
   return TResourceLoader{}.loadResource(std::forward<Args>(args)...));
}

int main()
{
   std::string texFilePath = "tex.png";
   std::shared_ptr<Texture> myTexture = loadResource<Texture, TextureLoader>(texFilePath);
   return 0;
}

Как видите, шаблон функции loadResource способен загружать ресурсы любого типа (например, Texture, Shader, Model, Sound и т. Д.).В документации библиотеки говорится, что в идеале класс загрузчика не должен содержать никаких переменных-членов.Я предполагаю, что это происходит потому, что каждый раз, когда вызывается loadResource, создается временный объект переданного ему класса загрузчика для вызова его функции-члена loadResource.И вот где мой вопрос: какова стоимость TResourceLoader{}.loadResource()?Может ли компилятор удалить создание временного, потому что оно не содержит переменных-членов?Есть ли лучший способ для этого?

Спасибо за любую информацию!

Ответы [ 2 ]

1 голос
/ 27 июня 2019

Не должно быть никаких существенных последствий для производительности, хотя код будет очень незначительно наказываться. Чтобы лучше понять последствия, давайте попробуем разбить код на что-то похожее на сгенерированный код компилятора:

От:

return TResourceLoader{}.loadResource(std::forward<Args>(args)...));

Кому:

char Storage[1]; // Any object in C++ is at least 1 byte, including classes with no members
Storage(&Storage); // Pseudo-code illustrating calling constructor
loadResource(&Storage, <args>); // considering loadResource can't be inlined
Storage.~Storage();

В приведенном выше коде компилятор увидит, что как конструктор, так и деструктор являются значениями по умолчанию, и, поскольку класс не имеет члена, действительно тривиальны - так что их можно было бы безопасно опустить.

В результате вы должны выделить 1 байт в автоматическом хранилище, что в современных архитектурах обычно означает уменьшение регистра указателя стека с последующим его увеличением.

Это невероятно быстрая операция, но она все еще не мгновенная.

1 голос
/ 27 июня 2019

Да, компилятор оптимизирует создание временной переменной без каких-либо элементов данных.Там в основном не требуется Codegen для этого.Вы можете проверить это самостоятельно и поиграть с различными уровнями оптимизации в онлайн-инструменте, таком как Compiler Explorer .

...