Каковы издержки производительности std :: function? - PullRequest
57 голосов
/ 20 февраля 2011

Я слышал на форуме с использованием std::function<> вызывает снижение производительности.Это правда?Если это правда, это большое падение производительности?

Ответы [ 4 ]

74 голосов
/ 01 февраля 2012

Действительно, с std:function существуют проблемы с производительностью, которые необходимо учитывать при его использовании. Основная сила std::function, а именно его механизм стирания типов, не предоставляется бесплатно, и мы могли бы (но не обязательно должны) заплатить за это цену.

std::function - это шаблонный класс, который оборачивает вызываемые типы. Однако он не параметризован для самого вызываемого типа, а только для его типов возврата и аргументов. Вызываемый тип известен только во время создания, и поэтому std::function не может иметь предварительно объявленный член этого типа для хранения копии объекта, переданного его конструктору.

Грубо говоря (на самом деле все сложнее, чем это) std::function может содержать только указатель на объект, переданный его конструктору, и это вызывает проблему на всю жизнь. Если указатель указывает на объект, время жизни которого меньше, чем у объекта std::function, то внутренний указатель станет висящим. Чтобы предотвратить эту проблему, std::function может сделать копию объекта в куче посредством вызова operator new (или пользовательского распределителя). Динамическое распределение памяти - это то, что люди больше всего называют ухудшением производительности, подразумеваемым std::function.

Недавно я написал статью с более подробной информацией, которая объясняет, как (и где) можно избежать расплаты за выделение памяти.

http://drdobbs.com/cpp/232500059

14 голосов
/ 20 февраля 2011

Вы можете найти информацию из справочных материалов наддува: Сколько накладных расходов делает вызов через boost :: function incur? и Производительность

Это неопределите "да или нет", чтобы повысить функцию.Падение производительности может быть вполне приемлемым, учитывая требования программы.Чаще всего части программы не являются критичными для производительности.И даже тогда это может быть приемлемым.Это только то, что вы можете определить.

Что касается стандартной версии библиотеки, стандарт определяет только интерфейс.Это полностью зависит от отдельных реализаций, чтобы заставить его работать.Я полагаю, что будет использована аналогичная реализация функции boost.

11 голосов
/ 20 февраля 2011

Это сильно зависит от того, передаете ли вы функцию без привязки какого-либо аргумента (не выделяет кучи) или нет.

Также зависит от других факторов, но это главный.

Это правда, что вам нужно что-то для сравнения, вы не можете просто сказать, что это «снижает накладные расходы» по сравнению с тем, чтобы вообще не использовать его, вам нужно сравнить это с использованием альтернативного способа передачи функции. И если вы можете просто отказаться от его использования, то это не было необходимо с самого начала

9 голосов
/ 18 января 2012

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

Во-вторых: g ++ 4.5 не показывает никакой разницы по сравнению с виртуальными функциями:

main.cc

#include <functional>
#include <iostream>

// Interface for virtual function test.
struct Virtual {
    virtual ~Virtual() {}
    virtual int operator() () const = 0;
};

// Factory functions to steal g++ the insight and prevent some optimizations.
Virtual *create_virt();
std::function<int ()> create_fun();
std::function<int ()> create_fun_with_state();

// The test. Generates actual output to prevent some optimizations.
template <typename T>
int test (T const& fun) {
    int ret = 0;
    for (int i=0; i<1024*1024*1024; ++i) {
        ret += fun();
    }    
    return ret;
}

// Executing the tests and outputting their values to prevent some optimizations.
int main () {
    {
        const clock_t start = clock();
        std::cout << test(*create_virt()) << '\n';
        const double secs = (clock()-start) / double(CLOCKS_PER_SEC);
        std::cout << "virtual: " << secs << " secs.\n";
    }
    {
        const clock_t start = clock();
        std::cout << test(create_fun()) << '\n';
        const double secs = (clock()-start) / double(CLOCKS_PER_SEC);
        std::cout << "std::function: " << secs << " secs.\n";
    }
    {
        const clock_t start = clock();
        std::cout << test(create_fun_with_state()) << '\n';
        const double secs = (clock()-start) / double(CLOCKS_PER_SEC);
        std::cout << "std::function with bindings: " << secs << " secs.\n";
    }
}

impl.cc

#include <functional>

struct Virtual {
    virtual ~Virtual() {}
    virtual int  operator() () const = 0;
};
struct Impl : Virtual {
    virtual ~Impl() {}
    virtual int  operator() () const { return 1; }
};

Virtual *create_virt() { return new Impl; }

std::function<int ()> create_fun() { 
    return  []() { return 1; };
}

std::function<int ()> create_fun_with_state() { 
    int x,y,z;
    return  [=]() { return 1; };
}

Выход g++ --std=c++0x -O3 impl.cc main.cc && ./a.out:

1073741824
virtual: 2.9 secs.
1073741824
std::function: 2.9 secs.
1073741824
std::function with bindings: 2.9 secs.

Так что, не бойтесь.Если ваш дизайн / удобство обслуживания могут быть лучше, чем предпочтение std::function виртуальным вызовам, попробуйте их.Лично мне очень нравится идея не навязывать интерфейсы и наследование клиентам моих классов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...