Подтвердить что ты не робот

Были ли использованы идеи Fast Delegate (и др.) Для оптимизации std:: function?

Были предложения для делегатов С++, которые имеют меньшие накладные расходы, чем boost::function:

Были ли какие-либо из этих идей использованы для реализации std::function, что привело к лучшей производительности, чем boost::function? Кто-нибудь сравнивал производительность std::function vs boost::function?

Я хочу знать это специально для компилятора GCC и libstdС++ на 64-разрядных архитектурах Intel, но информация о других компиляторах приветствуется (например, Clang).

4b9b3361

Ответ 1

В libstdС++ std::function мы используем тип объединения, подходящий для размера и выравнивания для хранения указателей, указателей функций или указателей на функции-члены. Мы избегаем выделения кучи для любого объекта функции, который может быть сохранен в этом размере и выравнивании, , но, только если это "инвариант местоположения"

/**
 *  Trait identifying "location-invariant" types, meaning that the
 *  address of the object (or any of its members) will not escape.
 *  Also implies a trivial copy constructor and assignment operator.
 */

Код основан на реализации std::tr1::function, и эта часть существенно не изменилась. Я думаю, что это можно упростить с помощью std::aligned_storage, и его можно было бы улучшить, специализируясь на этом признаке, чтобы больше типов идентифицировались как инвариант местоположения.

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

Этот дизайн был внесен оригинальным автором boost::function, и я считаю, что он близок к ускоренной реализации. См. Производительность для Boost.Function для некоторого обоснования. Это означает, что вряд ли GCC std::function будет быстрее, чем boost::function, потому что это аналогичный дизайн того же человека.

N.B. наш std::function не поддерживает конструкцию с помощью распределителя, любые распределения, которые необходимо выполнить, будут выполняться с помощью new.


В ответ на комментарий Эмиля, выражающий желание избежать выделения кучи для std::function, который содержит указатель на функцию-член и объект, здесь немного взломать его (но вы не слышали его от меня; -)

struct A {
  int i = 0;
  int foo() const { return 0; }
};

struct InvokeA
{
  int operator()() const { return a->foo(); }
  A* a;
};

namespace std
{
  template<> struct __is_location_invariant<InvokeA>
  { static const bool value = true; };
}

int main()
{
  A a;
  InvokeA inv{ &a };

  std::function<int()> f2(inv);

  return f2();
}

Фокус в том, что InvokeA достаточно мал, чтобы помещаться в буфере объектов function, а специализация признаков говорит, что он безопасен для хранения там, поэтому function хранит копию этого объекта напрямую, а не на куче. Это требует, чтобы a сохранялся до тех пор, пока указатель на него сохраняется, но это было бы так или иначе, если цель function была bind(&A::foo, &a).

Ответ 2

Как отмечено в комментариях, std:: function - это только интерфейс, и разные реализации могут делать разные вещи, но стоит отметить, что стандарт действительно имеет что сказать об этом. От 20.8.11.2.1/5 (который больше похож на IP-адрес, чем на часть стандарта):

Примечание. Реализации рекомендуется избегать использования динамически выделенная память для небольших вызываемых объектов, например, где fs target - объект, содержащий только указатель или ссылку на объект и указатель функции-члена. -end note

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