Skip to content

Commit

Permalink
Cache constructor MethodHandles in factories
Browse files Browse the repository at this point in the history
Update LocalCacheFactory.java
  • Loading branch information
yuzawa-san committed Apr 12, 2023
1 parent 9bb8228 commit 557aa49
Show file tree
Hide file tree
Showing 4 changed files with 66 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@
*/
package com.github.benmanes.caffeine;

import java.lang.invoke.LambdaMetafactory;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Constructor;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.infra.Blackhole;

/**
* @author [email protected] (Ben Manes)
Expand All @@ -34,35 +35,46 @@ public class FactoryBenchmark {
private final MethodHandleFactory methodHandleFactory = new MethodHandleFactory();

@Benchmark
public Alpha direct() {
return new Alpha();
public void direct(Blackhole blackhole) {
blackhole.consume(new Alpha());
}

@Benchmark
public Alpha methodHandle_invoke() {
return methodHandleFactory.invoke();
public void methodHandle_invoke(Blackhole blackhole) {
blackhole.consume(methodHandleFactory.invoke());
}

@Benchmark
public Alpha methodHandle_invokeExact() {
return methodHandleFactory.invokeExact();
public void methodHandle_invokeExact(Blackhole blackhole) {
blackhole.consume(methodHandleFactory.invokeExact());
}

@Benchmark
public Alpha reflection() {
return reflectionFactory.newInstance();
public void methodHandle_lambda(Blackhole blackhole) {
blackhole.consume(methodHandleFactory.lambda());
}

@Benchmark
public void reflection(Blackhole blackhole) {
blackhole.consume(reflectionFactory.newInstance());
}

static final class MethodHandleFactory {
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static final MethodType METHOD_TYPE = MethodType.methodType(void.class);

private final MethodHandle methodHandle;
private final AlphaConstructor lambda;

MethodHandleFactory() {
try {
methodHandle = LOOKUP.findConstructor(Alpha.class, METHOD_TYPE);
} catch (NoSuchMethodException | IllegalAccessException e) {
lambda =
(AlphaConstructor) LambdaMetafactory
.metafactory(LOOKUP, "construct", MethodType.methodType(AlphaConstructor.class),
methodHandle.type(), methodHandle, methodHandle.type())
.getTarget().invokeExact();
} catch (Throwable e) {
throw new RuntimeException(e);
}
}
Expand All @@ -82,6 +94,10 @@ Alpha invokeExact() {
throw new RuntimeException(e);
}
}

Alpha lambda() {
return lambda.construct();
}
}

static final class ReflectionFactory {
Expand All @@ -107,4 +123,8 @@ Alpha newInstance() {
static final class Alpha {
public Alpha() {}
}

private interface AlphaConstructor {
Alpha construct();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import org.checkerframework.checker.nullness.qual.Nullable;

Expand All @@ -30,6 +32,7 @@ final class LocalCacheFactory {
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
private static final MethodType FACTORY = MethodType.methodType(
void.class, Caffeine.class, AsyncCacheLoader.class, boolean.class);
private static final Map<String, MethodHandle> CONSTRUCTORS = new ConcurrentHashMap<>();

private LocalCacheFactory() {}

Expand All @@ -41,7 +44,7 @@ static <K, V> BoundedLocalCache<K, V> newBoundedLocalCache(Caffeine<K, V> builde
}

static String getClassName(Caffeine<?, ?> builder) {
var className = new StringBuilder(LocalCacheFactory.class.getPackageName()).append('.');
var className = new StringBuilder();
if (builder.isStrongKeys()) {
className.append('S');
} else {
Expand Down Expand Up @@ -80,10 +83,21 @@ static String getClassName(Caffeine<?, ?> builder) {

static <K, V> BoundedLocalCache<K, V> loadFactory(Caffeine<K, V> builder,
@Nullable AsyncCacheLoader<? super K, V> cacheLoader, boolean async, String className) {
var constructor = CONSTRUCTORS.get(className);
if (constructor == null) {
constructor = CONSTRUCTORS.computeIfAbsent(className, LocalCacheFactory::newConstructor);
}
try {
return (BoundedLocalCache<K, V>) constructor.invoke(builder, cacheLoader, async);
} catch (Throwable t) {
throw new IllegalStateException(className, t);
}
}

static MethodHandle newConstructor(String className) {
try {
Class<?> clazz = Class.forName(className);
MethodHandle handle = LOOKUP.findConstructor(clazz, FACTORY);
return (BoundedLocalCache<K, V>) handle.invoke(builder, cacheLoader, async);
Class<?> clazz = Class.forName(LocalCacheFactory.class.getPackageName() + "." + className);
return LOOKUP.findConstructor(clazz, FACTORY);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable t) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.ref.ReferenceQueue;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

import com.github.benmanes.caffeine.cache.References.LookupKeyReference;
import com.github.benmanes.caffeine.cache.References.WeakKeyReference;
Expand All @@ -31,6 +33,7 @@
interface NodeFactory<K, V> {
MethodType FACTORY = MethodType.methodType(void.class);
MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
Map<String, MethodHandle> CONSTRUCTORS = new ConcurrentHashMap<>();

RetiredStrongKey RETIRED_STRONG_KEY = new RetiredStrongKey();
RetiredWeakKey RETIRED_WEAK_KEY = new RetiredWeakKey();
Expand Down Expand Up @@ -87,7 +90,7 @@ static <K, V> NodeFactory<K, V> newFactory(Caffeine<K, V> builder, boolean isAsy
}

static String getClassName(Caffeine<?, ?> builder, boolean isAsync) {
var className = new StringBuilder(Node.class.getPackageName()).append('.');
var className = new StringBuilder();
if (builder.isStrongKeys()) {
className.append('P');
} else {
Expand Down Expand Up @@ -132,10 +135,21 @@ static String getClassName(Caffeine<?, ?> builder, boolean isAsync) {
}

static <K, V> NodeFactory<K, V> loadFactory(String className) {
var constructor = CONSTRUCTORS.get(className);
if (constructor == null) {
constructor = CONSTRUCTORS.computeIfAbsent(className, NodeFactory::newConstructor);
}
try {
return (NodeFactory<K, V>) constructor.invoke();
} catch (Throwable t) {
throw new IllegalStateException(className, t);
}
}

static MethodHandle newConstructor(String className) {
try {
Class<?> clazz = Class.forName(className);
MethodHandle handle = LOOKUP.findConstructor(clazz, FACTORY);
return (NodeFactory<K, V>) handle.invoke();
Class<?> clazz = Class.forName(Node.class.getPackageName() + "." + className);
return LOOKUP.findConstructor(clazz, FACTORY);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable t) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,6 @@
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.hamcrest.Matchers.nullValue;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
Expand Down

0 comments on commit 557aa49

Please sign in to comment.