tipos significado semántica semantica ramas rama gramatica general esquema derecho cual c++ templates c++11 move-semantics

c++ - significado - Comprender los beneficios de mover la semántica frente a la metaprogramación de plantillas.



semántica en gramatica (5)

He leído algunas descripciones sobre la semántica de movimientos en C ++ 11 y me pregunto en qué contexto podría usarse. Actualmente, muchas bibliotecas matemáticas de C ++ utilizan metaprogramación de plantillas para retrasar la evaluación.

Si M = A + B + C * D, donde M, A, B, C y D son matrices, la metaprogramación de la plantilla permite evitar copias inútiles. ¿Es la semántica de movimientos una manera más conveniente de hacer este tipo de cosas?

Si no, en qué contexto se usa la semántica. En caso afirmativo, ¿cuáles son las diferencias / limitaciones en comparación con la metaprogramación de plantillas para ese tipo de uso?


Creo que un término más preciso para lo que se llama "metaprogramación de plantillas" es " plantillas de expresión" .

Si sus matrices asignan sus datos dinámicamente, mover la semántica puede ayudar a transferir esos datos de un objeto a otro (incluso a / desde los temporales) generados durante una expresión como:

M = A + B + C*D

Las plantillas de expresión, por otro lado, eliminarán los temporales por completo.

Si sus matrices no asignan sus datos dinámicamente (por ejemplo, si son de tamaño fijo y pequeño), mover la semántica no ayudará en absoluto a su rendimiento.

La aplicación de plantillas de expresión a una biblioteca matricial dará como resultado el rendimiento más alto. También es una técnica de implementación muy difícil. Mover la semántica es mucho más fácil de implementar, y se puede hacer además de las plantillas de expresión (si hay recursos como la memoria que se pueden transferir).

En resumen:

Mover la semántica no elimina los temporales, sino que transferirá la memoria asignada dinámicamente entre los temporales en lugar de reasignarla.

Las plantillas de expresión eliminan los temporales.


La semántica de Move se aplica a los recursos administrados dentro de los objetos y se utiliza para evitar la adquisición / liberación innecesaria de recursos cuando se crean objetos temporales (es decir, una memoria asignada dinámicamente es un recurso).

La metaprogramación de plantillas opera en estructuras, que se asignan en la pila (ya que requiere la evaluación del tiempo de compilación de los operandos). Podría usarlo para evitar el cálculo en tiempo de ejecución para operaciones que se pueden calcular en tiempo de compilación


Las semánticas de movimiento son dinámicas, las plantillas de expresión no lo son. No puede crear una plantilla de expresión una expresión que se extiende sobre varias afirmaciones y parte de ella solo se evalúa si la luna es azul, mientras que la semántica de movimiento puede.


No soy un experto en estas optimizaciones, pero como lo entiendo, las técnicas de evaluación demoradas de las que está hablando funcionan mediante la definición de operadores aritméticos en su tipo de matriz tal que, por ejemplo, A+B+C*D no devuelve una matriz , devuelve un objeto proxy que puede convertir a una matriz. Esto sucede cuando se asigna a M , y el código de conversión computará cada celda de la matriz de resultados por los medios más eficientes que puedan crear los diseñadores de la biblioteca, evitando los objetos temporales de la matriz.

Entonces, supongamos que el programa contiene M = A + B + C * D;

Si no hizo nada inteligente, aparte de implementar operator+ de la forma habitual con operator+= , obtendría algo como esto una vez que era normal, elision de copia al estilo C ++ 03 ha iniciado:

Matrix tmp1 = C; tmp1 *= D; Matrix tmp2 = A; tmp2 += B; tmp2 += tmp1; M = tmp2;

Con la evaluación retrasada, podría obtener algo más como:

for (int i = 0; i < M.rows; ++i) { for (int j = 0; j < M.cols; ++j) { /* not necessarily the best matrix multiplication, but serves to illustrate */ c_times_d = 0; for (int k = 0; k < C.cols; ++k) { c_times_d += C[i][k] * D[k][j]; } M[i][j] = A[i][j] + B[i][j] + c_times_d; } }

mientras que el código "nada inteligente" haría un par de bucles de suma separados y mucha más asignación.

Por lo que yo sé, mover semántica no ayuda mucho en este caso. Nada en lo que has escrito nos permite pasar de A , B , C o D , por lo que vamos a terminar con el equivalente de:

Matrix tmp1 = C; tmp1 *= D; Matrix tmp2 = A; tmp2 += B; tmp2 += std::move(tmp1); M = std::move(tmp2);

Así que mover la semántica no ha ayudado con nada más que con el último bit, donde quizás las versiones de valor de los operadores son mejores que las normales. Hay más disponible si escribió std::move(A) + std::move(B) + std::move(C) * std::move(D) , porque no tendríamos que copiar desde C o A , pero todavía no creo que el resultado sea tan bueno como el código de evaluación diferida.

Básicamente, la semántica de movimiento no ayuda con algunas partes importantes de la optimización proporcionada por la evaluación retrasada:

1) con la evaluación diferida, los resultados intermedios nunca necesitan existir realmente como matrices completas. Mover la semántica no hace que el compilador no cree la matriz completa A+B en la memoria en algún momento.

2) con la evaluación diferida, podemos comenzar a modificar M antes de que hayamos terminado de calcular la expresión completa. Mover la semántica no ayuda al compilador a reordenar las modificaciones: incluso si el compilador es lo suficientemente inteligente como para detectar la oportunidad potencial, las modificaciones no temporales deben mantenerse en su orden correcto si existe el peligro de que se produzca una excepción, porque, en su caso, parte de los lanzamientos A + B + C * D , luego M debe dejarse como comenzó.


Son dos bestias diferentes. Mover la semántica consiste en apropiarse de los recursos de un valor que se va a destruir. Cuando se combinan con plantillas de expresión de, por ejemplo, grandes intenciones (que necesitan asignación de memoria dinámica), uno simplemente se apropiaría de dicha memoria en lugar de hacer una copia de algo que está a punto de ser destruido.

Mover la semántica también es importante para los objetos que no son copiables (como fstreams) pero tiene sentido para hacerlos móviles .