Skip to content

Commit

Permalink
Performance fix for create new value in prism container (+test)
Browse files Browse the repository at this point in the history
  • Loading branch information
semancik committed Feb 16, 2016
1 parent 7e04157 commit 6db98fe
Show file tree
Hide file tree
Showing 6 changed files with 218 additions and 8 deletions.
Expand Up @@ -370,10 +370,14 @@ public boolean addAll(Collection<V> newValues) throws SchemaException {
}

public boolean add(V newValue) throws SchemaException {
return add(newValue, true);
}

public boolean add(V newValue, boolean checkUniqueness) throws SchemaException {
newValue.setParent(this);
if (containsEquivalentValue(newValue)) {
if (checkUniqueness && containsEquivalentValue(newValue)) {
return false;
}
}
if (getDefinition() != null) {
newValue.applyDefinition(getDefinition(), false);
}
Expand Down
Expand Up @@ -163,7 +163,7 @@ public void setValue(PrismContainerValue<C> value) throws SchemaException {
}

@Override
public boolean add(PrismContainerValue newValue) throws SchemaException {
public boolean add(PrismContainerValue newValue, boolean checkUniqueness) throws SchemaException {
// when a context-less item is added to a contextful container, it is automatically adopted
if (newValue.getPrismContext() == null && this.prismContext != null) {
prismContext.adopt(newValue);
Expand All @@ -175,7 +175,7 @@ public boolean add(PrismContainerValue newValue) throws SchemaException {
}
}
}
return super.add(newValue);
return super.add(newValue, checkUniqueness);
}

@Override
Expand Down Expand Up @@ -237,7 +237,11 @@ public void add(Item<?,?> item) throws SchemaException {
public PrismContainerValue<C> createNewValue() {
PrismContainerValue<C> pValue = new PrismContainerValue<C>(prismContext);
try {
add(pValue);
// No need to check uniqueness, we know that this value is new and therefore
// it will change anyway and therefore the check is pointless.
// However, the check is expensive (especially if there are many values).
// So we are happy to avoid it.
add(pValue, false);
} catch (SchemaException e) {
// This should not happen
throw new SystemException("Internal Error: "+e.getMessage(),e);
Expand Down
Expand Up @@ -335,18 +335,22 @@ public Collection<QName> getPropertyNames() {
return names;
}

public <IV extends PrismValue,ID extends ItemDefinition> boolean add(Item<IV,ID> item) throws SchemaException {
return add(item, true);
}

/**
* Adds an item to a property container.
*
* @param item item to add.
* @throws SchemaException
* @throws IllegalArgumentException an attempt to add value that already exists
*/
public <IV extends PrismValue,ID extends ItemDefinition> boolean add(Item<IV,ID> item) throws SchemaException {
public <IV extends PrismValue,ID extends ItemDefinition> boolean add(Item<IV,ID> item, boolean checkUniquness) throws SchemaException {
if (item.getElementName() == null) {
throw new IllegalArgumentException("Cannot add item without a name to value of container "+getParent());
}
if (findItem(item.getElementName(), Item.class) != null) {
if (checkUniquness && findItem(item.getElementName(), Item.class) != null) {
throw new IllegalArgumentException("Item " + item.getElementName() + " is already present in " + this.getClass().getSimpleName());
}
item.setParent(this);
Expand Down Expand Up @@ -845,7 +849,7 @@ public <X> PrismProperty<X> createProperty(QName propertyName) throws SchemaExce
} else {
property = propertyDefinition.instantiate();
}
add(property);
add(property, false);
return property;
}

Expand Down
@@ -0,0 +1,81 @@
/*
* Copyright (c) 2016 Evolveum
*
* 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 com.evolveum.midpoint.prism;

import org.testng.AssertJUnit;

/**
* @author semancik
*
*/
public class PerfRecorder {

private String name;
private int count = 0;
private Double min = null;
private Double max = null;
private Double sum = 0D;

public PerfRecorder(String name) {
super();
this.name = name;
}

public void record(int index, Double value) {
sum += value;
count ++;
if (min == null || value < min) {
min = value;
}
if (max == null || value > max) {
max = value;
}
}

public int getCount() {
return count;
}

public Double getMin() {
return min;
}

public Double getMax() {
return max;
}

public Double getSum() {
return sum;
}

public double getAverage() {
return sum/count;
}

public void assertAverageBelow(double expected) {
AssertJUnit.assertTrue(name+ ": Expected average below "+expected+" but was "+getAverage(), getAverage() < expected);
}

public void assertMaxBelow(double expected) {
AssertJUnit.assertTrue(name+ ": Expected maximum below "+expected+" but was "+max, max < expected);
}


public String dump() {
return name + ": min / avg / max = "+min+" / "+getAverage()+" / "+max + " (sum="+sum+", count="+count+")";
}

}
@@ -0,0 +1,116 @@
/*
* Copyright (c) 2016 Evolveum
*
* 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 com.evolveum.midpoint.prism;

import static com.evolveum.midpoint.prism.PrismInternalTestUtil.*;
import static org.testng.AssertJUnit.assertNotNull;
import static org.testng.AssertJUnit.assertTrue;
import static org.testng.AssertJUnit.assertEquals;

import java.io.IOException;
import java.util.List;

import javax.xml.namespace.QName;
import javax.xml.transform.dom.DOMSource;
import javax.xml.validation.Schema;
import javax.xml.validation.Validator;

import org.testng.annotations.BeforeSuite;
import org.testng.annotations.Test;
import org.w3c.dom.Document;
import org.xml.sax.SAXException;

import com.evolveum.midpoint.prism.foo.ActivationType;
import com.evolveum.midpoint.prism.foo.AssignmentType;
import com.evolveum.midpoint.prism.foo.UserType;
import com.evolveum.midpoint.prism.util.PrismAsserts;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.PrettyPrinter;
import com.evolveum.midpoint.util.exception.SchemaException;

/**
* @author semancik
*
*/
public class TestPerformance {

private static final int ITERATIONS = 10000;


@BeforeSuite
public void setupDebug() {
PrettyPrinter.setDefaultNamespacePrefix(DEFAULT_NAMESPACE_PREFIX);
}

/**
* Construct object with schema. Starts by instantiating a definition and working downwards.
* All the items in the object should have proper definition.
*/
@Test
public void testPerfContainerNewValue() throws Exception {
final String TEST_NAME = "testPerfContainerNewValue";
PrismInternalTestUtil.displayTestTitle(TEST_NAME);

// GIVEN
PrismContext ctx = constructInitializedPrismContext();
PrismObjectDefinition<UserType> userDefinition = getFooSchema(ctx).findObjectDefinitionByElementName(new QName(NS_FOO,"user"));
PrismObject<UserType> user = userDefinition.instantiate();
PrismContainer<AssignmentType> assignmentContainer = user.findOrCreateContainer(UserType.F_ASSIGNMENT);
PerfRecorder recorderCreateNewValue = new PerfRecorder("createNewValue");
PerfRecorder recorderFindOrCreateProperty = new PerfRecorder("findOrCreateProperty");
PerfRecorder recorderSetRealValue = new PerfRecorder("setRealValue");

// WHEN
for (int i=0; i < ITERATIONS; i++) {
long tsStart = System.nanoTime();

PrismContainerValue<AssignmentType> newValue = assignmentContainer.createNewValue();

long ts1 = System.nanoTime();

PrismProperty<String> descriptionProperty = newValue.findOrCreateProperty(AssignmentType.F_DESCRIPTION);

long ts2 = System.nanoTime();

descriptionProperty.setRealValue("ass "+i);

long tsEnd = System.nanoTime();

recorderCreateNewValue.record(i, ((double)(ts1 - tsStart))/1000000);
recorderFindOrCreateProperty.record(i, ((double)(ts2 - ts1))/1000000);
recorderSetRealValue.record(i, ((double)(tsEnd - ts2))/1000000);

System.out.println("Run "+i+": total "+(((double)(tsEnd - tsStart))/1000000)+"ms");
}

// THEN
System.out.println(recorderCreateNewValue.dump());
System.out.println(recorderFindOrCreateProperty.dump());
System.out.println(recorderCreateNewValue.dump());

// Do not assert maximum here. The maximum values may jump around
// quite wildly (e.g. because of garbage collector runs?)
recorderCreateNewValue.assertAverageBelow(0.05D);

recorderFindOrCreateProperty.assertAverageBelow(0.1D);

recorderCreateNewValue.assertAverageBelow(0.05D);

System.out.println("User:");
System.out.println(user.debugDump());
}

}
1 change: 1 addition & 0 deletions infra/prism/testng-unit.xml
Expand Up @@ -40,6 +40,7 @@
<class name="com.evolveum.midpoint.prism.TestDelta"/>
<class name="com.evolveum.midpoint.prism.TestPath"/>
<class name="com.evolveum.midpoint.prism.TestFind"/>
<class name="com.evolveum.midpoint.prism.TestPerformance"/>
<class name="com.evolveum.midpoint.prism.crypto.TestProtector"/>
<class name="com.evolveum.midpoint.prism.match.TestMatchingRule"/>
<class name="com.evolveum.midpoint.prism.parser.TestDomParser"/>
Expand Down

0 comments on commit 6db98fe

Please sign in to comment.