Verifique as medições da JMH de simples para / comparações lambda

David M. Karr:

Eu queria fazer algumas medições de desempenho e comparações de simples para loops e córregos equivalentes implementações. Eu acredito que é o caso de que fluxos será um pouco mais lento do que o equivalente não-córregos código, mas eu queria ter certeza que eu estou medindo as coisas certas.

Estou incluindo toda a minha classe jmh aqui.

import java.util.ArrayList;
import java.util.List;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;

@State(Scope.Benchmark)
public class MyBenchmark {
    List<String>    shortLengthListConstantSize     = null;
    List<String>    mediumLengthListConstantSize    = null;
    List<String>    longerLengthListConstantSize    = null;
    List<String>    longLengthListConstantSize      = null;

    @Setup
    public void setup() {
        shortLengthListConstantSize     = populateList(2);
        mediumLengthListConstantSize    = populateList(12);
        longerLengthListConstantSize    = populateList(300);
        longLengthListConstantSize      = populateList(300000);
    }

    private List<String> populateList(int size) {
        List<String> list   = new ArrayList<>();
        for (int ctr = 0; ctr < size; ++ ctr) {
            list.add("xxx");
        }
        return list;
    }

    @Benchmark
    public long shortLengthConstantSizeFor() {
        long count   = 0;
        for (String val : shortLengthListConstantSize) {
            if (val.length() == 3) { ++ count; }
        }
        return count;
    }

    @Benchmark
    public long shortLengthConstantSizeForEach() {
        IntHolder   intHolder   = new IntHolder();
        shortLengthListConstantSize.forEach(s -> { if (s.length() == 3) ++ intHolder.value; } );
        return intHolder.value;
    }

    @Benchmark
    public long shortLengthConstantSizeLambda() {
        return shortLengthListConstantSize.stream().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long shortLengthConstantSizeLambdaParallel() {
        return shortLengthListConstantSize.stream().parallel().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long mediumLengthConstantSizeFor() {
        long count   = 0;
        for (String val : mediumLengthListConstantSize) {
            if (val.length() == 3) { ++ count; }
        }
        return count;
    }

    @Benchmark
    public long mediumLengthConstantSizeForEach() {
        IntHolder   intHolder   = new IntHolder();
        mediumLengthListConstantSize.forEach(s -> { if (s.length() == 3) ++ intHolder.value; } );
        return intHolder.value;
    }

    @Benchmark
    public long mediumLengthConstantSizeLambda() {
        return mediumLengthListConstantSize.stream().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long mediumLengthConstantSizeLambdaParallel() {
        return mediumLengthListConstantSize.stream().parallel().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long longerLengthConstantSizeFor() {
        long count   = 0;
        for (String val : longerLengthListConstantSize) {
            if (val.length() == 3) { ++ count; }
        }
        return count;
    }

    @Benchmark
    public long longerLengthConstantSizeForEach() {
        IntHolder   intHolder   = new IntHolder();
        longerLengthListConstantSize.forEach(s -> { if (s.length() == 3) ++ intHolder.value; } );
        return intHolder.value;
    }

    @Benchmark
    public long longerLengthConstantSizeLambda() {
        return longerLengthListConstantSize.stream().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long longerLengthConstantSizeLambdaParallel() {
        return longerLengthListConstantSize.stream().parallel().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long longLengthConstantSizeFor() {
        long count   = 0;
        for (String val : longLengthListConstantSize) {
            if (val.length() == 3) { ++ count; }
        }
        return count;
    }

    @Benchmark
    public long longLengthConstantSizeForEach() {
        IntHolder   intHolder   = new IntHolder();
        longLengthListConstantSize.forEach(s -> { if (s.length() == 3) ++ intHolder.value; } );
        return intHolder.value;
    }

    @Benchmark
    public long longLengthConstantSizeLambda() {
        return longLengthListConstantSize.stream().filter(s -> s.length() == 3).count();
    }

    @Benchmark
    public long longLengthConstantSizeLambdaParallel() {
        return longLengthListConstantSize.stream().parallel().filter(s -> s.length() == 3).count();
    }

    public static class IntHolder {
        public int value    = 0;
    }
}

Eu estou correndo estes em um laptop Win7. Eu não me importo sobre medidas absolutas, apenas relativa. Aqui estão os mais recentes resultados de estes:

Benchmark                                            Mode  Cnt          Score         Error  Units
MyBenchmark.longLengthConstantSizeFor               thrpt  200       2984.554 ±      57.557  ops/s
MyBenchmark.longLengthConstantSizeForEach           thrpt  200       2971.701 ±     110.414  ops/s
MyBenchmark.longLengthConstantSizeLambda            thrpt  200        331.741 ±       2.196  ops/s
MyBenchmark.longLengthConstantSizeLambdaParallel    thrpt  200       2827.695 ±     682.662  ops/s
MyBenchmark.longerLengthConstantSizeFor             thrpt  200    3551842.518 ±   42612.744  ops/s
MyBenchmark.longerLengthConstantSizeForEach         thrpt  200    3616285.629 ±   16335.379  ops/s
MyBenchmark.longerLengthConstantSizeLambda          thrpt  200    2791292.093 ±   12207.302  ops/s
MyBenchmark.longerLengthConstantSizeLambdaParallel  thrpt  200      50278.869 ±    1977.648  ops/s
MyBenchmark.mediumLengthConstantSizeFor             thrpt  200   55447999.297 ±  277442.812  ops/s
MyBenchmark.mediumLengthConstantSizeForEach         thrpt  200   57381287.954 ±  362751.975  ops/s
MyBenchmark.mediumLengthConstantSizeLambda          thrpt  200   15925281.039 ±   65707.093  ops/s
MyBenchmark.mediumLengthConstantSizeLambdaParallel  thrpt  200      60082.495 ±     581.405  ops/s
MyBenchmark.shortLengthConstantSizeFor              thrpt  200  132278188.475 ± 1132184.820  ops/s
MyBenchmark.shortLengthConstantSizeForEach          thrpt  200  124158664.044 ± 1112991.883  ops/s
MyBenchmark.shortLengthConstantSizeLambda           thrpt  200   18750818.019 ±  171239.562  ops/s
MyBenchmark.shortLengthConstantSizeLambdaParallel   thrpt  200     474054.951 ±    1344.705  ops/s

Em uma pergunta anterior, eu confirmou que esses benchmarks parecem ser "funcionalmente equivalente" (apenas procurando olhos adicionais). Que esses números parecem estar em linha, talvez com corridas independentes destes benchmarks?

Outra coisa que eu sempre fui incerto sobre com saída JMH, é determinar exatamente o que os números representam rendimento. Por exemplo, o que o "200" na coluna "Cnt" representam exatamente? As unidades de rendimento estão em "operações por segundo", então o que faz exatamente a "operação" representam, é que a execução de uma chamada para o método de referência? Por exemplo, na última linha, que representaria 474k execuções do método de referência em um segundo.

atualização :

Faço notar que quando eu comparar o "para" com o "lambda", começando com a lista de "short" e ir para listas mais longas, a relação entre eles é muito grande, mas diminui, até que a lista "longa", onde a proporção é ainda maior do que para a lista de "short" (14%, 29%, 78% e 11%). Acho isso surpreendente. Eu teria esperado que a razão entre os fluxos de sobrecarga para diminuir à medida que o trabalho nas reais aumenta de lógica de negócios. Alguém tem alguma ideia sobre isso?

Svetlin Zarev:

Por exemplo, o que o "200" na coluna "Cnt" representam exatamente?

A cntcoluna é o número de iterações - isto é, quantas vezes por testes é repetido. Você pode controlar esse valor usando as seguintes anotações:

  • Para as medições reais: @Measurement(iterations = 10, time = 50, timeUnit = TimeUnit.MILLISECONDS)
  • Para a fase de aquecimento: @Warmup(iterations = 10, time = 1, timeUnit = TimeUnit.SECONDS)

Aqui iterationsé cnt; timerepresenta a duração requerida de uma iteração, e timeUnité a unidade de medida do timevalor.

As unidades de transferência são em "operações por segundo"

Você pode controlar a saída de várias maneiras. Por exemplo, você pode alterar a unidade de medida para o tempo de utilização @OutputTimeUnit(TimeUnit.XXXX), para que possa obter ops / US, ops / ms

Você também pode alterar o mode: em vez de measureing ops / hora você pode medir o "tempo médio", "tempo de amostra", etc. Você pode controlar esta via a @BenchmarkMode({Mode.AverageTime})anotação

Então o que exatamente faz a "operação" representam, é que a execução de uma chamada para o método de referência

Então, digamos que uma iteração é de 1 segundo de duração e você começa 1000 ops / seg. Isto significa que o método benchamrk foi executada 1000 vezes.

Em outras palavras uma operação é uma execução do método de referência, a menos que você tem a @OperationsPerInvocation(XXX)anotação, o que significa tha invocação teach dos métodos contará como operações XXX.

O erro é calculado em todas as iterações.


Mais uma dica: em vez de codificar cada tamanho possível, você pode fazer uma referência com parâmetros:

@Param({"3", "12", "300", "3000"})
private int length;

Então você pode usar que param na sua configuração:

 @Setup(Level.Iteration)
 public void setUp(){
     populateList(length)
 }

Acho que você gosta

Origin http://43.154.161.224:23101/article/api/json?id=195608&siteId=1
Recomendado
Clasificación