2009-06-10 19 views
9

Una de las mejoras de C++ 0x que permitirá escribir código C++ más eficiente es el puntero inteligente unique_ptr (lástima que no permita moverse a través de memmove() como operaciones: la propuesta no entró en el borrador).Mejoras en el rendimiento de C++ 0x

¿Cuáles son otras mejoras de rendimiento en el próximo estándar? Tome siguiente código de ejemplo:

vector<char *> v(10,"astring"); 
string concat = accumulate(v.begin(),v.end(), string("")); 

El código concatenar todas las cadenas contenidas en el vector v . El problema con esta ordenada pieza de código es que accumulate() copia las cosas y no utiliza referencias. Y la cadena() se reasigna cada vez que se llama al operador. Por lo tanto, el código tiene un rendimiento deficiente en comparación con el código C analógico bien optimizado.

¿C++ 0x proporciona herramientas para resolver el problema y tal vez otras?

+1

Aquí hay alguna otra respuesta sobre el tema: http://stackoverflow.com/questions/637695/how-efficient-is-stdstring-compared-to-null-terminated-strings/637737#637737 –

Respuesta

13

Sí C++ resuelve el problema a través de algo llamado semántica de movimiento.

Básicamente permite que un objeto tome la representación interna de otro objeto si ese objeto es temporal. En lugar de copiar cada byte en la cadena mediante un constructor de copias, por ejemplo, a menudo puede permitir que la cadena de destino adopte la representación interna de la cadena fuente. Esto solo se permite cuando la fuente es un valor r.

Esto se hace mediante la introducción de un movimiento constructor. Es un constructor donde sabes que el objeto src es temporal y se va. Por lo tanto, es aceptable que el destino tome la representación interna del objeto src.

Lo mismo ocurre con los operadores de asignación de movimiento .

Para distinguir un constructor de copia de un constructor de movimiento, el lenguaje ha introducido referencias rvalue. Una clase define su constructor de movimiento para tomar una referencia rvalue que solo se vinculará a los valores r (temporales). Así que mi clase se definen algo en la línea de:

class CMyString 
{ 
private: 
    char* rawStr; 
public: 

    // move constructor bound to rvalues 
    CMyString(CMyString&& srcStr) 
    { 
     rawStr = srcStr.rawStr 
     srcStr.rawStr = NULL;    
    } 

    // move assignment operator 
    CMyString& operator=(CMyString&& srcStr) 
    { 
     if(rawStr != srcStr.rawStr) // protect against self assignment 
     { 
      delete[] rawStr; 
      rawStr = srcStr.rawStr 
      srcStr.rawStr = NULL; 
     } 
     return *this; 
    } 

    ~CMyString() 
    { 
     delete [] rawStr; 
    } 
} 

Here es un artículo muy buena y detallada sobre la semántica y la sintaxis de movimiento que le permite hacer esto.

+3

Esto hace que las operaciones de permutación como ordenar y rotar colecciones de colecciones mucho más rápido. Sospecho que el ejemplo más probable sería ordenar una colección de cadenas. – Brian

+1

Esta es también una muy buena explicación de las referencias de los valores: https://www.boostpro.com/trac/wiki/BoostCon09/RValue101 –

+0

Ampliar (o convertir) todos los contenedores y algoritmos para mover la semántica parece una gran tarea, me pregunto cómo están funcionando y si la compatibilidad con STL real se preservará sin la eficiencia comercial. –

7

Un aumento de rendimiento será expresiones constantes generalizadas, que se introduce con la palabra clave constexpr.

constexpr int returnSomething() {return 40;} 

int avalue[returnSomething() + 2]; 

Esto no es código de C++ legal, porque returnSomething() + 2 no es una expresión constante.

Pero al usar la palabra clave constexpr, C++ 0x puede decirle al compilador que la expresión es una constante en tiempo de compilación.

+1

Más palabras clave ???! Ya tenemos volatil, unsigned, restrict, static, const, Register ... ¿Me falta algo? –

+1

@TrevorBoydSmith: C++ tiene como 60 palabras clave, le faltan un montón de ellas –

1

Lo sentimos, no se puede indicar como un hecho que string concat = accumulate(v.begin(),v.end(), string(""));debe reasignar. Una implementación directa, por supuesto. Pero a los compiladores se les permite hacer lo correcto aquí.

Este ya es el caso en C++ 98, y C++ 0x continúa permitiendo implementaciones tanto inteligentes como tontas. Dicho esto, la semántica de movimiento simplificará las implementaciones inteligentes.

+0

Aquí está acumular() el cuerpo de gcc 4.1.1: para (; __first! = __último; ++ __primero) __init = __init + * __ first; return __init; Tiene razón diciendo que las implementaciones se permiten optimizar, proporcionando especializaciones. Para la cadena es parcialmente posible, la especialización debería: 1) tomar cadena como referencia o puntero, eso es difícil, porque la firma de la función está especificada por estándar 2) use append() o + = - eso es posible. Pero después de todo, creo que las nuevas herramientas adecuadas en el lenguaje permitirán resolver el problema sin especializaciones tan específicas. –

0
vector<string> v(10, "foo"); 
string concat = accumulate(v.begin(), v.end(), string("")); 

Este ejemplo es simplemente mala programación, en cualquier estándar de C++. Es equivalente a esto:

string tmp; 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 
tmp = tmp + "foo"; //copy tmp, append "foo", then copy the result back into tmp 

C++ 11 semántica moverse sólo se hará cargo de la "copia el resultado en tmp" parte de la ecuación. Las copias iniciales de tmp seguirán siendo copias. Es un clásico Schlemiel the Painter's algorithm, pero incluso peor que el ejemplo habitual utilizando strcat en C.

Si accumulate acaba de utilizar en lugar de +=+ y = entonces se habría evitado todas esas copias.

Pero C++ 11 nos da una manera de hacerlo mejor, sin dejar de ser conciso, utilizando una función lambda:

string concat; 
for_each(v.begin(), v.end(), [&](const string &s){ concat += s; }); 

EDIT: supongamos que un implementador biblioteca estándar podría optar por aplicar accumulate con movimiento en el operando a +, entonces tmp = tmp + "foo" se convertiría en tmp = move(tmp) + "foo", y eso resolvería bastante este problema. No estoy seguro si tal implementación sería estrictamente conforme. Ni GCC, MSVC ni LLVM lo hacen actualmente. Y como se define accumulate en <numeric>, se puede suponer que solo está diseñado para su uso con tipos numéricos.