2015-12-21 2 views
4

Я посмотрел на другой вопрос переполнения стека относительно std :: function и почему он медленный, но я все еще не убежден/не понимаю. Я запустил программу с вопросом с несколькими изменениями.std :: function performance по сравнению с шаблонами

#include <iostream> 
#include <functional> 
#include <string> 
#include <chrono> 

template <typename F> 
float calc1(F f) { return -1.0f * f(3.3f) + 666.0f; } 

float calc2(const std::function<float (float)>& f) { return -1.0f * f(3.3f) + 666.0f; } 
int main() { 

    std::function<float (float)> f = [](float arg){ return arg * 0.5f; }; 
    for (int i = 0; i < 1e9; ++i) { 
     // calc2(f); 
     calc1([](float arg){ return arg * 0.5f; }); 
    } 

    return 0; 
} 

С шаблонной версией, код работает в 4-х секунд, но с станд :: функциями задействована во время выполнения увеличивается до 15 секунд. Я понимаю, почему копирование функции std :: может быть дорогостоящим, но здесь даже с передачей ссылки, кажется, нет никакой разницы, может кто-нибудь объяснить, почему это происходит?

Просто для справки это выход, когда я набираю в г ++ --version

Apple LLVM version 7.0.2 (clang-700.1.81) 
+0

И вы скомпилировали с включенными оптимизациями? – Cornstalks

+0

Yep с оптимизацией, включенной в обычный код, занимает всего 0,4 секунды, я полагаю, что ничего не выполняется вообще, поскольку в другом месте кода нет зависимости, но с помощью std :: function время выполнения составляет 1,9 секунды – Curious

+0

@NirFriedman: Это другое. 'std :: function' больше, чем просто функтор. Он использует стирание типа, что является существенной разницей. – Cornstalks

ответ

3

Когда я скомпилировать программу (с -O3 оптимизации) и использовать calc1, время выполнения составляет 0.0 секунды. Это связано с тем, что компилятор может полностью оптимизировать код. Он знает, что ваш код на самом деле не do ничего, поэтому нет смысла запускать его.

Когда я скомпилирую вашу программу (опять же с оптимизацией -O3) и использую calc2 (который использует std::function), программа занимает 2 секунды. Причина, по которой требуется больше времени, заключается в том, что оптимизатор не может все оптимизировать. std::function работает во время выполнения (не время компиляции, потому что он должен делать тип erasure; см. this question и this question), и в целом оптимизатор не может встроить (или полностью оптимизировать) вызовы, которые проходят через std::function (в этой ситуации, технически это возможно для оптимизатора, поскольку это простая программа, но это не так).


Причина std::function вызовы не могут быть встраиваемыми потому, что компилятор не всегда знает, что std::function будет делать. В этом коде достаточно просто, чтобы статический анализатор компилятора мог, если бы он был достаточно «умным», фактически встроить все это и затем оптимизировать его.

Но это может быть сложной задачей для реализации в компиляторе, и это не делает большой разницы в «реальных» программах, которые являются более сложными. В более сложных программах на самом деле невозможно узнать, что сделает std::function. Например, представьте себе, что у вас есть второй файл .cpp, который вызывает calc2 с разнымиstd::function. Или представьте, если вы установили std::function в один из двух разных лямбдов, в зависимости от ввода пользователем. Компилятор не знал бы, на какую лямбду фактически позвонить, пока программа не запустится, поэтому она не может просто оптимизировать все. Из-за проблем, подобных этому, на самом деле не стоит прилагать глубокий статический анализ для std::function, который полностью оптимизировал бы ваш простой код.

+0

Спасибо! Я посмотрел на вторую ссылку, и я все еще не понимаю. Почему компилятор не сможет оптимизировать вызов функции? – Curious

+0

@Curious: Я попытался добавить некоторую расширенную информацию. Дайте мне знать, если у вас остались вопросы. – Cornstalks

+0

@downvoter: Я в порядке с вами, но не мог бы вы оставить комментарий, чтобы мы все могли узнать, что плохого/плохого в моем ответе? – Cornstalks

Смежные вопросы