Em uma pergunta C ++ sobre otimização e estilo de código , várias respostas se referiram a "SSO" no contexto de otimização de cópias de std::string
. O que significa SSO nesse contexto?
Claramente, não "logon único". "Otimização de string compartilhada", talvez?
c++
string
optimization
Raedwald
fonte
fonte
std::string
implementada", e uma outra pergunta: "o que faz SSO média", você tem que ser absolutamente insano considerar que eles sejam a mesma perguntaRespostas:
Histórico / Visão Geral
Operações em variáveis automáticas ("da pilha", que são criadas sem chamar
malloc
/new
) geralmente são muito mais rápidas do que aquelas que envolvem o armazenamento gratuito ("a pilha", que são criadas usandonew
). No entanto, o tamanho das matrizes automáticas é fixo no momento da compilação, mas o tamanho das matrizes do armazenamento gratuito não é. Além disso, o tamanho da pilha é limitado (normalmente alguns MiB), enquanto o armazenamento gratuito é limitado apenas pela memória do seu sistema.SSO é a otimização de cadeia curta / pequena. A
std::string
normalmente armazena a string como um ponteiro para a loja gratuita ("a pilha"), o que fornece características de desempenho semelhantes às de uma chamadanew char [size]
. Isso evita um estouro de pilha para cadeias muito grandes, mas pode ser mais lento, especialmente com operações de cópia. Como otimização, muitas implementaçõesstd::string
criam uma pequena matriz automática, algo assimchar [20]
. Se você tiver uma string com 20 caracteres ou menos (dado este exemplo, o tamanho real varia), ela será armazenada diretamente nessa matriz. Isso evita a necessidade de ligarnew
, o que acelera um pouco as coisas.EDITAR:
Eu não esperava que essa resposta fosse tão popular, mas, como é, permita-me dar uma implementação mais realista, com a ressalva de que nunca realmente li nenhuma implementação do SSO "in the wild".
Detalhes da implementação
No mínimo, é
std::string
necessário armazenar as seguintes informações:O tamanho pode ser armazenado como um
std::string::size_type
ou como um ponteiro para o final. A única diferença é se você deseja subtrair dois ponteiros quando o usuário chamasize
ou adicionar umsize_type
a um ponteiro quando o usuário chamaend
. A capacidade também pode ser armazenada de qualquer maneira.Você não paga pelo que não usa.
Primeiro, considere a implementação ingênua com base no que descrevi acima:
Para um sistema de 64 bits, isso geralmente significa que
std::string
possui 24 bytes de 'sobrecarga' por sequência, além de outros 16 para o buffer SSO (16 escolhidos aqui em vez de 20 devido a requisitos de preenchimento). Realmente não faria sentido armazenar esses três membros de dados mais uma matriz local de caracteres, como no meu exemplo simplificado. Sem_size <= 16
, então, colocarei todos os dadosm_sso
, para que eu já conheça a capacidade e não precise do ponteiro para os dados. Sem_size > 16
, então eu não precisom_sso
. Não há absolutamente nenhuma sobreposição onde eu preciso de todos eles. Uma solução mais inteligente que não desperdice espaço seria algo mais ou menos assim (apenas para fins de exemplo não testados):Eu diria que a maioria das implementações se parece mais com isso.
fonte
std::string const &
, chegar aos dados é um único indireção de memória, porque os dados são armazenados no local da referência. Se não houvesse otimização de sequência pequena, o acesso aos dados exigiria duas indiretas de memória (primeiro para carregar a referência à sequência e ler seu conteúdo, depois a segunda para ler o conteúdo do ponteiro de dados na sequência).SSO é a abreviação de "Small String Optimization", uma técnica em que pequenas strings são incorporadas no corpo da classe string em vez de usar um buffer alocado separadamente.
fonte
Como já explicado pelas outras respostas, SSO significa Otimização de cadeia pequena / curta . A motivação por trás dessa otimização é a evidência inegável de que os aplicativos em geral lidam com cadeias muito mais curtas do que com cadeias mais longas.
Conforme explicado por David Stone em sua resposta acima , a
std::string
classe usa um buffer interno para armazenar conteúdo até um determinado comprimento, e isso elimina a necessidade de alocar memória dinamicamente. Isso torna o código mais eficiente e rápido .Essa outra resposta relacionada mostra claramente que o tamanho do buffer interno depende da
std::string
implementação, que varia de plataforma para plataforma (consulte os resultados de benchmark abaixo).Benchmarks
Aqui está um pequeno programa que avalia a operação de cópia de várias seqüências de caracteres com o mesmo comprimento. Começa a imprimir o tempo para copiar 10 milhões de strings com comprimento = 1. Em seguida, repete-se com strings de comprimento = 2. Continua até o comprimento ser 50.
Se você deseja executar este programa, deve fazê-lo da
./a.out > /dev/null
maneira que o tempo para imprimir as seqüências não seja contado. Os números importantes são impressos parastderr
que eles apareçam no console.Criei gráficos com a saída dos meus computadores MacBook e Ubuntu. Observe que há um grande salto no tempo para copiar as strings quando o comprimento atinge um determinado ponto. Esse é o momento em que as strings não cabem mais no buffer interno e a alocação de memória deve ser usada.
Observe também que na máquina linux, o salto ocorre quando o comprimento da cadeia atinge 16. No macbook, o salto ocorre quando o comprimento atinge 23. Isso confirma que o SSO depende da implementação da plataforma.
Ubuntu
MacBook Pro
fonte