- Por que usar a alocação de arena?
- Vamos começar
- API da classe Arena
- Classe de mensagens geradas
- Padrões de uso e práticas recomendadas
- Exemplo
A alocação de arena é um recurso somente C++ que ajuda a otimizar o uso de memória e melhorar o desempenho ao trabalhar com buffers de protocolo. Esta página descreve exatamente o código C++ que o compilador de buffer de protocolo gera, além do código descrito no Guia de código gerado em C++ quando a alocação de arena está ativada. Você precisa conhecer o material no guia de linguagem e no Guia de código gerado em C++.
Por que usar a alocação de arena?
A alocação e a desalocação de memória constituem uma fração significativa do tempo de CPU gasto no código de buffers de protocolo. Por padrão, os buffers de protocolo executam alocações de heap para cada objeto de mensagem, cada um dos subobjetos e vários tipos de campo, como strings. Essas alocações ocorrem em massa ao analisar uma mensagem e criar novas mensagens na memória, e as desalocações associadas acontecem quando as mensagens e as árvores de subobjetos delas são liberadas.
A alocação baseada em arena foi projetada para reduzir esse custo de desempenho. Com a alocação de arena, novos objetos são alocados fora de uma grande parte de memória pré-alocada chamada arena. Os objetos podem ser liberados de uma só vez descartando-se toda a arena, de preferência sem executar destrutores de qualquer contêiner. No entanto, uma arena ainda pode manter uma "lista de destrutores" quando necessário. Isso torna a alocação de objetos mais rápida reduzindo-a a um simples incremento de ponteiro, e torna a desalocação quase sem custos. A alocação de arena também aumenta a eficiência do cache: quando as mensagens são analisadas, elas têm mais probabilidade de serem alocadas na memória contínua, o que aumenta a probabilidade de as mensagens recebidas passarem por linhas de cache quentes.
Para aproveitar esses benefícios, você precisa conhecer o ciclo de vida do objeto e encontrar uma granularidade adequada para usar arenas (para servidores, isso geralmente é por solicitação). Saiba mais sobre como aproveitar ao máximo a alocação de arena em Padrões de uso e práticas recomendadas.
Esta tabela resume as vantagens e desvantagens comuns de desempenho no uso de arenas:
Operação | Mensagens proto alocadas | Mensagens proto alocadas na Arena |
---|---|---|
Alocação de mensagens | Mais lento, em média | Mais rápida em média |
Destruição de mensagens | Mais lento, em média | Mais rápida em média |
Transferências de mensagem | Sempre uma movimentação (equivalente a uma cópia superficial no custo) | Às vezes, uma cópia |
Primeiros passos
O compilador de buffer de protocolo gera código para alocação de arena para as mensagens em seu arquivo, conforme usado no exemplo a seguir.
#include <google/protobuf/arena.h> { google::protobuf::Arena arena; MyMessage* message = google::protobuf::Arena::CreateMessage<MyMessage>(&arena); // ... }
O objeto de mensagem criado por CreateMessage()
existe
enquanto existir arena
, e não é permitido delete
o ponteiro de mensagem retornado. Todo o armazenamento interno do objeto de mensagem (com algumas exceções1) e submensagens (por exemplo, submensagens em um campo repetido em MyMessage
) também são alocados na arena.
Na maioria das vezes, o restante do código será o mesmo que se você não estivesse usando a alocação de arena.
Analisaremos a API da arena em mais detalhes nas seções a seguir, e você pode ver um exemplo mais extenso no final do documento.
1. Atualmente, os campos de string armazenam os dados no heap mesmo quando a mensagem que o contém está na arena. Campos desconhecidos também são alocados de heap.↩
API da classe Arena
Você cria objetos de mensagem na arena usando a classe
google::protobuf::Arena
. Essa classe implementa os métodos públicos a seguir.
Construtores
Arena()
: cria uma nova arena com parâmetros padrão, ajustados para casos de uso médios.Arena(const ArenaOptions& options)
: cria uma nova arena que usa as opções de alocação especificadas. As opções disponíveis emArenaOptions
incluem a capacidade de usar um bloco inicial de memória fornecida pelo usuário para alocações antes de recorrer ao alocador do sistema, controlar os tamanhos de solicitação inicial e máximo para blocos de memória e permitir que você transmita ponteiros de alocação de bloco e de desalocação personalizados para criar listas sem custo financeiro e outras sobre os blocos.
Métodos de alocação
-
template<typename T> static T* CreateMessage(Arena* arena)
: cria um novo objeto de buffer de protocolo do tipo de mensagemT
na arena.Se
arena
não for NULL, o objeto de mensagem retornado será alocado na arena, seu armazenamento interno e as submensagens (se houver) serão alocados na mesma arena, e seu ciclo de vida é o mesmo da arena. O objeto não pode ser excluído/liberado manualmente: a arena é proprietária do objeto de mensagem para fins de vida útil.Se
arena
for NULL, o objeto de mensagem retornado será alocado no heap, e o autor da chamada será o proprietário do objeto após o retorno. -
template<typename T> static T* Create(Arena* arena, args...)
: semelhante aCreateMessage()
, mas permite criar um objeto de qualquer classe na arena, não apenas tipos de mensagens de buffer de protocolo. Por exemplo, digamos que você tenha esta classe C++:class MyCustomClass { MyCustomClass(int arg1, int arg2); // ... };
...você pode criar uma instância dele na arena, assim:
void func() { // ... google::protobuf::Arena arena; MyCustomClass* c = google::protobuf::Arena::Create<MyCustomClass>(&arena, constructor_arg1, constructor_arg2); // ... }
-
template<typename T> static T* CreateArray(Arena* arena, size_t n)
: searena
não for NULL, esse método alocará armazenamento bruto para elementosn
do tipoT
e o retornará. A arena é dona da memória retornada e a libera após a destruição. Searena
for NULL, esse método alocará o armazenamento no heap, e o autor da chamada receberá a propriedade.T
precisa ter um construtor trivial: eles não são chamados quando a matriz é criada na arena.
Métodos de "Lista própria"
Os métodos a seguir permitem especificar que objetos ou destrutores específicos são "detidos" pela arena, garantindo que eles sejam excluídos ou chamados quando a própria arena for excluída.
template<typename T> void Own(T* object)
: adicionaobject
à lista de objetos de heap do local. Quando a arena é destruída, ela passa por essa lista e libera cada objeto usando a exclusão do operador, ou seja, o alocador de memória do sistema. Esse método é útil nos casos em que o ciclo de vida de um objeto precisa ser vinculado à arena, mas, por qualquer motivo, o próprio objeto não pode ser ou não foi alocado na arena.template<typename T> void OwnDestructor(T* object)
: adiciona o destrutor deobject
à lista de destrutores da arena para chamada. Quando a arena é destruída, ela passa por essa lista e chama cada destrutor por vez. Ele não tenta liberar a memória subjacente do objeto. Esse método é útil quando um objeto é incorporado ao armazenamento alocado para uma arena, mas o destrutor dele não é chamado. Por exemplo, porque a classe que o contém é uma mensagem protobuf cujo destrutor não vai ser chamado ou porque ele foi construído manualmente em um bloco alocado porAllocateArray()
.
Outros métodos
uint64 SpaceUsed() const
: retorna o tamanho total da arena, que é a soma dos tamanhos dos blocos subjacentes. Esse método é seguro para linhas de execução. No entanto, se houver alocações simultâneas de várias linhas de execução, o valor de retorno desse método talvez não inclua os tamanhos desses novos blocos.uint64 Reset()
: destrói o armazenamento da arena, primeiro chamando todos os destrutores registrados e liberando todos os objetos de heap registrados e descartando todos os blocos de arena. Esse procedimento de desmontagem é equivalente ao que ocorre quando o destrutor da arena é executado, exceto quando a arena é reutilizável para novas alocações após o retorno desse método. Retorna o tamanho total usado pela arena: essa informação é útil para ajustar o desempenho.template<typename T> Arena* GetArena()
: retorna um ponteiro para esta arena. Não é muito útil, mas permite que aArena
seja usada em instanciações de modelo que esperam que os métodosGetArena()
estejam presentes.
Segurança de linha de execução
Os métodos de alocação de google::protobuf::Arena
são seguros para linhas de execução, e a implementação subjacente dura pouco tempo para agilizar a alocação de várias linhas de execução. O método Reset()
não é seguro para linhas de execução:
a linha de execução que executa a redefinição de arena precisa ser sincronizada com todas as linhas de execução
que executam alocações ou usar objetos alocados dessa arena primeiro.
Classe da mensagem gerada
Os seguintes membros da classe da mensagem são alterados ou adicionados quando você ativa a alocação da arena.
Métodos da classe da mensagem
Message(Message&& other)
: se a mensagem de origem não estiver na arena, o construtor de mover mover todos os campos de uma mensagem para outra sem fazer cópias ou alocações de heap (a complexidade de tempo dessa operação éO(number-of-declared-fields)
). No entanto, se a mensagem de origem estiver na arena, ela executará uma cópia profunda dos dados subjacentes. Em ambos os casos, a mensagem de origem é deixada em um estado válido, mas não especificado.Message& operator=(Message&& other)
: se as duas mensagens não estiverem na arena ou estiverem na mesma arena, o operador de atribuição de movimento mover todos os campos de uma mensagem para outra sem fazer cópias ou alocações de heap (a complexidade do tempo dessa operação éO(number-of-declared-fields)
). No entanto, se apenas uma mensagem estiver na arena ou se estiver em diferentes arenas, ela executará uma cópia profunda dos dados subjacentes. Em ambos os casos, a mensagem de origem é deixada em um estado válido, mas não especificado.void Swap(Message* other)
: se as duas mensagens a serem trocadas não estiverem em arenas ou estiverem na mesma arena,Swap()
se comportará da mesma forma que sem a alocação de arena: ela troca o conteúdo dos objetos de mensagem de maneira eficiente, geralmente por meio de trocas de ponteiros baratas e evitando cópias a qualquer custo. No entanto, se apenas uma mensagem estiver em uma arena ou se elas estiverem em arenas diferentes, oSwap()
vai executar cópias profundas dos dados. Esse novo comportamento é necessário porque, caso contrário, os subobjetos trocados podem ter ciclos de vida diferentes, levando a bugs de uso após a liberação.Message* New(Arena* arena)
: uma substituição alternativa do métodoNew()
padrão. Ela permite que um novo objeto de mensagem desse tipo seja criado na arena especificada. A semântica será idêntica àArena::CreateMessage<T>(arena)
se o tipo de mensagem concreta em que ele é chamado for gerado com a alocação de arena ativada. Se o tipo de mensagem não for gerado com a alocação de arena ativada, ela será equivalente a uma alocação comum seguida porarena->Own(message)
searena
não for NULL.Arena* GetArena()
: retorna a arena em que o objeto da mensagem foi alocado, se houver.void UnsafeArenaSwap(Message* other)
: idêntico aSwap()
, exceto que pressupõe que os dois objetos estejam na mesma arena (ou não estejam em arenas) e sempre usa a implementação eficiente de troca de ponteiro dessa operação. O uso desse método pode melhorar o desempenho, já que, ao contrário deSwap()
, ele não precisa verificar quais mensagens estão em qual arena antes da troca. Como o prefixoUnsafe
sugere, use esse método somente se você tiver certeza de que as mensagens que você quer trocar não estão em arenas diferentes. Caso contrário, esse método poderá ter resultados imprevisíveis.
Campos de mensagens incorporados
Quando você aloca um objeto de mensagem em uma arena, os objetos de campo de mensagens incorporados (submensagens) também são de propriedade automática. A alocação desses objetos de mensagem depende de onde eles são definidos:
- Se o tipo de mensagem também for definido em um arquivo
.proto
com a alocação de arena ativada, o objeto será alocado na arena diretamente. - Se o tipo de mensagem for de outra
.proto
sem a alocação de arena ativada, o objeto será alocado em heap, mas será "detido" pela arena de mensagem mãe. Isso significa que, quando a arena for destruída, o objeto será liberado com os objetos na arena.
Para qualquer uma dessas definições de campo:
optional Bar foo = 1; required Bar foo = 1;
Os métodos a seguir são adicionados ou têm algum comportamento especial quando a alocação de arena está ativada. Caso contrário, os métodos de acesso usam apenas o comportamento padrão.
Bar* mutable_foo()
: retorna um ponteiro mutável para a instância da submensagem. Se o objeto pai estiver em uma arena, o objeto retornado também estará.-
void set_allocated_foo(Bar* bar)
: aceita um novo objeto e o utiliza como o novo valor do campo. O suporte à arena adiciona outras semânticas de cópia para manter a propriedade adequada quando os objetos ultrapassam os limites da arena/arena ou da arena/pilha:- Se o objeto pai estiver no heap e
bar
estiver no heap ou se o pai e a mensagem estiverem na mesma arena, o comportamento desse método permanecerá inalterado. - Se o pai estiver em uma arena e
bar
estiver no heap, a mensagem pai adicionarábar
à lista de propriedades da arena comarena->Own()
. - Se o pai estiver em uma arena e
bar
estiver em uma arena diferente, esse método fará uma cópia da mensagem e a usará como o novo valor de campo.
- Se o objeto pai estiver no heap e
-
Bar* release_foo()
: retorna a instância de submensagem existente do campo, se definido, ou um ponteiro NULL se não estiver definido, liberando a propriedade dessa instância para o autor da chamada e limpando o campo da mensagem pai. O suporte à arena adiciona outras semânticas de cópia para manter o contrato de que o objeto retornado é sempre alocado na alocação:- Se a mensagem mãe estiver em uma arena, esse método fará uma cópia da submensagem no heap, apagará o valor do campo e retornará a cópia.
- Se a mensagem mãe estiver no heap, o comportamento do método não será alterado.
void unsafe_arena_set_allocated_foo(Bar* bar)
: idêntico aset_allocated_foo
, mas pressupõe que o pai e a submensagem estão na mesma arena. O uso dessa versão do método pode melhorar o desempenho, já que não é necessário verificar se as mensagens estão em uma arena específica ou no heap. Consulte os padrões de lançamento/alocado para ver detalhes sobre maneiras seguras de usar esse recurso.Bar* unsafe_arena_release_foo()
: semelhante arelease_foo()
, mas ignora todas as verificações de propriedade. Consulte os padrões de lançamento/alocado para ver detalhes sobre maneiras seguras de usar esse recurso.
Campos de string
Atualmente, os campos de string armazenam os dados no heap mesmo quando a mensagem mãe está na arena. Por isso, os métodos do acessador de strings usam o comportamento padrão mesmo quando a alocação de arena está ativada.
Campos repetidos
Os campos repetidos alocam armazenamento de matriz interno na arena quando a mensagem que contém a memória é alocada, e também alocam os elementos na arena quando esses elementos são objetos separados retidos por ponteiro (mensagens ou strings). No nível da classe da mensagem, os métodos gerados para campos repetidos não são alterados. No entanto, os objetos RepeatedField
e RepeatedPtrField
retornados pelos acessadores têm novos métodos e semânticas modificadas quando o suporte à arena está ativado.
Campos numéricos repetidos
Objetos RepeatedField
que contêm tipos primitivos têm os seguintes métodos novos/alterados
quando a alocação de arena é ativada:
void UnsafeArenaSwap(RepeatedField* other)
: executa uma troca de conteúdo deRepeatedField
sem validar se esse campo repetido e outros estão na mesma arena. Se não forem, os dois objetos de campo repetidos precisarão estar em arenas com ciclos de vida equivalentes. O caso em que uma delas está em uma arena e a outra em um heap está marcada e não permitida.void Swap(RepeatedField* other)
: verifica a arena de cada objeto de campo repetido. Se uma estiver em uma arena enquanto uma estiver no heap ou se ambas estiverem em arenas, mas em outras, as matrizes subjacentes serão copiadas antes da troca. Isso significa que, após a troca, cada objeto de campo repetido mantém uma matriz na própria arena ou heap, conforme apropriado.
Campos repetidos de mensagem incorporada
Objetos RepeatedPtrField
que contêm messages têm os seguintes métodos novos/alterados quando
a alocação de arena está ativada.
void UnsafeArenaSwap(RepeatedPtrField* other)
: realiza uma troca de conteúdo deRepeatedPtrField
sem validar que esse campo repetido e outros têm o mesmo ponteiro de arena. Caso contrário, os dois objetos de campo repetidos precisam ter ponteiros de arena com ciclos de vida equivalentes. O caso em que um tem um ponteiro de arena não NULL e um tem um ponteiro de arena NULL é verificado e não permitido.void Swap(RepeatedPtrField* other)
: verifica o ponteiro do arena de cada objeto de campo repetido. Se um deles for não NULL (conteúdo na arena), enquanto um for NULL (conteúdo no heap) ou se os dois forem não NULL, mas tiverem valores diferentes, as matrizes subjacentes e os objetos apontados serão copiados antes da troca. Isso significa que, após a troca, cada objeto de campo repetido mantém uma matriz na própria arena ou no heap, conforme apropriado.void AddAllocated(SubMessageType* value)
: verifica se o objeto de mensagem fornecido está na mesma arena que o ponteiro de campo repetido do campo. Nas situações a seguir, as ações são diferentes:- A origem e o destino são alocados em arena e na mesma arena: o ponteiro de objeto é adicionado diretamente à matriz subjacente.
- A origem e o destino são alocados em arena e em arenas diferentes: uma cópia é feita, o original é liberado se ele for alocado em heap e a cópia é colocada na matriz.
- A origem é alocada em heap e o destino é alocado em arena: nenhuma cópia é feita.
- A origem é alocada em arena, e o destino é alocado em heap: uma cópia é feita e colocada na matriz.
- Tanto a origem quanto o destino são alocados por heap: o ponteiro do objeto é adicionado diretamente à matriz subjacente.
SubMessageType* ReleaseLast()
: retorna uma mensagem alocada ao heap equivalente à última mensagem no campo repetido, removendo-a do campo repetido. Se o próprio campo repetido tiver um ponteiro de arena NULL (e, assim, todas as mensagens apontadas forem alocadas em heap), esse método simplesmente retornará um ponteiro para o objeto original. Caso contrário, se o campo repetido tiver um ponteiro de arena não NULL, esse método fará uma cópia que é alocada em heap e retorna essa cópia. Em ambos os casos, o autor da chamada recebe a propriedade de um objeto alocado de heap e é responsável pela exclusão do objeto.void UnsafeArenaAddAllocated(SubMessageType* value)
: semelhante aAddAllocated()
, mas não realiza verificações de heap/arena ou cópias de mensagens. Ele adiciona o ponteiro fornecido diretamente à matriz interna de ponteiros para esse campo repetido. Consulte os padrões de lançamento/alocado para ver detalhes sobre maneiras seguras de usar esse recurso.SubMessageType* UnsafeArenaReleaseLast()
: semelhante aReleaseLast()
, mas não realiza cópias, mesmo que o campo repetido tenha um ponteiro de arena não NULL. Em vez disso, ele retorna diretamente o ponteiro para o objeto como estava no campo repetido. Consulte os padrões de lançamento/alocado para ver detalhes sobre maneiras seguras de usar esse recurso.void ExtractSubrange(int start, int num, SubMessageType** elements)
: remove os elementosnum
do campo repetido, começando pelo índicestart
, e os retorna emelements
se não forem NULL. Se o campo repetido estiver em uma arena e os elementos estiverem sendo retornados, os elementos serão copiados para o heap primeiro. Em ambos os casos (arena ou não arena), o autor da chamada é proprietário dos objetos retornados no heap.void UnsafeArenaExtractSubrange(int start, int num, SubMessageType** elements)
: remove os elementosnum
do campo repetido a partir do índicestart
e os retorna emelements
caso não seja NULL. Ao contrário deExtractSubrange()
, esse método nunca copia os elementos extraídos. Consulte os padrões de lançamento/alocado para ver detalhes sobre maneiras seguras de usar esse recurso.
Campos de string repetidos
Campos repetidos de strings têm os mesmos novos métodos e semântica modificada que os campos repetidos de mensagens, porque também mantêm os objetos subjacentes (ou seja, strings) por referência do ponteiro.
Padrões de uso e práticas recomendadas
Ao usar mensagens alocadas por arena, vários padrões de uso podem resultar em cópias não intencionais ou outros efeitos negativos no desempenho. Esteja ciente dos seguintes padrões comuns que podem precisar ser alterados ao adaptar o código para arenas. Tomamos cuidado com o design da API para garantir que o comportamento correto ainda ocorra, mas soluções de maior desempenho podem exigir algumas reformulações.
Cópias não intencionais
Vários métodos que nunca criam cópias de objetos sem usar a alocação de arena podem acabar fazendo isso quando o suporte à arena está ativado. Essas cópias indesejadas podem ser evitadas se você certificar-se de que seus objetos estejam alocados adequadamente e/ou usem versões de métodos específicas de arena, conforme descrito em mais detalhes abaixo.
Definir alocado/Adicionar alocados/liberar
Por padrão, os métodos release_field()
e set_allocated_field()
(para campos de mensagens únicos) e os métodos ReleaseLast()
e AddAllocated()
(para campos de mensagem repetidos) permitem que o código do usuário anexe e remova submensagens diretamente, passando a propriedade de ponteiros sem copiar nenhum dado.
No entanto, quando a mensagem pai está em uma arena, esses métodos
às vezes precisam copiar o objeto transmitido ou retornado para manter
a compatibilidade com os contratos de propriedade existentes. Mais especificamente, os métodos
que assumem a propriedade (set_allocated_field()
e
AddAllocated()
) podem copiar dados se o pai estiver em uma arena e
o novo subobjeto não, ou vice-versa, ou estiver em arenas diferentes.
Os métodos que liberam a propriedade (release_field()
e
ReleaseLast()
) podem copiar dados se o pai estiver na arena,
porque o objeto retornado precisa estar no heap, por contrato.
Para evitar essas cópias, adicionamos versões correspondentes de "arena não segura"
desses métodos em que as cópias nunca são realizadas:
unsafe_arena_set_allocated_field()
,
unsafe_arena_release_field()
,
UnsafeArenaAddAllocated()
e UnsafeArenaRelease()
, para campos únicos e repetidos, respectivamente. Esses métodos só devem ser usados quando você sabe que eles são seguros. Há dois padrões comuns para esses métodos:
- Mover mensagens de árvores entre partes da mesma arena. Observe que as mensagens precisam estar na mesma arena para que esse caso seja seguro.
- Emprestar temporariamente uma mensagem de propriedade para uma árvore para evitar cópias. Parear um método add/set não seguro com um método não seguro de release faz o empréstimo da maneira mais barata possível, independentemente da propriedade de cada mensagem. Esse padrão funciona quando estão na mesma arena, em uma arena diferente ou em nenhuma arena. Observe que, entre o add/set não seguro e a liberação correspondente, o mutuário não pode ser trocado, movido, limpo nem destruído. A mensagem emprestado não pode ser trocada ou movida, a mensagem emprestado não pode ser apagada ou liberada pelo mutuário, e a mensagem emprestado não pode ser destruída.
Veja um exemplo de como evitar cópias desnecessárias com esses métodos. Suponha que você tenha criado as mensagens a seguir em uma arena.
Arena* arena = new google::protobuf::Arena(); MyFeatureMessage* arena_message_1 = google::protobuf::Arena::CreateMessage<MyFeatureMessage>(arena); arena_message_1->mutable_nested_message()->set_feature_id(11); MyFeatureMessage* arena_message_2 = google::protobuf::Arena::CreateMessage<MyFeatureMessage>(arena);
O código a seguir faz uso ineficiente da
API release_...()
:
arena_message_2->set_allocated_nested_message(arena_message_1->release_nested_message()); arena_message_1->release_message(); // returns a copy of the underlying nested_message and deletes underlying pointer
Usar a versão "arena não segura" evita a cópia:
arena_message_2->unsafe_arena_set_allocated_nested_message( arena_message_1->unsafe_arena_release_nested_message());
Saiba mais sobre esses métodos na seção Campos de mensagens incorporados acima.
Swap
Quando o conteúdo de duas mensagens for trocado por Swap()
, os
subobjetos subjacentes poderão ser copiados se as duas mensagens estiverem em arenas
diferentes ou se uma estiver na arena e a outra estiver no heap. Se você quiser
evitar essa cópia e (i) saber que as duas mensagens estão na mesma
arena ou arenas diferentes, mas as arenas têm ciclos de vida equivalentes, ou (ii)
saber que as duas mensagens estão no heap, você pode usar um novo método,
UnsafeArenaSwap()
. Esse método evita a sobrecarga de
realizar a verificação de arena e evita a cópia se isso
tiver ocorrido.
Por exemplo, o código a seguir gera uma cópia na chamada Swap()
:
MyFeatureMessage* message_1 = google::protobuf::Arena::CreateMessage<MyFeatureMessage>(arena); message_1->mutable_nested_message()->set_feature_id(11); MyFeatureMessage* message_2 = new MyFeatureMessage; message_2->mutable_nested_message()->set_feature_id(22); message_1->Swap(message_2); // Inefficient swap!
Para evitar a cópia neste código, você aloca message_2
na
mesma arena que message_1
:
MyFeatureMessage* message_2 = google::protobuf::Arena::CreateMessage<MyFeatureMessage>(arena);
Granularidade
Descobrimos na maioria dos casos de uso do servidor de aplicativos que um modelo "arena por solicitação" funciona bem. Você pode ficar tentado a dividir o uso da arena
seja para reduzir a sobrecarga do heap (destruindo arenas menores com mais
frequência) ou para reduzir possíveis problemas de disputa de linha de execução. No entanto, o uso de
arenas mais detalhadas pode levar a cópias não intencionais de mensagens, conforme
descrito acima. Também nos esforçamos para otimizar a implementação de Arena
para o caso de uso de várias linhas de execução. Portanto, uma única arena será
apropriada para uso durante todo o ciclo de vida da solicitação, mesmo que várias linhas de execução processem essa solicitação.
Exemplo
Este é um exemplo completo simples que demonstra alguns dos recursos da API de alocação de arena.
// my_feature.proto syntax = "proto2"; import "nested_message.proto"; package feature_package; // NEXT Tag to use: 4 message MyFeatureMessage { optional string feature_name = 1; repeated int32 feature_data = 2; optional NestedMessage nested_message = 3; };
// nested_message.proto syntax = "proto2"; package feature_package; // NEXT Tag to use: 2 message NestedMessage { optional int32 feature_id = 1; };
Construção e desalocação de mensagens:
#include <google/protobuf/arena.h> Arena arena; MyFeatureMessage* arena_message = google::protobuf::Arena::CreateMessage<MyFeatureMessage>(&arena); arena_message->set_feature_name("Proto2 Arena"); arena_message->mutable_feature_data()->Add(2); arena_message->mutable_feature_data()->Add(4); arena_message->mutable_nested_message()->set_feature_id(247);