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

Были предложения для C ++ «делегаты» которые имеют более низкие накладные расходы, чемboost::function:

Member Function Pointers and the Fastest Possible C++ Delegates Fast C++ Delegate The Impossibly Fast C++ Delegates

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

I want to know this specifically for the GCC compiler and libstdc++ on Intel 64-bit architectures, но информация о других компиляторах приветствуется (например, Clang).

 abarnert20 июн. 2012 г., 21:40
Из быстрой проверки версий libstdc ++ и libc ++, которые у меня есть (ни одна из которых не полностью обновлена), они оба, кажется, используют распределитель для создания хранилища для указателя на функцию-член, и я думаю, что ключевой бит во всех эти уловки избегают этого распределения.
 ildjarn20 июн. 2012 г., 20:57
std::function это интерфейс, а не реализация. Если вы хотите спросить о stdlib VC ++, libstdc ++ или libc ++specifically тогда это правильный вопрос, но ваш вопрос слишком широк
 Emile Cormier20 июн. 2012 г., 21:13
@abarnert: Я думал, что этот вопрос будет представлять общий интерес для программистов на C ++. Я думаю, что этот вопрос принадлежит StackOverflow, даже если я смогу найти ответ самостоятельно. StackOverflow - это больше, чем «исправить мою ошибку». сайт. :-) Если никто еще не знает ответ, я сам исследую это и опубликую свои выводы на благо сообщества.
 abarnert20 июн. 2012 г., 21:04
@ildjarn: я не вижу, как упоминание о том, что информация о других реализациях также приветствуется, разрушает иным образом действительный вопрос. Вы всегда можете проигнорировать эту часть и ответить конкретно на libstdc ++, если хотите.
 abarnert20 июн. 2012 г., 20:58
@ildjarn: прочитайте последнее предложение вопроса. Он спрашивает о конкретных реализациях и, в частности, о libstdc ++.

Ответы на вопрос(2)

Решение Вопроса

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

/**
 *  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. Функция для некоторого обоснования. Это означает, что весьма маловероятно, что GCCstd::function немного быстрее чемboost::functionпотому что это похожий дизайн одного и того же человека.

Нотабене наш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).

 09 окт. 2014 г., 21:08
Я только что совершил изменение, которое делает__is_location_invariant специализация выше не нужна, поэтому GCC 5.0 не будет выделять память дляInvokeA
 20 июн. 2012 г., 23:08
Нет, это не было бы инвариантом местоположения, потому что результат этого выражения связывания будет содержать указатель на функцию-членand копия вашего объекта! Я планирую увеличить размер объединения, чтобы он мог содержать указатель на член и указатель, чтобыbind(&O::f, &o) было бы хорошо, но это будет относительно сложно.
 Emile Cormier20 июн. 2012 г., 22:57
Ничего себе, прямиком изо рта лошади! Если бы я построилstd::function из этого выраженияstd::bind(&Object::memberFunction, objectInstance)будет ли это означать «инвариант местоположения»?
 Emile Cormier20 июн. 2012 г., 23:22
Я думаю, что вызов функции-члена, связанной с экземпляром объекта черезstd::function это довольно распространенный случай, поэтому я был бы очень рад увидетьstd::function реализация, которая позволяет избежать выделения кучи для этой ситуации.
 20 июн. 2012 г., 23:17
Да, есть оператор переключения. Для данного вызова используемый перечислитель известен во время компиляции. Вызов функции менеджера через указатель функции не будет происходить быстрее, чем виртуальный вызов, но это не единственные издержки. Избегать vtable выгодно, и только имеяone функция для вызоваall Операции могут иметь преимущества по сравнению с несколькими виртуальными функциями. TBH Я не тестировал его, я предполагал, что оригиналboost::function автор знал, что делал;)

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

Note: Implementations are encouraged to avoid the use of dynamically allocated memory for small callable objects, for example, where f’s target is an object holding only a pointer or reference to an object and a member function pointer. —end note

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

Ваш ответ на вопрос