Какова служебная нагрузка для std:: function?

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

Ответ 1

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

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

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

Ответ 2

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

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

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

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

http://drdobbs.com/cpp/232500059

Ответ 3

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

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

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

Ответ 4

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

Во-вторых: 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 по виртуальным вызовам, попробуйте их. Лично мне очень нравится идея не форсировать интерфейсы и наследование на клиентах моих классов.