c++ - usar - no se guardaron los cambios vuelve a intentarlo instagram
¿Por qué no se agregó el rendimiento a C++ 0x? (8)
He estado utilizando el rendimiento en muchos de mis programas de Python, y realmente aclara el código en muchos casos. Publiqué sobre esto y es una de las páginas populares de mi sitio.
C # también ofrece rendimiento: se implementa a través del mantenimiento del estado en el lado del que llama, se realiza a través de una clase generada automáticamente que mantiene el estado, las variables locales de la función, etc.
Actualmente estoy leyendo sobre C ++ 0x y sus adiciones; y al leer acerca de la implementación de lambdas en C ++ 0x, descubro que también se realizó a través de clases generadas automáticamente, equipadas con operador () que almacena el código lambda. La pregunta natural se formó en mi mente: lo hicieron para las lambdas, ¿por qué no lo consideraron también para apoyar el "rendimiento"?
Seguramente pueden ver el valor de las co-rutinas ... así que solo puedo suponer que piensan que las implementaciones basadas en macros (como las de Simon Tatham ) son un sustituto adecuado. Sin embargo, no son, por muchas razones: estado guardado de la persona llamada, no reentrante, basado en macros (solo eso es razón suficiente), etc.
Editar: yield
no depende de recolección de basura, hilos o fibras. Puedes leer el artículo de Simon para ver que estoy hablando de que el compilador está haciendo una transformación simple, como:
int fibonacci() {
int a = 0, b = 1;
while (true) {
yield a;
int c = a + b;
a = b;
b = c;
}
}
Dentro:
struct GeneratedFibonacci {
int state;
int a, b;
GeneratedFibonacci() : state (0), a (0), b (1) {}
int operator()() {
switch (state) {
case 0:
state = 1;
while (true) {
return a;
case 1:
int c = a + b;
a = b;
b = c;
}
}
}
}
¿Recolección de basura? No. ¿Hilos? No. ¿Fibras? No. ¿Transformación simple? Podría decirse que sí.
Lo hicieron por lambdas, ¿por qué no lo consideraron también para apoyar el rendimiento?
Revisa los papers . ¿Alguien lo propuso?
... Solo puedo suponer que consideran que las implementaciones basadas en macro son un sustituto adecuado.
No necesariamente. Estoy seguro de que saben que existen tales soluciones macro, pero reemplazarlas no es suficiente motivación, por sí sola, para pasar nuevas características.
A pesar de que hay varios problemas en torno a una nueva palabra clave, podrían superarse con una nueva sintaxis, como se hizo para lambdas y el uso de auto como tipo de retorno de función.
Las características radicalmente nuevas necesitan impulsores sólidos (es decir, personas) para analizarlas en profundidad e impulsarlas a través del comité, ya que siempre tendrán un montón de personas escépticas ante un cambio radical. De modo que, incluso sin lo que vería como una fuerte razón técnica en contra de una construcción de rendimiento, puede que aún no haya habido suficiente apoyo.
Pero fundamentalmente, la biblioteca estándar de C ++ ha adoptado un concepto diferente de iteradores de lo que se vería con el rendimiento. Compare con los iteradores de Python, que solo requieren dos operaciones:
- an_iter.next () devuelve el siguiente elemento o genera StopIteration (next () builtin incluido en 2.6 en lugar de usar un método)
- iter (an_iter) devuelve an_iter (para que pueda tratar iterables e iteradores de forma idéntica en funciones)
Los iteradores de C ++ se usan en pares (que deben ser del mismo tipo), se dividen en categorías, sería un cambio semántico para pasar a algo más susceptible a una construcción de rendimiento, y ese cambio no encajaría bien con los conceptos (que ha sido desde que se cayó, pero eso llegó relativamente tarde). Por ejemplo, vea la rationale para rechazar (de manera justificable, aunque decepcionante) mi comentario sobre el cambio de rango basado en bucles para una forma que haría mucho más fácil escribir esta forma diferente de iterador.
Para aclarar concretamente lo que quiero decir con respecto a las diferentes formas de iteradores: el ejemplo de código generado necesita otro tipo para ser el tipo de iterador más la maquinaria asociada para obtener y mantener esos iteradores. No es que no se pueda manejar, pero no es tan simple como se puede imaginar al principio. La complejidad real es la "transformación simple" que respeta las excepciones para las variables "locales" (incluso durante la construcción), el control de la vida útil de las variables "locales" en los ámbitos locales dentro del generador (la mayoría tendría que guardarse en todas las llamadas), etc.
Agregar una palabra clave siempre es complicado, porque invalida el código previamente válido. Intenta evitar eso en un lenguaje con una base de código tan grande como C ++.
La evolución de C ++ es un proceso público. Si cree que debería haber yield
, formule una solicitud apropiada al comité estándar de C ++.
Recibirá su respuesta directamente de las personas que tomaron la decisión.
Bueno, para un ejemplo tan trivial como ese, el único problema que veo es que std::type_info::hash_code()
no se especifica constexpr
. Creo que una implementación conforme podría hacerlo y apoyar esto. De todos modos, el verdadero problema es obtener identificadores únicos, por lo que podría haber otra solución. (Obviamente pedí prestado su construcción de "interruptor maestro", gracias).
#define YIELD(X) do { /
constexpr size_t local_state = typeid([](){}).hash_code(); /
return (X); state = local_state; case local_state: ; } /
while (0)
Uso:
struct GeneratedFibonacci {
size_t state;
int a, b;
GeneratedFibonacci() : state (0), a (0), b (1) {}
int operator()() {
switch (state) {
case 0:
while (true) {
YIELD( a );
int c = a + b;
a = b;
b = c;
}
}
}
}
Mmm, también necesitarían garantizar que el hash no sea 0. No hay problema allí tampoco. Y una macro DONE
es fácil de implementar.
El verdadero problema es qué sucede cuando regresas de un alcance con objetos locales. No hay esperanza de guardar un marco de pila en un lenguaje basado en C. La solución es usar una coroutine real, y C ++ 0x lo aborda directamente con hilos y futuros.
Considere este generador / coroutine:
void ReadWords() {
ifstream f( "input.txt" );
while ( f ) {
string s;
f >> s;
yield s;
}
}
Si se utiliza un truco similar para el yield
, f
se destruye en el primer yield
, y es ilegal continuar el ciclo después de él, porque no se puede pasar o switch
una definición de objeto que no sea POD.
En general, puede hacer un seguimiento de lo que sucede en los papers del papers , aunque es mejor mantener un seguimiento en lugar de buscar un problema específico.
Una cosa para recordar acerca del comité de C ++ es que es un comité de voluntarios y no puede lograr todo lo que quiere. Por ejemplo, no había un mapa tipo hash en el estándar original, porque no pudieron llegar a tiempo. Podría ser que no hubiera nadie en el comité que se preocupara lo suficiente por el yield
y lo que hace para asegurarse de que el trabajo se realice.
La mejor manera de averiguarlo sería preguntarle a un miembro activo del comité.
Entonces parece que no llegó a C ++ 11 o C ++ 14, pero podría estar en camino a C ++ 17. Eche un vistazo a la conferencia C ++ Coroutines, una abstracción de sobrecarga negativa de CppCon2015 y el documento here .
Para resumir, están trabajando para extender las funciones de C ++ para tener rendimiento y esperar como características de las funciones. Parece que tienen una implementación inicial en Visual Studio 2015, no estoy seguro si Clang tiene una implementación todavía. También parece que puede haber algunos problemas con el uso del rendimiento y esperar como palabras clave.
La presentación es interesante porque habla sobre cuánto simplificó el código de red, en el que está esperando que ingresen los datos para continuar la secuencia de procesamiento. Sorprendentemente, parece que usar estos nuevos resultados de corutinas en un código más rápido / menos de lo que se haría hoy. Es una gran presentación.
La propuesta de funciones reanudables para C ++ se puede encontrar here .
No puedo decir por qué no agregaron algo como esto, pero en el caso de lambdas, tampoco se agregaron al lenguaje.
Comenzaron su vida como una implementación de biblioteca en Boost, lo que demostró que
- Las lambdas son ampliamente útiles: mucha gente las usará cuando estén disponibles, y eso
- una implementación de biblioteca en C ++ 03 adolece de varias deficiencias.
Sobre esta base, el comité decidió adoptar algún tipo de lambdas en C ++ 0x, y creo que inicialmente experimentaron con la adición de características de lenguaje más generales para permitir una mejor implementación de la biblioteca que Boost.
Y, finalmente, lo convirtieron en una característica del lenguaje central, porque no tenían otra opción: porque no era posible hacer una implementación de la biblioteca lo suficientemente buena .
Las nuevas características del lenguaje central no se agregan simplemente al idioma porque parecen una buena idea. El comité es muy reacio a agregarlos, y la característica en cuestión realmente necesita probarse a sí misma. Debe mostrarse que la característica es:
- posible implementar en el compilador
- va a resolver una necesidad real, y
- que una implementación de biblioteca no sería suficiente
En el caso de una palabra clave de yield
, sabemos que el primer punto se puede resolver. Como ha demostrado, es una transformación bastante simple que se puede hacer mecánicamente.
El segundo punto es complicado. ¿Cuánta necesidad de esto hay? ¿Cuán ampliamente utilizados están las implementaciones de la biblioteca que existen? ¿Cuántas personas han pedido esto o presentado propuestas para ello?
El último punto parece pasar también. Al menos en C ++ 03, la implementación de una biblioteca adolece de algunos defectos, como usted señaló, lo que podría justificar la implementación de un lenguaje central. ¿Se podría hacer una mejor implementación de la biblioteca en C ++ 0x?
Entonces sospecho que el problema principal es realmente una falta de interés. C ++ ya es un lenguaje enorme, y nadie quiere que crezca a menos que las características que se agreguen realmente valgan la pena. Sospecho que esto no es lo suficientemente útil.
de acuerdo con @Potatoswatter primero.
Soportar coroutine no es lo mismo que soportar las lambdas y no la transformación simple como la que se juega con el dispositivo de Duff.
Necesita coroutines asimétricas completas (apiladas) para funcionar como generadores en Python. La implementación de Simon Tatham y Chris'' son aptas, mientras que Boost.Coroutine es muy compacta, aunque es pesada.
Desafortunadamente, C ++ 11 todavía no tiene yield
para corutinas, tal vez C ++ 1y;)
PD: Si realmente te gustan los generadores de estilo Python, echa un vistazo a this .
ha habido varias implementaciones de corutinas como bibliotecas de espacio de usuario. Sin embargo, y este es el trato, esas implementaciones se basan en detalles no estándar. Por ejemplo, en ninguna parte del estándar c ++ se especifica cómo se guardan los cuadros de pila. La mayoría de las implementaciones simplemente copian la pila porque así es como funcionan la mayoría de las implementaciones de C ++
con respecto a los estándares, c ++ podría haber ayudado al soporte de coroutine al mejorar la especificación de los marcos de pila.
En realidad, ''agregarlo'' al lenguaje no me parece una buena idea, ya que eso te pegaría con una implementación ''lo suficientemente buena'' para la mayoría de los casos que depende completamente del compilador. Para los casos en que el uso de una corutina importa, esto no es aceptable de todos modos