Lista somente de acréscimos imutáveis em C# de alta velocidade. (atualização: 23 de setembro de 24; uso não recomendado. Veja as notas na parte inferior para justificativas e alternativas.)
dotnet adicionar pacote AppendOnly
Em primeiro lugar, as coleções imutáveis existentes têm muita dificuldade em permitir que você adicione, exclua e atualize que retorne novas coleções. Eles literalmente TÊM que copiar MUITO as coisas. Se você estiver apenas anexando, o LinkedList será seu amigo e poderá parecer "copiado" instantaneamente.
Em segundo lugar, as bibliotecas imutáveis existentes são complexas e difíceis de usar. Eles exigem classes de construtores e um bom conhecimento da mecânica real, caso contrário você terá sérios problemas de desempenho.
Por último: esta classe é totalmente segura para uso em loops realmente muito apertados. Tipo, o mais apertado possível.
AppendOnly usa A linkedList internamente e nunca copia os itens da coleção ao criar uma nova cópia "imutável". Ele simplesmente retorna uma nova classe de coleção que compartilha o mesmo linkedList interno "mutável", mas não expõe nenhum método para alterá-lo, expondo apenas um interator que itera pelos primeiros (n) itens da coleção.
Assim, à medida que a coleção cresce, as instâncias anteriores (mais antigas) da classe de coleção nunca verão (expostas por meio do enumerador) as novas entradas anexadas ao final da lista. Depois de adicionar 1.000 novas "transações" a uma transação AppendOnly, você efetivamente terá (se mantiver as referências a cada coleção recém-criada) 1.000 ponteiros para coleções, cada uma contendo 1, 2, 3, 4 itens etc., mas com quase zero adicional sobrecarga de memória.
Se você usar outras bibliotecas imutáveis com construtores, a maioria delas fornecerá no mínimo 500.000 instâncias de objetos.
Posso estar errado, é um ponto de partida, deixe-me saber se você usar isso. Criarei mais coleções em linhas semelhantes.
var moves = new AppendOnlyList();var new_moves = new AppendOnlyList ();Debug.Assert(moves.Length == 0);// adiciona 10 movimentos aleatórios for(int i=0; i<10 ; i++) new_moves = new_moves.Add(randomMove());// os movimentos originais ainda não possuem itens mesmo que new_list tenha adicionado um item ao List.Debug.Assert(moves.Length == 0);// new_list tem 10 itens em sua coleçãoDebug.Assert(new_moves.Length == 10);// esta lista é segura para iterar várias vezes e é threadsafe // o código abaixo mostra a iteração de toda a lista dez vezes, algo que você normalmente só faria em // um enumerável se o tivesse armazenado em cache, ou seja, criado uma cópia local.// Eu sei que isso não parece estar fazendo muito, mas isso é muito importante. // além disso, é seguro fazer isso ENQUANTO outros threads estão ocupados adicionando itens à mesma coleção subjacente, // algo que é um enorme NÃO NÃO no mundo dos threading. Aqui é totalmente seguro.for(int i = 0; i<10; i++){foreach(var move in new_moves.Items) await DoSomethingFunkyAsync(move);}
No código acima, new_moves
e os moves
originais compartilham a mesma lista vinculada subjacente. Quando uma nova cópia de AppendOnlyList é criada, NÃO há novos clones feitos, apenas um ponto no topo da lista e o Comprimento são mantidos.
Isso é EXTREMAMENTE RÁPIDO, pois NENHUMA CÓPIA é feita, portanto é infinitamente mais rápida do que qualquer outra coleção imutável que cria cópias.
E é seguro para threads iterar na coleção ENQUANTO ela está sendo enumerada de outros threads! BUM! ... ou mais especificamente "no Boom!".
esta é a versão 0.1.3
, o que significa que não está pronta para uso em produção. É uma prova de conceito, e preciso escrever alguns testes de threading para provar as afirmações acima e, em seguida, pedir aos meus colegas que revisem e me avisem.
Também quero fazer uma comparação de velocidade entre esta e outras coleções, bem como uma comparação lado a lado.
se você gosta deste pacote, dê uma olhada na implementação real, são literalmente 2 classes, AppendOnlyList
e LinkedListExtensions
, e elas são minúsculas.
deixe-me saber o que você pensa?
:D
Qualquer dúvida, entre em contato comigo,
Alan Hemmings, twitter : @snowcode LinkedIn : https://www.linkedin.com/in/goblinfactory www : https://goblinfactory.co.uk
...e não usei muito, e bem, esqueci. As coisas (C#) mudaram muito desde que escrevi isso e, embora haja muitas mudanças no C#, nenhuma delas realmente simplificou muito as coisas. Infelizmente, acho que meus pensamentos acima, sobre quando usar este pacote, à luz das novas mudanças de idioma, podem ser uma porcaria e podem ter sido... bastante defeituosos. Os testes são DEFINITIVAMENTE necessários antes de fazer qualquer coisa adicional. Aqui estão algumas notas de um bate-papo rápido com um arquiteto AI C#, sugerindo uma revisão deste projeto. TBH, acho que este projeto ainda pode ser justificado, mas talvez como um wrapper DSL (uma espécie de classe de fábrica simples) em torno das diferentes maneiras de criar adequadamente estruturas do tipo anexadas. Nuff disse; aqui está o feedback;
(rascunhos de notas precisam ser testados e confirmados!)
Sim, em C#, você pode usar System.Collections.Generic.ArrayBuffer
ou System.Buffers.ArrayPool
para estruturas mais eficientes somente para acréscimos e com uso eficiente de memória que podem ser enumeradas com eficiência. No entanto, se você deseja uma estrutura apenas de acréscimos mais prática e eficiente, considere estas opções:
ImmutableArray
Uma estrutura somente de acréscimo fornecida pelo pacote System.Collections.Immutable
.
Ele permite acréscimos eficientes e é mutável durante a construção, mas se torna imutável depois de convertido em ImmutableArray
.
var construtor = ImmutableArray.CreateBuilder();builder.Add(1);builder.Add(2);var immutableArray = construtor.ToImmutable();
Memory
e Span
com ArrayPool
Eficiente para lidar com grandes blocos de dados com alocações mínimas de memória.
Você mesmo gerencia a memória, o que evita realocações frequentes em comparação com List
.
Adequado se você deseja mais controle sobre o uso da memória, mas requer um gerenciamento cuidadoso.
var pool = ArrayPool.Shared;int[] array = pool.Rent(1000); // Solicite um array do pool// Anexe e gerencie seus dados aquipool.Return(array); //Retorna para a piscina quando terminar
ValueListBuilder
(de System.Buffers)
Fornece uma maneira eficiente de criar coleções de maneira alocada por pilha.
Especialmente útil quando você sabe que o tamanho dos dados é limitado e deseja alto desempenho sem alocações de heap.
SpaninicialSpan = stackalloc int[16]; var valueListBuilder = new ValueListBuilder (initialSpan);valueListBuilder.Append(1);valueListBuilder.Append(2);ReadOnlySpan span = valueListBuilder.AsSpan();
Sequence
de System.IO.Pipelines
Adequado para lidar com uma sequência de dados de forma eficiente apenas com acréscimos.
Bom para cenários que exigem operações de E/S de alto desempenho e streaming de dados.
List
: Bom para anexar (amortizado O(1)
) e fornece enumeração rápida devido à memória contígua.
ImmutableArray
: eficiente para anexar e tornar imutável.
Memory
com ArrayPool
: ótimo para reduzir alocações.
ValueListBuilder
: construção eficiente baseada em pilha quando o tamanho é conhecido. Baseado em pilha, adequado para coleções temporárias e eficientes.
Sequence
: para cenários de alto rendimento que necessitam de anexação eficiente.
Escolha com base em seus requisitos específicos de desempenho e memória.
ChatGPT disse;
LinkedList
em C# é uma opção para uma estrutura de memória somente anexada, mas geralmente é menos eficiente em termos de memória e mais lenta para enumeração em comparação com outras estruturas devido aos seguintes motivos:
LinkedList
: Sobrecarga de memória : cada nó em um LinkedList
contém referências aos nós seguintes e anteriores, o que introduz uma sobrecarga de memória significativa em comparação com estruturas contíguas como matrizes ou List
.
Ineficiência de cache : ao contrário de arrays ou List
que são armazenados em memória contígua, os nós LinkedList
estão espalhados pelo heap. Isso o torna menos amigável ao cache, levando a iterações e tempos de acesso mais lentos.
Desempenho de enumeração : embora LinkedList
seja eficiente para operações de acréscimo ( O(1)
para adição ao final), seu desempenho de enumeração é normalmente mais lento do que matrizes ou listas porque não possui a vantagem de localidade do cache.
LinkedList
:Quando você precisa de inserções ou exclusões frequentes nas duas extremidades ou no meio.
Se você quiser evitar a mudança de elementos como em List
.
LinkedList
pode ser usado para cenários somente de acréscimo, mas geralmente não é a escolha mais eficiente para enumeração ou uso de memória em comparação com List
ou outras estruturas de dados modernas.