melhorias de desempenho C ++ 0x
-
13-09-2019 - |
Pergunta
Um dos C ++ 0x melhorias que permitirão a escrever mais eficiente código C ++ é o ponteiro inteligente unique_ptr (muito ruim, que não vai permitir que se deslocam através memmove () como operações: a proposta não fez para o draft).
Quais são outras melhorias de desempenho no próximo padrão Tome seguinte código por exemplo:?
vector<char *> v(10,"astring");
string concat = accumulate(v.begin(),v.end(), string(""));
O código irá concatenar todas as cordas contido no vector v . O problema com esta peça elegante de código é que se acumulam () copia as coisas ao redor, e não usa referências. E a string () realoca cada vez mais operador é chamado. O código tem, portanto, um fraco desempenho em comparação com o código C analógico bem optimizada.
O C ++ 0x fornecer ferramentas para resolver o problema e talvez outros?
Solução
resolve Sim C ++ o problema através de algo chamado Mover semântica .
Basicamente, ele permite a um objeto para assumir a representação interna de outro objeto se o objeto é um temporário. Em vez de copiar cada byte na seqüência através de uma cópia-construtor, por exemplo, muitas vezes você pode apenas permitir que a string de destino para assumir a representação interna da cadeia fonte. Isso é permitido apenas quando a fonte é um valor de r.
Isto é feito através da introdução de um movimento construtor . É um construtor onde você sabe que o objeto src é uma temporária e está indo embora. Por isso, é aceitável para o destino para assumir a representação interna do objeto src.
O mesmo é verdadeiro para operadores movimento de atribuição .
Para distinguir um construtor de cópia de um construtor movimento, a linguagem introduziu referências rvalue . A classe define seu construtor movimento para tomar um referência rvalue , que só será obrigado a rvalues ??(temporários). Assim, a minha classe definiria algo ao longo das linhas 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;
}
}
Aqui é um artigo muito bom e detalhadas sobre semântica mover ea sintaxe que permite que você faça isso.
Outras dicas
Um desempenho-boost será generalizada expressões constantes, que é introduzido pela palavra-chave constexpr.
constexpr int returnSomething() {return 40;}
int avalue[returnSomething() + 2];
Este não é legal C ++ código, porque returnSomething () + 2 não é uma expressão constante.
Mas usando a constexpr palavra-chave, C ++ 0x pode dizer ao compilador que a expressão é uma constante de tempo de compilação.
Desculpe - você não pode Estado como um fato que string concat = accumulate(v.begin(),v.end(), string(""));
deve realocar. A vontade implementação direta, é claro. Mas compiladores são muito autorizados a fazer a coisa certa aqui.
Este já é o caso em C ++ 98 e C ++ 0x continua a permitir que ambas as implementações inteligentes e mudos. Dito isto, movimento semântica fará implementações inteligentes mais simples.
vector<string> v(10, "foo");
string concat = accumulate(v.begin(), v.end(), string(""));
Este exemplo é simplesmente má programação, em qualquer C ++ padrão. É equivalente a esta:
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 mover semântica só vai cuidar do "copiar parte de trás resultado em tmp" parte da equação. As cópias iniciais de tmp ainda será cópias. É um Schlemiel do pintor algoritmo , mas ainda pior do que o exemplo de costume usando strcat
em C.
Se accumulate
apenas +=
vez de +
e =
usado, então ele teria evitado todas essas cópias.
Mas C ++ 11 nos dá uma maneira de fazer melhor, mantendo-se sucinta, usando uma função lambda:
string concat;
for_each(v.begin(), v.end(), [&](const string &s){ concat += s; });
EDIT: Acho que um implementador biblioteca padrão pode optar por implementar accumulate
com o movimento no operando a +
, então tmp = tmp + "foo"
se tornaria tmp = move(tmp) + "foo"
, e que seria muito bonito resolver este problema. Eu não tenho certeza se tal implementação seria rigorosamente em conformidade. Nem GCC, MSVC, nem LLVM atualmente faz isso. E como accumulate
é definida em <numeric>
pode-se supor que só é projetado para uso com tipos numéricos.