2012-04-12 17 views
5

El objetivo es lograr el mismo efecto que en this C++ example: evitar la creación de temporales. Intenté traducir el ejemplo de C++ a D sin éxito. También probé diferentes enfoques.Plantillas de expresión en D

import std.datetime : benchmark; 
import std.stdio : writefln, writeln; 

void bench(alias fun, string time = "msecs")(string msg, uint n = 1_000_000) { 
    auto b = benchmark!fun(n); 
    writefln(" %s: %s ms", msg, b[0].to!(time, int)); 
} 

alias double Real; 

struct Expression(string op, E1, E2) { 
    E1 _v1; 
    E2 _v2; 
    alias _v1 v1; 
    alias _v2 v2; 

    auto opIndex(size_t i) { 
    return mixin("v1[i]" ~ op ~ "v2[i]"); 
    } 

    auto opBinary(string op, E)(auto ref E e) { 
    return Expression!(op, typeof(this), E)(this, e); 
    } 
} 

struct ExpVector { 

    Real[40] _data = void; 
    alias _data this; 

    this(Real datum) pure nothrow { _data = datum; } 

    auto opBinary(string op, T)(auto ref T other) { 
    return Expression!(op, typeof(this), T)(this, other); 
    } 

    void opAssign(E)(auto ref E exp) { 
    foreach(i, ref datum; _data) 
     datum = exp[i]; 
    } 
} 

struct Vector { 

    Real[40] _data = void; 
    alias _data this; 

    this(Real datum) pure nothrow { _data = datum; } 

    auto opBinary(string op)(auto ref Vector other) { 
    Vector ret; 
    foreach(i, datum; _data) 
     ret[i] = mixin("datum" ~ op ~ "other[i]"); 
    return ret; 
    } 
} 

void main() { 

    ExpVector e1 = ExpVector(1.5); 
    ExpVector e2 = ExpVector(7.3); 
    ExpVector ef; 
    void T1() { 
    ef = (e1 + e2) * (e1 + e2); 
    } 
    bench!T1(" vector operations using template expression"); 

    Vector v1 = Vector(1.5); 
    Vector v2 = Vector(7.3); 
    Vector vf; 
    void T2() { 
    vf = (v1 + v2) * (v1 + v2); 
    } 
    bench!T2(" regular vector operations"); 

    void T3() { 
    for(int i = 0; i < vf.length; ++i) 
     vf[i] = (v1[i] + v2[i]) * (v1[i] + v2[i]); 
    } 
    bench!T3(" what is expected if template expressions worked and temporaries were not created."); 
} 

La versión de la plantilla de expresión es más lenta que la versión de la plantilla de no expresión. Esperaba que la versión de la plantilla de expresión fuera mucho más rápida y cercana a lo esperado. Entonces, ¿qué pasa con mis plantillas de expresión? ¿Cuál es la forma correcta de hacer plantilla de expresión en D?

+1

Bueno, sin siquiera considerar lo que hace el código, quisiera señalar que Tendría que compilar el código en un compilador que comparte un backend con el compilador de C++ para poder realmente comparar el lenguaje en lugar de la implementación del compilador. Si usa gdc para las cosas D y gcc para las cosas C++, obtendrá una mejor comparación del lenguaje en sí. El código D podría ser más lento simplemente porque el motor de dmd no se optimiza tan bien como el de gcc. Podría ser más que eso, pero comparar dmd vs gcc podría ser simplemente comparar los optimizadores de los dos compiladores. –

+0

Creo que ha entendido mal lo que dije. El código que he publicado es más de dos veces más lento que la versión sin plantilla de expresión, es decir, solo una estructura 'Vector' regular que implementa la función de miembro' opBinary', con ambas versiones compiladas con DMD. No lo estoy comparando con el código C++ en Wikipedia. – Arlen

+0

Luego debe publicar ambas versiones. Es difícil comparar algo con otra cosa cuando solo tienes uno de los dos. –

Respuesta

2

en C++ ejemplo clases de expresión de Wikipedia mantiene referencias a los temporales, y en el caso de la expresión ...

 
Vec a, b; 
double c; 
Vec result = (a - b) * c 
... justo antes de la asignación al vector resultante tenemos una especie de árbol en la memoria:
a   b 
\  /
    \ /
    \ /
VecDifference 
     \ 
     \ 
     \ 
     VecScaled(has copy of 'c' embedded directly in object) 
VecDifference hold just references to a and b, and VecScaled holds reference to temporary VecDifference (which, according to the c++ rules, will be alive until expression end). At the end we have no duplication of initial data and no unnecessary computations(if we will use just some components of vector, but not all of them as in simple assignment to Vec)
In your struct Expression(string op, E1, E2) initial and intermediate data simply copied to data members and at the end of previous expression you will have
 
Expression!("*", Expression!("-", Vec, Vec), double) 
donde la expresión externa tendrá copia de expresión interna ("-", Vec, Vec) y los parámetros dobles y expresión interna tendrá copias de vectores ayb. Así que al final de un día evitaste Vec temporal pero hiciste 4 copias de ayb (sin contar la asignación final al vector de resultados). Sin embargo, no sé qué tan bien evitar copiar en esta situación en D. ¿Quizás apunta a las estructuras?
(Por cierto, las plantillas de inferencia y expresión de tipo automático de C++ 11 con referencias a temporales tienen una interacción desafortunada, que puede provocar errores: http://lanzkron.wordpress.com/2011/02/21/inferring-too-much/)

+0

Estaba al tanto de la copia, pero D no me deja tenerlos como referencia. Probé con el puntero (tenía la impresión de que mi código no sería seguro) y marca una gran diferencia, pero aún no está del todo allí. Por ejemplo, la versión de la plantilla de expresión toma '250ms' mientras que se espera' 150ms'. – Arlen

+0

Sí, las indicaciones no son seguras. La página enlazada al final de mi respuesta muestra uno de esos peligros (se trata de C++, pero creo que podemos obtener los mismos resultados usando plantillas de expresión en D con punteros) – cybevnm

Cuestiones relacionadas