Eu tenho trabalhado sob o princípio de compartilhar nada da programação simultânea. Essencialmente, todos os meus threads de trabalho têm cópias imutáveis de somente leitura do mesmo estado que nunca são compartilhadas entre eles ( mesmo por referência ). De um modo geral, isso funcionou muito bem.
Agora, alguém introduziu um cache singleton sem bloqueio ( por exemplo, um dicionário estático ) que todos os threads estão acessando simultaneamente. Como o dicionário nunca é alterado após a inicialização, não há bloqueios. Não houve nenhum problema de segurança de thread, mas agora há uma degradação do desempenho.
A questão é ... como não há bloqueios, por que a introdução desse singleton cria um impacto no desempenho? O que exatamente está acontecendo nos bastidores que poderia explicar isso?
Para confirmar, o acesso a esse novo singleton é a única alteração e posso recriá-lo com segurança simplesmente comentando a chamada para o cache.
fonte
Respostas:
Pode ser que o estado imutável compartilhe uma linha de cache com algo mutável. Nesse caso, uma alteração no estado mutável próximo pode ter o efeito de forçar uma ressincronização dessa linha de cache entre os núcleos, o que pode diminuir o desempenho.
fonte
false sharing
cenário que você está descrevendo. Para isolar isso, preciso criar um perfil do cache L2. Infelizmente, esses são tipos de referência, portanto, adicionar espaço no buffer não será uma opção se é isso que está realmente acontecendo.Eu me certificaria de que os métodos
Equals()
eGetHashCode()
dos objetos que você usa como chaves do dicionário não tenham efeitos colaterais inesperados e não compatíveis com o encadeamento. A criação de perfil ajudaria bastante aqui.Se, por um acaso, suas chaves são strings, talvez seja isso: existem rumores de que as strings se comportam como objetos imutáveis, mas, por causa de certas otimizações, elas são implementadas internamente de maneira mutável, com tudo o que isso implica em relação à multithreading .
Eu tentaria passar o dicionário para os segmentos que o usam como referência regular, em vez de um singleton, para verificar se o problema está no compartilhamento ou no singleton do dicionário. (Eliminando as possíveis causas.)
Eu também tentaria com um em
ConcurrentDictionary
vez de um regularDictionary
, caso seu uso produza alguns resultados surpreendentes. Há muitas coisas a serem especuladas sobre o problema em questão, se um tiver umConcurrentDictionary
desempenho muito melhor ou muito pior do que o normalDictionary
.Se nenhuma das opções acima apontar para o problema, acho que o desempenho degradado é causado por algum tipo estranho de contenção entre o encadeamento de coleta de lixo e o restante de seus encadeamentos, pois o coletor de lixo está tentando descobrir se os objetos no seu dicionário precisam ser descartados ou não, enquanto estão sendo acessados por seus threads.
fonte