0% acharam este documento útil (0 voto)
4 visualizações5 páginas

JMH: Guia Completo para Microbenchmarking

O JMH (Java Microbenchmark Harness) é uma ferramenta desenvolvida para medir com precisão a performance de código Java, superando limitações de medições simples na JVM. O artigo aborda desde a configuração do JMH até a criação de benchmarks, incluindo conceitos fundamentais, modos de benchmarking e melhores práticas para evitar armadilhas comuns. O JMH é essencial para otimizar código e garantir o melhor desempenho em sistemas de alto desempenho.

Enviado por

livioandrade
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd
0% acharam este documento útil (0 voto)
4 visualizações5 páginas

JMH: Guia Completo para Microbenchmarking

O JMH (Java Microbenchmark Harness) é uma ferramenta desenvolvida para medir com precisão a performance de código Java, superando limitações de medições simples na JVM. O artigo aborda desde a configuração do JMH até a criação de benchmarks, incluindo conceitos fundamentais, modos de benchmarking e melhores práticas para evitar armadilhas comuns. O JMH é essencial para otimizar código e garantir o melhor desempenho em sistemas de alto desempenho.

Enviado por

livioandrade
Direitos autorais
© All Rights Reserved
Levamos muito a sério os direitos de conteúdo. Se você suspeita que este conteúdo é seu, reivindique-o aqui.
Formatos disponíveis
Baixe no formato PDF, TXT ou leia on-line no Scribd

Java Microbenchmark Harness - JMH

Do Básico ao Avançado

No mundo do desenvolvimento de software, a otimização de código é um desafio


constante. Pequenos ajustes podem gerar grandes impactos na performance de
sistemas de alto desempenho.

No entanto, medir corretamente o tempo de execução de trechos de código na


JVM (Java Virtual Machine) pode ser mais complicado do que parece.

A JVM possui otimizações internas, como Just-In-Time Compilation (JIT),


Garbage Collection (GC), e outros mecanismos que podem distorcer medições
simplistas com [Link]().

Para solucionar esse problema, a equipe do OpenJDK desenvolveu o JMH (Java


Microbenchmark Harness), uma ferramenta robusta para criar
microbenchmarks confiáveis.

O JMH ajuda a medir com precisão a performance de métodos e algoritmos,


protegendo contra otimizações enganosas, aquecimento da JVM (warm-up), e
concorrência.

Neste artigo, exploraremos o JMH desde a configuração inicial até o uso


avançado, incluindo testes concorrentes, análise de throughput e melhores
práticas para benchmarking eficiente.

1. Configuração do JMH no Projeto


Para utilizar o JMH, você pode configurá-lo via Maven ou Gradle.

1.1 Dependências com Maven

Adicione as seguintes dependências ao seu [Link]:

xml
<dependencies>
<dependency>
<groupId>[Link]</groupId>
<artifactId>jmh-core</artifactId>
<version>1.38</version>
</dependency>
<dependency>
<groupId>[Link]</groupId>
<artifactId>jmh-generator-annprocess</artifactId>
<version>1.38</version>
<scope>provided</scope>
</dependency>
</dependencies>

1.2 Dependências com Gradle

Se estiver usando Gradle, adicione:

gradle
dependencies {
implementation '[Link]:jmh-core:1.38'
annotationProcessor '[Link]:jmh-generator-annprocess:1.38'
}

2. Conceitos Fundamentais do JMH


O JMH usa anotações para definir benchmarks. Vamos explorar as principais:

• @Benchmark → Indica o método que será testado.


• @BenchmarkMode → Define o modo de medição:
o Throughput: Mede o número de operações por segundo.
o AverageTime: Mede o tempo médio por operação.
o SampleTime: Mede o tempo de execução baseado em amostras.
o SingleShotTime: Mede o tempo de uma única execução.
• @OutputTimeUnit → Define a unidade de tempo dos resultados
(nanossegundos, microssegundos, milissegundos).
• @State([Link]) → Controla o escopo da instância do benchmark.
• @Warmup → Define execuções iniciais para compensar o warm-up da JVM.
• @Fork → Define quantas vezes o benchmark será executado em processos
separados.
• @Threads → Define quantas threads serão usadas para execução concorrente.

3. Criando um Benchmark Simples


Agora que entendemos os conceitos básicos, vamos criar nosso primeiro
benchmark para medir a execução de um loop de soma:

import [Link].*;

import [Link];

@BenchmarkMode([Link]) // Mede tempo médio de execução


@OutputTimeUnit([Link]) // Unidade: nanossegundos
@State([Link]) // Cada thread terá sua própria instância do benchmark
public class SimpleBenchmark {

@Benchmark
public int calcularSoma() {
int soma = 0;
for (int i = 0; i < 1000; i++) {
soma += i;
}
return soma;
}
}

Executando o Benchmark

Gere o JAR do benchmark e execute:

mvn clean install


java -jar target/[Link]

Saída esperada:

Benchmark Mode Cnt Score Error Units


[Link] avgt 10 12.345 ± 0.123 ns/op

Isso significa que a operação de soma leva 12.345 nanossegundos por


execução.

4. Modos de Benchmarking no JMH


O JMH permite diferentes modos para analisar o desempenho:

4.1 Medindo Throughput

Mede quantas operações são realizadas por unidade de tempo.

@BenchmarkMode([Link])
@OutputTimeUnit([Link])
@State([Link])
public class ThroughputBenchmark {

@Benchmark
public int processarDados() {
int resultado = 0;
for (int i = 0; i < 10000; i++) {
resultado += i;
}
return resultado;
}
}

4.2 Medindo Tempo Médio

Mede o tempo médio de cada operação.

@BenchmarkMode([Link])
@OutputTimeUnit([Link])
@State([Link])
public class AverageTimeBenchmark {

@Benchmark
public void executarTarefa() {
try {
[Link](5); // Simula uma operação I/O
} catch (InterruptedException e) {
[Link]().interrupt();
}
}
}

5. Benchmarks Concorrentes no JMH


Agora, vamos medir o desempenho de um contador compartilhado usando
AtomicInteger e um contador normal.

import [Link];
import [Link].*;

import [Link];

@BenchmarkMode([Link])
@OutputTimeUnit([Link])
@State([Link])
public class ConcurrencyBenchmark {

private final AtomicInteger atomicCounter = new AtomicInteger();


private int normalCounter = 0;

@Benchmark
@Group("atomic")
public int incrementarAtomic() {
return [Link]();
}

@Benchmark
@Group("normal")
public int incrementarNormal() {
return ++normalCounter;
}
}

Aqui, podemos comparar AtomicInteger com um contador comum para analisar


os impactos de contenção e concorrência.
6. Evitando Armadilhas no Benchmarking
Mesmo com o JMH, erros comuns podem prejudicar os resultados:

1. Dead Code Elimination → Se o resultado não for usado, o JIT pode removê-lo.
Sempre retorne valores no benchmark.
2. Loop Unrolling → Pequenos loops podem ser otimizados pelo compilador.
3. Warm-up → Sempre utilize @Warmup para evitar que otimizações da JVM
distorçam os resultados.
4. Efeito do Garbage Collector (GC) → O GC pode interferir na medição. Use -
XX:+PrintGC para monitorá-lo.
5. Influência de Outros Processos → Execute benchmarks em uma máquina
ociosa para evitar interferências externas.

O JMH é uma ferramenta essencial para quem deseja medir a performance de


código Java com precisão.

Ele elimina os problemas de medições ingênuas e permite analisar desde


pequenas operações até cenários complexos envolvendo concorrência.

Exploramos desde benchmarks básicos até análises avançadas de throughput e


concorrência.

Com isso, você pode otimizar código crítico, evitar armadilhas do JIT e garantir
que suas aplicações alcancem o melhor desempenho possível.

Se você trabalha com otimização de código ou desenvolvimento de sistemas de


alto desempenho, o JMH deve fazer parte do seu arsenal de ferramentas.

Referências

• Documentação oficial: [Link]


• Código-fonte do JMH: [Link]

EducaCiência FastCode para a comunidade

Você também pode gostar