Supondo que temos a T myarray[100]
com T = int, unsigned int, long long int ou unsigned long long int, qual é a maneira mais rápida de redefinir todo o seu conteúdo para zero (não apenas para inicialização, mas para redefinir o conteúdo várias vezes em meu programa) ? Talvez com o memset?
Mesma pergunta para um array dinâmico como T *myarray = new T[100]
.
new
é C ++ ...memset
quando C ++ está de alguma forma envolvido ... :)for
loop simples . Mas, surpreendentemente, você pode fazer muito pior tentando ser inteligente.Respostas:
memset
(de<string.h>
) é provavelmente a forma padrão mais rápida, já que geralmente é uma rotina escrita diretamente em assembly e otimizada à mão.A propósito, em C ++ a forma idiomática seria usar
std::fill
(de<algorithm>
):que pode ser otimizado automaticamente em um
memset
; Tenho certeza de que funcionará tão rápido quantomemset
paraint
s, embora possa ter um desempenho um pouco pior para tipos menores se o otimizador não for inteligente o suficiente. Ainda assim, em caso de dúvida, perfil.fonte
memset
definiria um inteiro como 0; não havia nenhuma declaração específica de que todos-bits-zero fosse uma representação0
. Uma Corrigenda Técnica acrescentou essa garantia, que está incluída no padrão ISO C de 2011. Eu acredito que all-bits-zero é uma representação válida de0
para todos os tipos inteiros em todas as implementações C e C ++ existentes, razão pela qual o comitê foi capaz de adicionar esse requisito. (Não há garantia semelhante para tipos de ponto flutuante ou ponteiro.)0
. (Com bits de preenchimento, existe a possibilidade de que todos os bits zero possam ser uma representação de trap). Mas, em qualquer caso, o TC deve reconhecer e substituir o texto defeituoso, portanto, a partir de 2004, devemos agir como se C99 sempre contivesse esse texto.int (*myarray)[N] = malloc(sizeof(*myarray));
.N
é o tamanho , mas na grande maioria dos casos, se você usou,malloc
você só sabe em tempo de execução.Esta questão, embora bastante antiga, precisa de alguns benchmarks, pois pede não a forma mais idiomática, ou a forma que pode ser escrita com o menor número de linhas, mas a forma mais rápida. E é bobagem responder a essa pergunta sem alguns testes reais. Então, comparei quatro soluções, memset vs. std :: fill vs. ZERO da resposta da AnT vs. uma solução que fiz usando intrínseco AVX.
Observe que esta solução não é genérica, ela só funciona com dados de 32 ou 64 bits. Por favor, comente se este código está fazendo algo incorreto.
Não vou afirmar que esse é o método mais rápido, já que não sou um especialista em otimização de baixo nível. Em vez disso, é um exemplo de uma implementação dependente de arquitetura correta que é mais rápida do que o memset.
Agora, vamos aos resultados. Calculei o desempenho para o tamanho 100 int e arrays longos longos, tanto estaticamente quanto dinamicamente alocados, mas com exceção do msvc, que eliminou o código morto em arrays estáticos, os resultados foram extremamente comparáveis, então mostrarei apenas o desempenho do array dinâmico. As marcações de tempo são ms para 1 milhão de iterações, usando a função de relógio de baixa precisão de time.h.
clang 3.8 (usando a interface clang-cl, sinalizadores de otimização = / OX / arch: AVX / Oi / Ot)
gcc 5.1.0 (sinalizadores de otimização: -O3 -march = native -mtune = native -mavx):
msvc 2015 (sinalizadores de otimização: / OX / arch: AVX / Oi / Ot):
Há muita coisa interessante acontecendo aqui: llvm kill gcc, as otimizações irregulares típicas do MSVC (ele faz uma eliminação impressionante de código morto em arrays estáticos e então tem um desempenho péssimo para preencher). Embora minha implementação seja significativamente mais rápida, isso pode ser apenas porque ela reconhece que a limpeza de bits tem muito menos sobrecarga do que qualquer outra operação de configuração.
A implementação do Clang merece mais atenção, pois é significativamente mais rápida. Alguns testes adicionais mostram que seu memset é de fato especializado para zero - diferente de zero memsets para matriz de 400 bytes são muito mais lentos (~ 220ms) e são comparáveis aos do gcc. No entanto, o memsetting diferente de zero com um array de 800 bytes não faz diferença na velocidade, o que provavelmente é porque, nesse caso, o memset deles tem pior desempenho do que a minha implementação - a especialização é apenas para pequenos arrays e o corte está em torno de 800 bytes. Observe também que gcc 'fill' e 'ZERO' não estão otimizando para memset (olhando para o código gerado), gcc está simplesmente gerando código com características de desempenho idênticas.
Conclusão: o memset não é realmente otimizado para essa tarefa tão bem quanto as pessoas fingem que está (caso contrário, o memset do gcc e do msvc e do llvm teria o mesmo desempenho). Se o desempenho é importante, o memset não deve ser a solução final, especialmente para esses arrays de tamanho médio desajeitados, porque não é especializado para limpeza de bits e não é otimizado manualmente melhor do que o compilador pode fazer sozinho.
fonte
a
cabem em um registro. Depois, ele faz um loop em todos os blocos de 32 bytes, que devem ser totalmente sobrescritos usando aritmética de ponteiro ((float *)((a)+x)
). Os dois intrínsecos (começando com_mm256
) apenas criam um registro de 32 bytes inicializado com zero e o armazena no ponteiro atual. Estas são as primeiras 3 linhas. O resto apenas trata de todos os casos especiais em que o último bloco de 32 bytes não deve ser totalmente substituído. É mais rápido devido à vetorização. - Espero que ajude.De
memset()
:Você pode usar
sizeof(myarray)
se o tamanho demyarray
for conhecido em tempo de compilação. Caso contrário, se você estiver usando uma matriz de tamanho dinâmico, como obtida por meio demalloc
ounew
, será necessário controlar o comprimento.fonte
sizeof
é sempre avaliado em tempo de compilação (e não pode ser usado com VLAs). Em C99, pode ser uma expressão de tempo de execução no caso de VLAs.c
ec++
. Eu comentei sobre a resposta de Alex, que diz "Você pode usar sizeof (myarray) se o tamanho de myarray for conhecido em tempo de compilação".Você pode usar
memset
, mas apenas porque nossa seleção de tipos é restrita a tipos integrais.No caso geral em C, faz sentido implementar uma macro
Isso lhe dará uma funcionalidade semelhante à do C ++ que permitirá que você "redefina para zeros" uma série de objetos de qualquer tipo sem ter que recorrer a hacks
memset
. Basicamente, este é um C análogo ao template de função C ++, exceto que você deve especificar o argumento type explicitamente.Além disso, você pode construir um "modelo" para matrizes não deterioradas
Em seu exemplo, seria aplicado como
Também é importante notar que especificamente para objetos de tipos escalares, pode-se implementar uma macro independente de tipo
e
transformando o exemplo acima em
fonte
;
depois dowhile(0)
, para que possamos ligarZERO(a,n);
, +1 ótima respostado{}while(0)
idioma requer não;
na definição macro. Fixo.Para declaração estática, acho que você poderia usar:
Para declaração dinâmica, sugiro da mesma forma:
memset
fonte
zero(myarray);
é tudo o que você precisa em C ++.Basta adicionar a um cabeçalho:
fonte
zero
também é correta para, por exemplo,T=char[10]
como poderia ser o caso quando oarr
argumento é um array multidimensional, por exemplochar arr[5][10]
.ARRAY_SIZE
macro, que dá o tamanho errado se usada em uma matriz multidimensional, um nome melhor talvez sejaARRAY_DIM<n>_SIZE
.Esta é a função que uso:
Você pode chamá-lo assim:
Acima está mais em C ++ 11 do que usando memset. Além disso, você obterá um erro de tempo de compilação se usar uma matriz dinâmica com a especificação do tamanho.
fonte