Classic singleton and lazy using java 8 performance

Recently, I read an article "be lazy with Java 8", which introduces a method to create inert objects (objects whose internal state will be created on the first access)

public final class Lazy<T> {

    private volatile T value;

    public T getOrCompute(supplier<T> supplier){
        final T result = value;
        return result == null ? maybeCompute(supplier) : result;
    }

    private synchronized T maybeCompute(supplier<T> supplier) {
        if (value == null){
            value = Objects.requireNonNull(supplier.get());
        }
        return value;
    }
}

I found that this pattern is very similar to the well-known singleton pattern, except for generics:

public class PropertiesSingleton {

    public static Properties getProperties(){
        return Helper.INSTANCE;
    }

    private final static class Helper{
        private final static Properties INSTANCE = computeWithClassLoaderLock();


        private static Properties computeWithClassLoaderLock(){
            return new Properties();
        }
    }
}

The lazy class uses volatile members to synchronize access to internal objects, while singleton mode implementation is rare (I personally prefer to use it with an internal helper class with a static end member) I assume that the second pattern has better performance because every call to the getorcompute method of a lazy object involves reading from main memory (due to volatile members), while singleton is composed of L1 and amp; The class loader cached in is loaded once L2 memory cache I use jmh benchmark to test the assumption that I use Intel (R) core (TM) i5-3470 CPU @ 3.20ghz on CentOS 6 The benchmark can be downloaded from my git Repository: https://github.com/maximkir/LazyObjectVsSingletonPerformance

The following is the result table:

Benchmark                                   Mode      Cnt   score   Error  Units
LazyVsSingletonPerformance.testLazy       sample  1101716  33.793 ± 0.148  ns/op
LazyVsSingletonPerformance.testSingleton  sample   622603  33.993 ± 0.179  ns/op

The results show that there is no difference between the two options. I don't understand why I hope the second model performs better Any ideas? Inline? Compiler optimization? Wrong benchmark?

Reference code:

@State(Scope.Thread)
public class LazyVsSingletonPerformance {

    Blackhole bh = new Blackhole();
    Lazy<Properties> lazyProperties = new Lazy<>();

    public static void main(String... args) throws Exception{
        Options opts = new OptionsBuilder()
                .include(LazyVsSingletonPerformance.class.getSimpleName())
                .warmupIterations(3)
                .forks(2)
                .measurementIterations(3)
                .mode(Mode.SampleTime)
                .measurementTime(TimeValue.seconds(10))
                .timeUnit(TimeUnit.NANOSECONDS)
                .build();

        new Runner(opts).run();
    }


    @Benchmark
    public void testLazy(){
        bh.consume(lazyProperties.getOrCompute(() -> new Properties()));
    }


    @Benchmark
    public void testSingleton(){
        bh.consume(PropertiesSingleton.getProperties());
    }

Solution

I'm not a concurrency expert, but it seems that your lazy initialization program is incorrect In benchmarking, you use scope Thread status But this means that each thread has its own lazy, so there is no real concurrency

I use lazy to write my own benchmark (based on Apache Commons LazyInitializer), eager and static inner classes

Package org sample;

import java.util.Properties;

public class Eager {
    private final Properties value = new Properties();

    public Properties get(){
        return value;
    }
}

Lazy package org sample;

import org.apache.commons.lang3.concurrent.ConcurrentException;
import org.apache.commons.lang3.concurrent.LazyInitializer;

import java.util.Properties;

public class Lazy extends LazyInitializer<Properties> {
    @Override
    protected Properties initialize() throws ConcurrentException {
        return new Properties();
    }
}

Properties singleton is the same as yours

Benchmark package org sample;

import org.apache.commons.lang3.concurrent.ConcurrentException;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;

import java.util.Properties;

@State(Scope.Benchmark)
public class MyBenchmark {
    private Lazy lazyProperties = new Lazy();
    private Eager eagerProperties = new Eager();

    @Benchmark
    public Properties testEager(){
        return eagerProperties.get();
    }

    @Benchmark
    public Properties testLazy() throws ConcurrentException {
        return lazyProperties.get();
    }    

    @Benchmark
    public Properties testSingleton(){
        return PropertiesSingleton.getProperties();
    }
}

result

Benchmark                   Mode  Cnt         score         Error  Units
MyBenchmark.testEager      thrpt   20  90980753,160 ± 4075331,777  ops/s
MyBenchmark.testLazy       thrpt   20  83876826,598 ± 3445507,139  ops/s
MyBenchmark.testSingleton  thrpt   20  82260350,608 ± 3524764,266  ops/s
The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>