Recentemente, levantei uma questão em stackoverflow e encontrei a resposta. A pergunta inicial era: quais mecanismos diferentes de mutexs ou coleta de lixo podem tornar meu programa java multi-threaded lento?
Eu descobri para meu horror que o HashMap foi modificado entre JDK1.6 e JDK1.7. Ele agora tem um bloco de código que faz com que todos os threads que criam HashMaps sejam sincronizados.
A linha de código em JDK1.7.0_10 é
/**A randomizing value associated with this instance that is applied to hash code of keys to make hash collisions harder to find. */
transient final int hashSeed = sun.misc.Hashing.randomHashSeed(this);
Que acaba ligando
protected int next(int bits) {
long oldseed, nextseed;
AtomicLong seed = this.seed;
do {
oldseed = seed.get();
nextseed = (oldseed * multiplier + addend) & mask;
} while (!seed.compareAndSet(oldseed, nextseed));
return (int)(nextseed >>> (48 - bits));
}
Procurando em outros JDKs, descobri que isso não está presente em JDK1.5.0_22 ou JDK1.6.0_26.
O impacto no meu código é enorme. Isso faz com que, quando executo em 64 threads, obtenho menos desempenho do que quando executo em 1 thread. Um JStack mostra que a maioria dos threads está gastando a maior parte do tempo girando nesse loop em Random.
Então, parece que tenho algumas opções:
- Reescrever meu código para que eu não use HashMap, mas use algo semelhante
- De alguma forma, mexa com o rt.jar e substitua o hashmap dentro dele
- Mexa com o caminho da classe de alguma forma, para que cada thread obtenha sua própria versão de HashMap
Antes de começar a trilhar qualquer um desses caminhos (todos parecem muito demorados e potencialmente de alto impacto), me perguntei se não percebi um truque óbvio. Qualquer um de vocês pode sugerir o melhor caminho, ou talvez identificar uma nova ideia.
Obrigado pela ajuda