Skip to content

Commit

Permalink
PLANNER-706 rename Cache to Memoization + review
Browse files Browse the repository at this point in the history
  • Loading branch information
ge0ffrey committed Feb 7, 2018
1 parent d4602e6 commit 7043794
Show file tree
Hide file tree
Showing 4 changed files with 62 additions and 46 deletions.
@@ -0,0 +1,47 @@
/*
* Copyright 2018 Red Hat, Inc. and/or its affiliates.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.optaplanner.core.impl.domain.common;

import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;

/**
* A thread-safe memoization that caches a calculation.
*
* @param <K> the parameter of the calculation
* @param <V> the result of the calculation
* */
public final class ConcurrentMemoization<K, V> extends ConcurrentHashMap<K, V> {

/**
* An overridden implementation that heavily favors read access over write access speed.
* This is thread-safe.
*
* {@inheritDoc}
*/
@Override
public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction) {
// This might look like a Double Checked Idiom (which is broken), but it is not
// because value is not a global variable
V value = get(key);
if (value != null) {
return value;
}
return super.computeIfAbsent(key, mappingFunction);
}

}
Expand Up @@ -42,16 +42,16 @@
import java.util.SortedSet; import java.util.SortedSet;
import java.util.TreeMap; import java.util.TreeMap;
import java.util.TreeSet; import java.util.TreeSet;

import java.util.concurrent.ConcurrentMap; import java.util.concurrent.ConcurrentMap;

import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.optaplanner.core.api.domain.solution.PlanningSolution; import org.optaplanner.core.api.domain.solution.PlanningSolution;
import org.optaplanner.core.api.domain.solution.cloner.DeepPlanningClone; import org.optaplanner.core.api.domain.solution.cloner.DeepPlanningClone;
import org.optaplanner.core.api.domain.solution.cloner.SolutionCloner; import org.optaplanner.core.api.domain.solution.cloner.SolutionCloner;
import org.optaplanner.core.impl.domain.common.ConcurrentMemoization;
import org.optaplanner.core.impl.domain.common.ReflectionHelper; import org.optaplanner.core.impl.domain.common.ReflectionHelper;
import org.optaplanner.core.impl.domain.common.accessor.MemberAccessor; import org.optaplanner.core.impl.domain.common.accessor.MemberAccessor;
import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor; import org.optaplanner.core.impl.domain.solution.descriptor.SolutionDescriptor;
import org.optaplanner.core.impl.domain.solution.util.ConcurrentCache;


/** /**
* @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation * @param <Solution_> the solution type, the class with the {@link PlanningSolution} annotation
Expand All @@ -60,10 +60,10 @@ public class FieldAccessingSolutionCloner<Solution_> implements SolutionCloner<S


protected final SolutionDescriptor<Solution_> solutionDescriptor; protected final SolutionDescriptor<Solution_> solutionDescriptor;


protected final ConcurrentMap<Class<?>, Constructor<?>> constructorCache = new ConcurrentCache<>(); protected final ConcurrentMap<Class<?>, Constructor<?>> constructorMemoization = new ConcurrentMemoization<>();
protected final ConcurrentMap<Class<?>, List<Field>> fieldListCache = new ConcurrentCache<>(); protected final ConcurrentMap<Class<?>, List<Field>> fieldListMemoization = new ConcurrentMemoization<>();
protected final ConcurrentMap<Pair<Field, Class<?>>, Boolean> deepCloneDecisionFieldCache = new ConcurrentCache<>(); protected final ConcurrentMap<Pair<Field, Class<?>>, Boolean> fieldDeepClonedMemoization = new ConcurrentMemoization<>();
protected final ConcurrentMap<Class<?>, Boolean> deepCloneDecisionActualValueClassCache = new ConcurrentCache<>(); protected final ConcurrentMap<Class<?>, Boolean> actualValueClassDeepClonedMemoization = new ConcurrentMemoization<>();


public FieldAccessingSolutionCloner(SolutionDescriptor<Solution_> solutionDescriptor) { public FieldAccessingSolutionCloner(SolutionDescriptor<Solution_> solutionDescriptor) {
this.solutionDescriptor = solutionDescriptor; this.solutionDescriptor = solutionDescriptor;
Expand All @@ -86,7 +86,7 @@ public Solution_ cloneSolution(Solution_ originalSolution) {
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
protected <C> Constructor<C> retrieveCachedConstructor(Class<C> clazz) { protected <C> Constructor<C> retrieveCachedConstructor(Class<C> clazz) {
return (Constructor<C>) constructorCache.computeIfAbsent(clazz, key -> { return (Constructor<C>) constructorMemoization.computeIfAbsent(clazz, key -> {
Constructor<C> constructor; Constructor<C> constructor;
try { try {
constructor = clazz.getDeclaredConstructor(); constructor = clazz.getDeclaredConstructor();
Expand All @@ -106,7 +106,7 @@ protected <C> Constructor<C> retrieveCachedConstructor(Class<C> clazz) {
* @return never null * @return never null
*/ */
protected <C> List<Field> retrieveCachedFields(Class<C> clazz) { protected <C> List<Field> retrieveCachedFields(Class<C> clazz) {
return fieldListCache.computeIfAbsent(clazz, key -> { return fieldListMemoization.computeIfAbsent(clazz, key -> {
Field[] fields = clazz.getDeclaredFields(); Field[] fields = clazz.getDeclaredFields();
List<Field> fieldList = new ArrayList<>(fields.length); List<Field> fieldList = new ArrayList<>(fields.length);
for (Field field : fields) { for (Field field : fields) {
Expand All @@ -128,7 +128,7 @@ protected <C> List<Field> retrieveCachedFields(Class<C> clazz) {
*/ */
protected boolean retrieveDeepCloneDecision(Field field, Class<?> fieldInstanceClass, Class<?> actualValueClass) { protected boolean retrieveDeepCloneDecision(Field field, Class<?> fieldInstanceClass, Class<?> actualValueClass) {
Pair<Field, Class<?>> pair = Pair.of(field, fieldInstanceClass); Pair<Field, Class<?>> pair = Pair.of(field, fieldInstanceClass);
Boolean deepCloneDecision = deepCloneDecisionFieldCache.computeIfAbsent(pair, Boolean deepCloneDecision = fieldDeepClonedMemoization.computeIfAbsent(pair,
key -> isFieldDeepCloned(field, fieldInstanceClass)); key -> isFieldDeepCloned(field, fieldInstanceClass));
return deepCloneDecision || retrieveDeepCloneDecisionForActualValueClass(actualValueClass); return deepCloneDecision || retrieveDeepCloneDecisionForActualValueClass(actualValueClass);
} }
Expand Down Expand Up @@ -209,7 +209,7 @@ private boolean isFieldADeepCloneProperty(Field field, Class<?> fieldInstanceCla
* @return never null * @return never null
*/ */
protected boolean retrieveDeepCloneDecisionForActualValueClass(Class<?> actualValueClass) { protected boolean retrieveDeepCloneDecisionForActualValueClass(Class<?> actualValueClass) {
return deepCloneDecisionActualValueClassCache.computeIfAbsent(actualValueClass, return actualValueClassDeepClonedMemoization.computeIfAbsent(actualValueClass,
key -> isClassDeepCloned(actualValueClass)); key -> isClassDeepCloned(actualValueClass));
} }


Expand Down
Expand Up @@ -34,9 +34,9 @@
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.concurrent.ConcurrentMap;


import com.google.common.collect.Iterators; import com.google.common.collect.Iterators;
import java.util.concurrent.ConcurrentMap;
import org.apache.commons.lang3.tuple.MutablePair; import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.lang3.tuple.Pair;
import org.optaplanner.core.api.domain.autodiscover.AutoDiscoverMemberType; import org.optaplanner.core.api.domain.autodiscover.AutoDiscoverMemberType;
Expand Down Expand Up @@ -65,6 +65,7 @@
import org.optaplanner.core.api.score.buildin.simpledouble.SimpleDoubleScore; import org.optaplanner.core.api.score.buildin.simpledouble.SimpleDoubleScore;
import org.optaplanner.core.api.score.buildin.simplelong.SimpleLongScore; import org.optaplanner.core.api.score.buildin.simplelong.SimpleLongScore;
import org.optaplanner.core.config.util.ConfigUtils; import org.optaplanner.core.config.util.ConfigUtils;
import org.optaplanner.core.impl.domain.common.ConcurrentMemoization;
import org.optaplanner.core.impl.domain.common.ReflectionHelper; import org.optaplanner.core.impl.domain.common.ReflectionHelper;
import org.optaplanner.core.impl.domain.common.accessor.BeanPropertyMemberAccessor; import org.optaplanner.core.impl.domain.common.accessor.BeanPropertyMemberAccessor;
import org.optaplanner.core.impl.domain.common.accessor.FieldMemberAccessor; import org.optaplanner.core.impl.domain.common.accessor.FieldMemberAccessor;
Expand All @@ -74,7 +75,6 @@
import org.optaplanner.core.impl.domain.policy.DescriptorPolicy; import org.optaplanner.core.impl.domain.policy.DescriptorPolicy;
import org.optaplanner.core.impl.domain.solution.AbstractSolution; import org.optaplanner.core.impl.domain.solution.AbstractSolution;
import org.optaplanner.core.impl.domain.solution.cloner.FieldAccessingSolutionCloner; import org.optaplanner.core.impl.domain.solution.cloner.FieldAccessingSolutionCloner;
import org.optaplanner.core.impl.domain.solution.util.ConcurrentCache;
import org.optaplanner.core.impl.domain.variable.descriptor.GenuineVariableDescriptor; import org.optaplanner.core.impl.domain.variable.descriptor.GenuineVariableDescriptor;
import org.optaplanner.core.impl.domain.variable.descriptor.ShadowVariableDescriptor; import org.optaplanner.core.impl.domain.variable.descriptor.ShadowVariableDescriptor;
import org.optaplanner.core.impl.domain.variable.descriptor.VariableDescriptor; import org.optaplanner.core.impl.domain.variable.descriptor.VariableDescriptor;
Expand Down Expand Up @@ -161,7 +161,7 @@ private static List<Class<?>> sortEntityClassList(List<Class<?>> entityClassList
private final Map<Class<?>, EntityDescriptor<Solution_>> entityDescriptorMap; private final Map<Class<?>, EntityDescriptor<Solution_>> entityDescriptorMap;
private final List<Class<?>> reversedEntityClassList; private final List<Class<?>> reversedEntityClassList;


private final ConcurrentMap<Class<?>, EntityDescriptor<Solution_>> lowestEntityDescriptorCache = new ConcurrentCache<>(); private final ConcurrentMap<Class<?>, EntityDescriptor<Solution_>> lowestEntityDescriptorMemoization = new ConcurrentMemoization<>();
private LookUpStrategyResolver lookUpStrategyResolver = null; private LookUpStrategyResolver lookUpStrategyResolver = null;


// ************************************************************************ // ************************************************************************
Expand Down Expand Up @@ -189,7 +189,7 @@ public void addEntityDescriptor(EntityDescriptor<Solution_> entityDescriptor) {
} }
entityDescriptorMap.put(entityClass, entityDescriptor); entityDescriptorMap.put(entityClass, entityDescriptor);
reversedEntityClassList.add(0, entityClass); reversedEntityClassList.add(0, entityClass);
lowestEntityDescriptorCache.put(entityClass, entityDescriptor); lowestEntityDescriptorMemoization.put(entityClass, entityDescriptor);
} }


public void processAnnotations(DescriptorPolicy descriptorPolicy, ScoreDefinition deprecatedScoreDefinition, List<Class<?>> entityClassList) { public void processAnnotations(DescriptorPolicy descriptorPolicy, ScoreDefinition deprecatedScoreDefinition, List<Class<?>> entityClassList) {
Expand Down Expand Up @@ -822,7 +822,7 @@ public EntityDescriptor<Solution_> findEntityDescriptorOrFail(Class<?> entitySub
} }


public EntityDescriptor<Solution_> findEntityDescriptor(Class<?> entitySubclass) { public EntityDescriptor<Solution_> findEntityDescriptor(Class<?> entitySubclass) {
return lowestEntityDescriptorCache.computeIfAbsent(entitySubclass, key -> { return lowestEntityDescriptorMemoization.computeIfAbsent(entitySubclass, key -> {
// Reverse order to find the nearest ancestor // Reverse order to find the nearest ancestor
for (Class<?> entityClass : reversedEntityClassList) { for (Class<?> entityClass : reversedEntityClassList) {
if (entityClass.isAssignableFrom(entitySubclass)) { if (entityClass.isAssignableFrom(entitySubclass)) {
Expand Down

This file was deleted.

0 comments on commit 7043794

Please sign in to comment.