OBSERVAÇÃO:este site foi descontinuado. O site será desativado após 31 de janeiro de 2023, e o tráfego será redirecionado para o novo site em https://protobuf.dev. Enquanto isso, as atualizações serão feitas apenas para protobuf.dev.

Guia de alocação da Arena C++

Mantenha tudo organizado com as coleções Salve e categorize o conteúdo com base nas suas preferências.

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.&hookleftarrow;

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 em ArenaOptions 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 mensagem T 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 a CreateMessage(), 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): se arena não for NULL, esse método alocará armazenamento bruto para elementos n do tipo T e o retornará. A arena é dona da memória retornada e a libera após a destruição. Se arena 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): adiciona object à 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 de object à 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 por AllocateArray().

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 a Arena seja usada em instanciações de modelo que esperam que os métodos GetArena() 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, o Swap() 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étodo New() 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 por arena->Own(message) se arena 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 a Swap(), 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 de Swap(), ele não precisa verificar quais mensagens estão em qual arena antes da troca. Como o prefixo Unsafe 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 com arena->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.
  • 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 a set_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 a release_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 de RepeatedField 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 de RepeatedPtrField 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.
    Isso mantém a invariante de todos os objetos apontados por um campo repetido que estão no mesmo domínio de propriedade (heap ou arena específica), conforme indicado pelo ponteiro da arena do campo repetido.
  • 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 a AddAllocated(), 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 a ReleaseLast(), 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 elementos num do campo repetido, começando pelo índice start, e os retorna em elements 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 elementos num do campo repetido a partir do índice start e os retorna em elements caso não seja NULL. Ao contrário de ExtractSubrange(), 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);