tipos - Evaluación diferida en C++ 14/17-¿solo lambdas o también futuros, etc.?
tipos de evaluacion pdf (2)
Cuando escribes
auto unevaluted_x = []() { return foo(); };
...
auto x = unevaluted_x();
Cada vez que desea obtener el valor (cuando llama a unevaluated_x
) se calcula, desperdiciando recursos computacionales. Por lo tanto, para deshacerse de este trabajo excesivo, es una buena idea hacer un seguimiento de si el lambda ya ha sido llamado (tal vez en otro hilo, o en un lugar muy diferente en la base de código). Para hacerlo, necesitamos algo de envoltura alrededor de lambda:
template<typename Callable, typename Return>
class memoized_nullary {
public:
memoized_nullary(Callable f) : function(f) {}
Return operator() () {
if (calculated) {
return result;
}
calculated = true;
return result = function();
}
private:
bool calculated = false;
Return result;
Callable function;
};
Tenga en cuenta que este código es solo un ejemplo y no es seguro para subprocesos.
Pero en lugar de reinventar la rueda, puedes usar std::shared_future
:
auto x = std::async(std::launch::deferred, []() { return foo(); }).share();
Esto requiere menos código para escribir y admite algunas otras características (como, verificar si el valor ya se ha calculado, seguridad de hilo, etc.).
Hay el siguiente texto en el estándar [futures.async, (3.2)]:
Si
launch::deferred
está establecido en política, almacenaDECAY_COPY(std::forward<F>(f))
yDECAY_COPY(std::forward<Args>(args))...
en el estado compartido. Estas copias def
yargs
constituyen una función diferida. La invocación de la función diferida evalúaINVOKE(std::move(g), std::move(xyz))
dondeg
es el valor almacenado deDECAY_COPY(std::forward<F>(f))
yxyz
es la copia almacenada deDECAY_COPY(std::forward<Args>(args))....
Cualquier valor de retorno se almacena como resultado en el estado compartido. Cualquier excepción propagada desde la ejecución de la función diferida se almacena como el resultado excepcional en el estado compartido. El estado compartido no está listo hasta que la función se haya completado. La primera llamada a una función de espera no temporizada (30.6.4) en un objeto de retorno asíncrono que hace referencia a este estado compartido invocará la función diferida en el hilo que llamó a la función de espera . Una vez queINVOKE(std::move(g),std::move(xyz))
evaluación deINVOKE(std::move(g),std::move(xyz))
, la función ya no se considera diferida. [Nota: si esta política se especifica junto con otras políticas, como cuando se usa un valor de política delaunch::async | launch::deferred
launch::async | launch::deferred
implementaciones delaunch::async | launch::deferred
deben diferir la invocación o la selección de la política cuando no se puede explotar de manera efectiva más concurrencia. -finalizar nota]
Por lo tanto, tiene la garantía de que no se llamará al cálculo antes de que sea necesario.
Acabo de leer:
Evaluación diferida en C ++
y notó que es algo viejo y la mayoría de las respuestas se refieren a C ++ pre-2011. En estos días tenemos lambdas sintácticos, que incluso pueden deducir el tipo de retorno, por lo que la evaluación perezosa parece reducirse a simplemente pasarlos: en lugar de
auto x = foo();
usted ejecuta
auto unevaluted_x = []() { return foo(); };
y luego evalúe cuándo / dónde necesita hacerlo:
auto x = unevaluted_x();
Parece que no hay nada más. Sin embargo, una de las respuestas sugiere utilizar futuros con lanzamiento asincrónico. ¿Puede alguien explicar por qué / si los futuros son importantes para el trabajo de evaluación perezosa, en C ++ o de forma más abstracta? Parece que los futuros pueden muy bien evaluarse con entusiasmo, pero simplemente, por ejemplo, en otro hilo, y tal vez con menos prioridad que cualquier cosa que los haya creado; y de todos modos, debería depender de la implementación, ¿verdad?
Además, ¿hay otros constructos modernos de C ++ que sean útiles para tener en cuenta en el contexto de la evaluación perezosa?
Hay algunas cosas pasando aquí.
Applicative order
evaluación de Applicative order
significa evaluar argumentos antes de pasarlos a una función. Normal order
evaluación de Normal order
significa pasar los argumentos a una función antes de evaluarlos.
La evaluación de orden normal tiene el beneficio de que algunos argumentos nunca se evalúan y el inconveniente de que algunos argumentos se evalúan una y otra vez.
Lazy
evaluación normal order + memoization
generalmente significa normal order + memoization
. Posponga la evaluación con la esperanza de que no tenga que evaluar en absoluto, pero si necesita hacerlo, recuerde el resultado, de modo que solo tiene que hacerlo una vez. La parte importante es evaluar un término nunca o una vez, la memorización es el mecanismo más fácil para proporcionar esto.
La promise/future
modelo promise/future
es diferente nuevamente. La idea aquí es comenzar una evaluación, probablemente en otro hilo, tan pronto como tenga suficiente información disponible. A continuación, deja mirar el resultado el mayor tiempo posible para mejorar las posibilidades de que ya esté disponible.
El modelo de promise/future
tiene una sinergia interesante con la evaluación perezosa. La estrategia va:
- Posponer la evaluación hasta que definitivamente se necesite el resultado
- Comience la evaluación en otro hilo
- Hacer algunas otras cosas
- El hilo de fondo se completa y almacena el resultado en algún lugar
- El hilo inicial recupera el resultado
La memorización se puede introducir ordenadamente cuando el resultado es producido por el hilo de fondo.
A pesar de la sinergia entre los dos, no son el mismo concepto.