Estou tentando obter o máximo desempenho possível de algum método interno.
O código Java é:
List<DirectoryTaxonomyWriter> writers = Lists.newArrayList();
private final int taxos = 4;
[...]
@Override
public int getParent(final int globalOrdinal) throws IOException {
final int bin = globalOrdinal % this.taxos;
final int ordinalInBin = globalOrdinal / this.taxos;
return this.writers.get(bin).getParent(ordinalInBin) * this.taxos + bin; //global parent
}
No meu profiler, vi 1% de gasto de CPU java.util.Objects.requireNonNull
, mas nem chamo isso. Ao inspecionar o bytecode, vi o seguinte:
public getParent(I)I throws java/io/IOException
L0
LINENUMBER 70 L0
ILOAD 1
ALOAD 0
INVOKESTATIC java/util/Objects.requireNonNull (Ljava/lang/Object;)Ljava/lang/Object;
POP
BIPUSH 8
IREM
ISTORE 2
Portanto, o compilador gera essa verificação (inútil?). Eu trabalho com primitivas, que não podem ser null
assim, por que o compilador gera essa linha? Isso é um bug? Ou comportamento 'normal'?
(Eu posso trabalhar com uma máscara de bit, mas estou curioso)
[ATUALIZAR]
O operador parece não ter nada a ver com isso (veja a resposta abaixo)
Usando o compilador eclipse (versão 4.10), obtenho esse resultado mais razoável:
public getParent (I) lança java / io / IOException L0 LINENUMBER 77 L0 ILOAD 1 ICONST_4 IREM ISTORE 2 L1 LINENUMBER 78 L
Então isso é mais lógico.
INVOKESTATIC
javac
não gera isso.openjdk version "11.0.6" 2020-01-14
no ubuntu 64 bits.Respostas:
Por que não?
Assumindo
uma chamada como
c.test()
ondec
é declarada comoC
deve ser lançada quandoc
énull
. Seu método é equivalente acomo você trabalha apenas com constantes. Com
test
sendo não-estático, o cheque deve ser feito. Normalmente, isso seria feito implicitamente quando um campo é acessado ou um método não estático é chamado, mas você não faz isso. Portanto, é necessária uma verificação explícita. Uma possibilidade é ligarObjects.requireNonNull
.O bytecode
Não esqueça que o bytecode é basicamente irrelevante para o desempenho. A tarefa de
javac
é produzir algum bytecode cuja execução corresponda ao seu código fonte. Isso não deveria fazer qualquer otimizações, como código otimizado é geralmente mais longo e mais difícil de analisar, enquanto o bytecode é , na verdade, o código-fonte para o compilador otimizado JIT. Portanto,javac
espera-se mantê-lo simples ....O desempenho
Eu culparia o criador de perfil primeiro. A criação de perfil do Java é bastante difícil e você nunca pode esperar resultados perfeitos.
Você provavelmente deve tentar tornar o método estático. Você certamente deveria ler este artigo sobre verificações nulas .
fonte
this
é nãonull
. Como você mesmo disse, uma chamada comoc.test()
deve falhar quandoc
énull
e tem que falhar imediatamente, em vez de digitar o método. Portantotest()
, por dentro ,this
nunca pode sernull
(caso contrário, haveria um bug da JVM). Portanto, não há necessidade de verificar. A correção real deve estar alterando o campotaxos
parastatic
, pois não há sentido em reservar memória em todas as instâncias para uma constante em tempo de compilação. Então, setest()
éstatic
, é irrelevante.Bem, parece que minha pergunta estava "errada", pois não tem nada a ver com o operador, mas com o próprio campo. Ainda não sei por que ..
Que se transforma em:
fonte
this
referênciasnull
? Isso seria possível?Integer
alguma forma, e este é o resultado da caixa automática?ALOAD 0
referênciathis
? Portanto, faria sentido (não realmente) que o compilador adicione uma verificação nulathis
? Ótimo: /javac
para verificar amanhã; e se isso também mostrar esse comportamento, acho que pode ser um bug de javac?Em primeiro lugar, aqui está um exemplo reprodutível mínimo desse comportamento:
O comportamento é devido à forma como o compilador Java otimiza constantes em tempo de compilação .
Observe que no código de bytes de
foo()
nenhuma referência a objeto é acessada para obter o valor debar
. Isso ocorre porque é uma constante em tempo de compilação e, portanto, a JVM pode simplesmente executar oiconst_5
operação para retornar esse valor.Ao mudar
bar
para uma constante de tempo não compilada (removendo afinal
palavra - chave ou não inicializando dentro da declaração, mas dentro do construtor), você obteria:onde
aload_0
empurra a referência dethis
na pilha operando para, em seguida, obter obar
campo do objeto.Aqui, o compilador é inteligente o suficiente para notar que
aload_0
(athis
referência no caso de funções-membro) logicamente não pode sernull
.Agora, o seu caso é realmente uma otimização de compilador ausente?
Consulte a resposta @maaartinus.
fonte