name keywords etiquetas etiqueta ejemplos c++ performance boost c++11 std-function

c++ - keywords - seo meta tags



¿Se han utilizado las ideas detrás de Fast Delegate(et al) para optimizar std:: function? (2)

Como se señaló en los comentarios, std :: function es solo una interfaz, y diferentes implementaciones pueden hacer cosas diferentes, pero vale la pena señalar que la norma realmente tiene algo que decir sobre este asunto. Desde 20.8.11.2.1 / 5 (que se parece más a una dirección IP que a una parte del estándar):

Nota: Se recomienda a las implementaciones que eviten el uso de la memoria asignada dinámicamente para objetos pequeños que se pueden llamar, por ejemplo, donde el objetivo f es un objeto que contiene solo un puntero o referencia a un objeto y un puntero a la función miembro. Nota final

Esta es la forma de la norma de alentar a los implementadores a emplear la "optimización de la función pequeña", que fue motivada por los artículos citados sobre los delegados. (Los artículos en sí mismos no hablan de delegados en el sentido .NET, sino que usan el término "delegado" para referirse a las funciones de miembros vinculados).

Ha habido propuestas para "delegados" de C ++ que tienen una sobrecarga menor que boost::function

¿Alguna de esas ideas se ha utilizado para implementar std::function , lo que resulta en un mejor rendimiento que boost::function ? ¿Alguien ha comparado el rendimiento de std::function vs boost::function ?

Quiero saber esto específicamente para el compilador GCC y libstdc ++ en las arquitecturas Intel de 64 bits , pero la información sobre otros compiladores es bienvenida (como Clang).


En la std::function libstdc ++ utilizamos un tipo de unión que está adecuadamente dimensionado y alineado para almacenar punteros, punteros de funciones o punteros a las funciones de los miembros. Evitamos una asignación de montón para cualquier objeto de función que se pueda almacenar en ese tamaño y alineación, pero solo si es "invariante en la ubicación"

/** * 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. */

El código se basa en la implementación std::tr1::function y esa parte no ha cambiado significativamente. Creo que se podría simplificar usando std::aligned_storage y podría mejorarse especializando el rasgo para que se identifiquen más tipos como invariantes de ubicación.

La invocación del objeto de destino se realiza sin llamadas a funciones virtuales, el borrado de tipo se realiza almacenando un único puntero de función en la std::function que es la dirección de una especialización de plantilla de función. Todas las operaciones se realizan llamando a esa plantilla de función a través del puntero almacenado y pasando una enumeración que identifica qué operación se le pide que realice. Esto significa que no hay vtable y solo se necesita almacenar un único puntero de función en el objeto.

Este diseño fue contribuido por el autor original de boost::function y creo que está cerca de la implementación de impulso. Consulte los documentos de Performance para la función Boost.Function para algunos fundamentos. Eso significa que es bastante improbable que la std::function GCC sea más rápida que boost::function porque es un diseño similar de la misma persona.

Nota: nuestra std::function no admite la construcción con un asignador aún, las asignaciones que necesite hacer se realizarán con new .

En respuesta al comentario de Emile que expresa un deseo de evitar una asignación de montón para una std::function que tiene un puntero a la función miembro y un objeto, aquí hay un pequeño truco para hacerlo (pero no me lo escuchaste ;-)

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(); }

El truco es que InvokeA es lo suficientemente pequeño como para caber en el pequeño búfer de objetos de la function , y la especialización de rasgos dice que es seguro almacenarlo allí, por lo que la function almacena una copia de ese objeto directamente, no en el montón. Esto requiere que persista mientras el puntero persista, pero ese sería el caso si el objetivo de la function fuera bind(&A::foo, &a) .