Skip to content

Commit

Permalink
Improve performance unit test messaging and increase padding factors …
Browse files Browse the repository at this point in the history
…to help avoid intermittent fails
  • Loading branch information
jonathanaustin committed Oct 4, 2024
1 parent fef1f55 commit 0f32f48
Show file tree
Hide file tree
Showing 7 changed files with 78 additions and 89 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
* The latest version of the shade plugin used to create the examples lde dependency jar has changed how it handles
the reduced pom files. The reduced pom removes all the dependencies which impacts the use of the main attached jar so
its creation has been disabled.
* Improve performance unit test messaging and increase padding factors to help avoid intermittent fails.

## 1.5.35

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,6 @@
import com.github.bordertech.wcomponents.util.Duplet;
import com.github.bordertech.wcomponents.util.ReflectionUtil;
import com.github.bordertech.wcomponents.util.SystemException;
import org.junit.Assert;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;

import java.io.Serializable;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.Method;
Expand All @@ -19,6 +12,11 @@
import java.util.Map;
import java.util.function.BiConsumer;
import java.util.function.Function;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.After;
import org.junit.Assert;

/**
* This class includes features useful for the testing of WComponents.
Expand All @@ -35,6 +33,12 @@ public abstract class AbstractWComponentTestCase {
*/
private static final Log LOG = LogFactory.getLog(AbstractWComponentTestCase.class);

/**
* The number of repetitions to use for testing serialization time. This should be set to be greater than the
* minimum number of invocations required to trigger JIT compilation.
*/
public static final int NUM_REPETITIONS = 2000;

/**
* Creates a UI Context.
*
Expand Down Expand Up @@ -63,7 +67,7 @@ public void resetContext() {
UIContextHolder.reset();
}

/**
/**
* This method will test that the getter/setter methods on a component are returning the correct values in its (i)
* initial state (ii) default state and (iii) user context.
* <p>
Expand All @@ -81,11 +85,11 @@ public void resetContext() {
* @param userContextValue the value to be used with a user context
*/
protected <C extends WComponent, T> void assertAccessorsCorrect(
final C component,
final Function<C, T> getter,
final BiConsumer<C, T> setter,
final T initValue,
final T defaultValue, final T userContextValue) {
final C component,
final Function<C, T> getter,
final BiConsumer<C, T> setter,
final T initValue,
final T defaultValue, final T userContextValue) {
try {
// Check initial value
checkValue("", "Initial value.", initValue, getter.apply(component));
Expand Down Expand Up @@ -142,13 +146,13 @@ protected <C extends WComponent, T> void assertAccessorsCorrect(
* @param setterArgs the value of the setter's second argument
*/
protected <C extends WComponent, T, U> void assertAccessorsCorrect(
final C component,
final Function<C, T> getter,
final TriConsumer<C, T, U> setter,
final T initValue,
final T defaultValue,
final T userContextValue,
final U setterArgs) {
final C component,
final Function<C, T> getter,
final TriConsumer<C, T, U> setter,
final T initValue,
final T defaultValue,
final T userContextValue,
final U setterArgs) {
try {
// Check initial value
checkValue("", "Initial value.", initValue, getter.apply(component));
Expand Down Expand Up @@ -179,7 +183,7 @@ protected <C extends WComponent, T, U> void assertAccessorsCorrect(

// Check default value still correct
checkValue("", "Reset.", defaultValue, getter.apply(component));

} finally {
resetContext();
}
Expand Down Expand Up @@ -207,7 +211,7 @@ private void assertComponentModelUsesDefaultOnCreation(AbstractWComponent wCompo
* @param setterArgs array matching the variable argument type
*/
private void assertComponentModelUsesDefaultOnSameValue(AbstractWComponent wComponent, String method,
Object userContextValue, Object setterArgs[]) {
Object userContextValue, Object setterArgs[]) {
wComponent.setLocked(false);
// Set default model
invokeSetMethod(wComponent, method, userContextValue, setterArgs);
Expand All @@ -234,12 +238,12 @@ private void assertComponentModelUsesDefaultOnSameValue(AbstractWComponent wComp
*
* @param wComponent the component the model is being created from
* @param method the method used to change the model
* @param userContextValue the value to be used within the user context. Must be different to the value stored in the
* default model for this test.
* @param userContextValue the value to be used within the user context. Must be different to the value stored in
* the default model for this test.
* @param setterArgs array matching the variable argument type
*/
private void assertComponentModelDoesNotUseDefaultOnDifferentValue(AbstractWComponent wComponent, String method,
Object userContextValue, Object setterArgs[]) {
Object userContextValue, Object setterArgs[]) {
// Create a default model using whatever values are set for the wComponent
wComponent.setLocked(true);
ComponentModel shared = wComponent.getDefaultModel();
Expand All @@ -263,7 +267,7 @@ private void assertComponentModelDoesNotUseDefaultOnDifferentValue(AbstractWComp
* @param setterArgs array matching the variable argument type
*/
private void assertDuplicateUserModelNotCreatedOnSameValue(AbstractWComponent wComponent, String method,
Object userContextValue, Object setterArgs[]) {
Object userContextValue, Object setterArgs[]) {
wComponent.setLocked(true);

// Set the UI context and set a value to the current model
Expand Down Expand Up @@ -300,14 +304,15 @@ protected void assertNoDuplicateComponentModels(AbstractWComponent wComponent, S
}

/**
* Overloading method for {@link AbstractWComponentTestCase#assertNoDuplicateComponentModels(AbstractWComponent, String, Object, Object[])}.
* Overloading method for
* {@link AbstractWComponentTestCase#assertNoDuplicateComponentModels(AbstractWComponent, String, Object, Object[])}.
*
* @param wComponent the component the model is being created from
* @param method the method used to change the model
* @param userContextValue the value to be used within the user context
*/
protected void assertNoDuplicateComponentModels(AbstractWComponent wComponent, String method,
Object userContextValue) {
Object userContextValue) {
assertNoDuplicateComponentModels(wComponent, method, userContextValue, null);
}

Expand Down Expand Up @@ -375,6 +380,17 @@ public void run() {
return result[0];
}

/**
* Asserts that <code>first</code> is less than <code>second</code>.
*
* @param text the assertion text.
* @param first the first parameter to check.
* @param second the second parameter to check.
*/
protected void assertLessThan(final String text, final long first, final long second) {
Assert.assertTrue(text + ": " + first + " < " + second, first < second);
}

/**
* Modifies the component's flags. This is necessary for testing as some of the setter methods are intentionally not
* visible in the public API.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import java.util.List;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.junit.Assert;
import org.junit.Test;
import org.junit.experimental.categories.Category;

Expand All @@ -33,12 +32,6 @@
@Category(PerformanceTests.class)
public class SerializationPerformance_Test extends AbstractWComponentTestCase {

/**
* The number of repetitions to use for testing serialization time. This should be set to be greater than the
* minimum number of invocations required to trigger JIT compilation.
*/
private static final int NUM_REPETITIONS = 2000;

/**
* The logger instance for this class.
*/
Expand All @@ -59,8 +52,7 @@ public void testSerializationSize() throws Exception {

LOG.info("Optimised size - clean session: " + registeredSize);
LOG.info("default size - clean session: " + nonRegisteredSize);
assertLessThan("Optimised size should be smaller than default", registeredSize,
nonRegisteredSize);
assertLessThan("Optimised size should be smaller than default", registeredSize, nonRegisteredSize);

// Test used session - 50% components with models
createUserModels(nonRegistered, nonRegisteredContext, 50);
Expand All @@ -71,8 +63,7 @@ public void testSerializationSize() throws Exception {

LOG.info("Optimised size - 50% models: " + registeredSize);
LOG.info("default size - 50% models: " + nonRegisteredSize);
assertLessThan("Optimised size should be smaller than default", registeredSize,
nonRegisteredSize);
assertLessThan("Optimised size should be smaller than default", registeredSize, nonRegisteredSize);

// Test used session - 100% components with models
createUserModels(nonRegistered, nonRegisteredContext, 100);
Expand All @@ -83,8 +74,7 @@ public void testSerializationSize() throws Exception {

LOG.info("Optimised size - 100% models: " + registeredSize);
LOG.info("default size - 100% models: " + nonRegisteredSize);
assertLessThan("Optimised size should be smaller than default", registeredSize,
nonRegisteredSize);
assertLessThan("Optimised size should be smaller than default", registeredSize, nonRegisteredSize);
}

@Test
Expand Down Expand Up @@ -117,14 +107,12 @@ public void testSerializationTime() throws Exception {
UIContext registeredContext = createUIContext();

// Test clean session
long nonRegisteredTime = serializeSession(nonRegistered, nonRegisteredContext,
NUM_REPETITIONS);
long nonRegisteredTime = serializeSession(nonRegistered, nonRegisteredContext, NUM_REPETITIONS);
long registeredTime = serializeSession(registered, registeredContext, NUM_REPETITIONS);

LOG.info("Optimised time - clean session: " + (registeredTime / 1000000.0) + "ms");
LOG.info("default time - clean session: " + (nonRegisteredTime / 1000000.0) + "ms");
assertLessThan("Optimised time should be less than default time", registeredTime,
nonRegisteredTime);
assertLessThan("Optimised time should be less than default time", registeredTime, nonRegisteredTime);

// Test used session - 50% components with models
createUserModels(nonRegistered, nonRegisteredContext, 50);
Expand All @@ -135,8 +123,7 @@ public void testSerializationTime() throws Exception {

LOG.info("Optimised time - 50% models: " + (registeredTime / 1000000.0) + "ms");
LOG.info("default time - 50% models: " + (nonRegisteredTime / 1000000.0) + "ms");
assertLessThan("Optimised time should be less than default time", registeredTime,
nonRegisteredTime);
assertLessThan("Optimised time should be less than default time", registeredTime, nonRegisteredTime);

// Test used session - 100% components with models
createUserModels(nonRegistered, nonRegisteredContext, 100);
Expand All @@ -147,8 +134,7 @@ public void testSerializationTime() throws Exception {

LOG.info("Optimised time - 100% models: " + (registeredTime / 1000000.0) + "ms");
LOG.info("default time - 100% models: " + (nonRegisteredTime / 1000000.0) + "ms");
assertLessThan("Optimised time should be less than default time", registeredTime,
nonRegisteredTime);
assertLessThan("Optimised time should be less than default time", registeredTime, nonRegisteredTime);
}

@Test
Expand All @@ -168,7 +154,9 @@ public void testSerializationTimeScaling() throws Exception {

LOG.info("Optimised time - 50% models 1x: " + (registered1Time / 1000000.0) + "ms");
LOG.info("Optimised time - 50% models 10x: " + (registered10Time / 1000000.0) + "ms");
assertLessThan("Time scaling should be O(n)", registered10Time, registered1Time * 10);

// Should be a factor of x10 for O(n) but use x12 as a padding factor to avoid intermittent fails
assertLessThan("Time scaling should be O(n)", registered10Time, registered1Time * 12);
}

/**
Expand Down Expand Up @@ -299,17 +287,6 @@ private void findWComponents(final WComponent comp, final List<WComponent> resul
}
}

/**
* Asserts that <code>first</code> is less than <code>second</code>.
*
* @param text the assertion text.
* @param first the first parameter to check.
* @param second the second parameter to check.
*/
private static void assertLessThan(final String text, final long first, final long second) {
Assert.assertTrue(text + ": " + first + " < " + second, first < second);
}

/**
* AllComponents instantiated with 10 repetitions. This needs to be created as a subclass as the UIRegistry uses the
* class name.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,16 +70,15 @@ public void testOtherImplementationCorrectness() throws Exception {

@Test
public void testRequestHandlingPerformance() throws Exception {
final int numLoops = 2000;
final int numLoops = NUM_REPETITIONS;

long simpleTime = timeOtherServlet(numLoops) / numLoops;
long wcomponentTime = timeWComponentProcessing(numLoops) / numLoops;

LOG.info("Simple request handling time: " + (simpleTime / 1000000.0) + "ms");
LOG.info("WComponent request handling time: " + (wcomponentTime / 1000000.0) + "ms");

Assert.assertTrue("WComponent request handling time should not exceed 10x simple time",
wcomponentTime < simpleTime * 10);
assertLessThan("WComponent request handling time should not exceed 12x simple time", wcomponentTime, simpleTime * 12);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,12 +37,6 @@
@Category(PerformanceTests.class)
public class WebXmlRenderingPerformance_Test extends AbstractWComponentTestCase {

/**
* The number of repetitions to use for testing serialization time. This should be set to be greater than the
* minimum number of invocations required to trigger JIT compilation.
*/
private static final int NUM_REPETITIONS = 2000;

/**
* The logger instance for this class.
*/
Expand All @@ -57,9 +51,9 @@ public void testRenderingPerformance() throws Exception {
// Render and store the XML to compare against
setActiveContext(uic);
StringWriter tempStringWriter = new StringWriter();
PrintWriter tempPrintWriter = new PrintWriter(tempStringWriter);
component.paint(new WebXmlRenderContext(tempPrintWriter));
tempPrintWriter.close();
try (PrintWriter tempPrintWriter = new PrintWriter(tempStringWriter)) {
component.paint(new WebXmlRenderContext(tempPrintWriter));
}
resetContext();

// Run the test writing raw bytes to a writer - no computation necessary
Expand Down Expand Up @@ -111,8 +105,8 @@ public void run() {

LOG.info("Raw write time: " + (rawTime / 1000000.0) + "ms");
LOG.info("WComponent render time: " + (renderTime / 1000000.0) + "ms");
Assert.assertTrue("WComponent render time should not exceed 5x raw write time",
renderTime < rawTime * 5);

assertLessThan("WComponent render time should not exceed 6x raw write time", renderTime, rawTime * 6);
}

@Test
Expand Down Expand Up @@ -162,7 +156,10 @@ public void run() {

LOG.info("Render 1x time: " + (renderTime1 / 1000000.0) + "ms");
LOG.info("Render 10x time: " + (renderTime10 / 1000000.0) + "ms");
Assert.assertTrue("Render time scaling should be O(n)", renderTime10 < renderTime1 * 12);

// Should be a factor of x10 for O(n) but use x12 as a padding factor to avoid intermittent fails
assertLessThan("Render time scaling should be O(n)", renderTime10, renderTime1 * 12);

}

@Test
Expand Down Expand Up @@ -215,7 +212,9 @@ public void run() {

LOG.info("Render Component time: " + (renderTime1 / 1000000.0) + "ms");
LOG.info("Render Component/Chain time: " + (renderTimeChain / 1000000.0) + "ms");
Assert.assertTrue("Render with Chain time scaling should not be more than 3x.", renderTimeChain < renderTime1 * 3);

assertLessThan("Render with Chain time scaling should not be more than 4x.", renderTimeChain, renderTime1 * 4);

}

/**
Expand Down
Loading

0 comments on commit 0f32f48

Please sign in to comment.