blob: 02f8837449a43fbfea5540473baa80eaa15a507b [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2009, 2018 SAP AG and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v20.html
*
* Contributors:
* SAP AG - initial API and implementation
******************************************************************************/
package org.eclipse.ocl.examples.impactanalyzer.tests.filterSynthesis;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.eclipse.emf.common.notify.Adapter;
import org.eclipse.emf.common.notify.Notification;
import org.eclipse.emf.common.notify.Notifier;
import org.eclipse.emf.ecore.InternalEObject;
import org.eclipse.emf.ecore.impl.ENotificationImpl;
import org.eclipse.emf.ecore.resource.ResourceSet;
import org.eclipse.emf.ecore.resource.impl.ResourceSetImpl;
import org.eclipse.ocl.ecore.OCLExpression;
import org.eclipse.ocl.examples.eventmanager.EventFilter;
import org.eclipse.ocl.examples.eventmanager.EventManager;
import org.eclipse.ocl.examples.eventmanager.EventManagerFactory;
import org.eclipse.ocl.examples.eventmanager.util.Statistics;
import org.eclipse.ocl.examples.impactanalyzer.ImpactAnalyzerFactory;
import org.eclipse.ocl.examples.impactanalyzer.benchmark.preparation.ocl.BenchmarkOCLPreparer;
import org.eclipse.ocl.examples.impactanalyzer.benchmark.preparation.ocl.OCLExpressionWithContext;
import org.eclipse.ocl.examples.impactanalyzer.util.OCLFactory;
import org.eclipse.ocl.examples.testutils.BaseTest;
import org.junit.Before;
import org.junit.Test;
import behavioral.actions.ActionsFactory;
import behavioral.actions.ActionsPackage;
import data.classes.ClassesFactory;
import data.classes.ClassesPackage;
import data.classes.MethodSignature;
import data.classes.SapClass;
import dataaccess.analytics.AnalyticsFactory;
import dataaccess.analytics.AnalyticsPackage;
import dataaccess.expressions.literals.LiteralsFactory;
import dataaccess.expressions.literals.StringLiteral;
import modelmanagement.ModelmanagementPackage;
import persistence.expressions.ExpressionsFactory;
import persistence.expressions.ExpressionsPackage;
/**
* For each test there is a "small" and a "large" variant. The "small" variant
* tests that no notification is received twice. This requires recording the
* notifications received by a listener. For the "large" variants, notifications
* received are no longer recorded, and the duplicate check is not performed.
* The "small" or "large" variants differ in the value of
* {@link #NUMBER_OF_INNER_LOOP_EXECUTIONS} which defaults to 1000 iterations
* for the innermost loops in the "large" variant but is set to 3 for the
* "small" variant.
*
* @author Axel Uhl (d043530)
*
*/
public class PerformanceStressForEventManagerTest extends BaseTest {
private int NUMBER_OF_INNER_LOOP_EXECUTIONS = 1000;
private static final String FILTERSUBSCRIPTION = "filtersubscription";
private static final String FILTERCREATION = "filtercreation";
private static final String NOTIFY_REFERENCE_INIT_EXPRESSION_478 = "Notify_Reference_InitExpression_478";
private static final String NOTIFY_REFERENCE_CLAZZ_264 = "Notify_Reference_Clazz_264";
private static final String NOTIFY_REFERENCE_OWNED_SIGNATURES_41 = "Notify_Reference_OwnedSignatures_41";
private static final String NOTIFY_REFERENCE_FACTS_1 = "Notify_Reference_Facts_1";
private static final String NOTIFY_ATTRIBUTE_UPPER_MULTIPLICITY_487 = "Notify_Attribute_UpperMultiplicity_487";
private static final String NOTIFY_ATTRIBUTE_NAME_290 = "Notify_Attribute_Name_290";
private static final String NOTIFY_ATTRIBUTE_SNAPSHOT_1 = "Notify_Attribute_Snapshot_1";
private final int howManyMeasurements = 10;
private EventManager eventManager;
private ResourceSet rs;
private int notificationCount;
private int subscriptions;
private Set<NotificationReceiverWithFilter> listeners = new HashSet<NotificationReceiverWithFilter>();
private List<OCLExpressionWithContext> expressions;
private int numberOfAlreadyRegisteredExpressions;
@Override
@Before
public void setUp() {
rs = new ResourceSetImpl();
expressions = BenchmarkOCLPreparer.prepareAll(/* oclId */ null /* meaning ALL OCL expressions */);
eventManager = EventManagerFactory.eINSTANCE.getEventManagerFor(rs);
notificationCount = 0;
subscriptions = 0;
// uncomment the following line in case you want to compare with the performance of the naive
// event manager:
// eventManager = new EventManagerNaive(rs);
}
@Test
public void testCountRedundantFiltersSmall() throws IllegalArgumentException, SecurityException, IllegalAccessException,
NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
NUMBER_OF_INNER_LOOP_EXECUTIONS = 3;
testCountRedundantFilters();
}
@Test
public void testCountRedundantFilters() throws IllegalArgumentException, SecurityException, IllegalAccessException,
NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
registerFiltersForAllExpressions();
Object registrationManager = getRegistrationManager();
Method redundantFiltersMethod = registrationManager.getClass().getDeclaredMethod("redundantFilters");
int rf = (Integer) redundantFiltersMethod.invoke(registrationManager);
Field allRegistrationsField = registrationManager.getClass().getDeclaredField("allRegistrations");
allRegistrationsField.setAccessible(true);
Map<?, ?> allRegistrations = (Map<?, ?>) allRegistrationsField.get(registrationManager);
int totalRegistrationCount = allRegistrations.size();
debugPrintln("Total registrations: "+totalRegistrationCount+", redundant: "+rf+", distinct: "+(totalRegistrationCount-rf));
assertEquals("Hoping to have no redundant registrations", 0, rf);
}
@Test
public void testSingleAttributeValueChangeSmall() {
NUMBER_OF_INNER_LOOP_EXECUTIONS = 3;
testSingleAttributeValueChange();
}
@Test
public void testSingleAttributeValueChange() {
registerFiltersForAllExpressions();
handleAllTestEvents();
printStats();
}
@Test
public void testWithGrowingFilterSetSmall() {
NUMBER_OF_INNER_LOOP_EXECUTIONS = 3;
testWithGrowingFilterSet();
}
@Test
public void testWithGrowingFilterSet() {
for (int i=0; i<howManyMeasurements; i++) {
registerFiltersForANumberOfExpressions(expressions.size()/howManyMeasurements+1);
handleAllTestEvents();
printStats();
Statistics.getInstance().clear();
}
}
@Test
public void testIndividualNotificationsWithGrowingFilterSetSmall() throws IllegalArgumentException, SecurityException, IllegalAccessException,
NoSuchFieldException, InvocationTargetException, NoSuchMethodException {
NUMBER_OF_INNER_LOOP_EXECUTIONS = 3;
testIndividualNotificationsWithGrowingFilterSet();
}
@Test
public void testIndividualNotificationsWithGrowingFilterSet() {
for (Runnable handleRoutine : new Runnable[] {
new Runnable() { public void run() { handle_Attribute_Snapshot_1(); } },
new Runnable() { public void run() { handle_Attribute_Name_290(); } },
new Runnable() { public void run() { handle_Attribute_UpperMultiplicity_487(); } },
new Runnable() { public void run() { handle_Reference_Facts_1(); } },
new Runnable() { public void run() { handle_Reference_OwnedSignatures_41(); } },
new Runnable() { public void run() { handle_Reference_Clazz_264(); } },
new Runnable() { public void run() { handle_Reference_InitExpression_478(); } },
new Runnable() { public void run() { handle_Attribute_Snapshot_1(); printStats(NOTIFY_ATTRIBUTE_SNAPSHOT_1); } },
new Runnable() { public void run() { handle_Attribute_Name_290(); printStats(NOTIFY_ATTRIBUTE_NAME_290); } },
new Runnable() { public void run() { handle_Attribute_UpperMultiplicity_487(); printStats(NOTIFY_ATTRIBUTE_UPPER_MULTIPLICITY_487); } },
new Runnable() { public void run() { handle_Reference_Facts_1(); printStats(NOTIFY_REFERENCE_FACTS_1); } },
new Runnable() { public void run() { handle_Reference_OwnedSignatures_41(); printStats(NOTIFY_REFERENCE_OWNED_SIGNATURES_41); } },
new Runnable() { public void run() { handle_Reference_Clazz_264(); printStats(NOTIFY_REFERENCE_CLAZZ_264); } },
new Runnable() { public void run() { handle_Reference_InitExpression_478(); printStats(NOTIFY_REFERENCE_INIT_EXPRESSION_478); } }
}) {
eventManager = EventManagerFactory.eINSTANCE.getEventManagerFor(rs);
// eventManager = new EventManagerNaive(rs);
for (Adapter listener : listeners) {
eventManager.unsubscribe(listener);
}
listeners.clear();
notificationCount = 0;
numberOfAlreadyRegisteredExpressions = 0;
subscriptions = 0;
for (int i = 0; i <= howManyMeasurements; i++) {
// start first run with empty event manager
handleRoutine.run();
Statistics.getInstance().clear();
registerFiltersForANumberOfExpressions(expressions.size() / howManyMeasurements + 1);
}
}
}
private void printStats(String groupId) {
String minTableSizeGroupId;
try {
Field gidmts = getRegistrationManager().getClass().getDeclaredField("GROUP_ID_MINIMUM_TABLE_SIZE");
gidmts.setAccessible(true);
minTableSizeGroupId = (String) gidmts.get(null);
} catch (Exception e) {
minTableSizeGroupId = "minimumTableSize*1000000";
}
debugPrintln(groupId + "\t" + subscriptions + "\t"
+ notificationCount + "\t"
+ Statistics.getInstance().getAverage(minTableSizeGroupId)
+ "\t" + Statistics.getInstance().getAverage(groupId));
}
private void registerFiltersForAllExpressions() {
registerFiltersForANumberOfExpressions(expressions.size());
}
private void registerFiltersForANumberOfExpressions(int howManyMore) {
for (int i=0; i<howManyMore && numberOfAlreadyRegisteredExpressions < expressions.size(); i++) {
registerFilterForExpressionWithEventManager(expressions.get(numberOfAlreadyRegisteredExpressions++));
}
}
private void registerFilterForExpressionWithEventManager(OCLExpressionWithContext expression) {
OCLExpression e = expression.getExpression();
Statistics.getInstance().begin(FILTERCREATION, e);
EventFilter filter = ImpactAnalyzerFactory.INSTANCE.createImpactAnalyzer(e,
expression.getContext(), /* notifyOnNewContextElements */ false,
OCLFactory.getInstance()).createFilterForExpression();
Statistics.getInstance().end(FILTERCREATION, e);
Statistics.getInstance().begin(FILTERSUBSCRIPTION, e);
NotificationReceiverWithFilter listener = new NotificationReceiverWithFilter(filter);
listeners.add(listener); // hold on to the instance, otherwise it'll be collected due to weak reference usage
eventManager.subscribe(filter, listener);
subscriptions++;
Statistics.getInstance().end(FILTERSUBSCRIPTION, e);
}
private void printStats() {
debugPrintln("Subscription count\t"+subscriptions);
debugPrintln("Notification count\t"+notificationCount);
Statistics s = Statistics.getInstance();
debugPrintln(s.averageAsSV("\t"));
}
private void handleAllTestEvents() {
// first some attributes:
handle_Attribute_Name_290();
handle_Attribute_UpperMultiplicity_487();
handle_Attribute_Snapshot_1();
// now some references:
handle_Reference_Clazz_264();
handle_Reference_Facts_1();
handle_Reference_InitExpression_478();
handle_Reference_OwnedSignatures_41();
}
private void handle_Attribute_Name_290() {
// name has 290 entries in TableForAttributeFilter
InternalEObject notifier = (InternalEObject) ClassesFactory.eINSTANCE.createSapClass();
for (int i = 0; i < NUMBER_OF_INNER_LOOP_EXECUTIONS; i++) {
Notification n = new ENotificationImpl(notifier, Notification.SET,
ModelmanagementPackage.eINSTANCE.getNamedElement_Name(), "humba", "trala");
Statistics.getInstance().begin(NOTIFY_ATTRIBUTE_NAME_290, ""+i);
eventManager.handleEMFEvent(n);
Statistics.getInstance().end(NOTIFY_ATTRIBUTE_NAME_290, ""+i);
}
}
private void handle_Attribute_UpperMultiplicity_487() {
InternalEObject notifier;
// upperMultiplicity has 487 entries in TableForAttributeFilter
notifier = (InternalEObject) ClassesFactory.eINSTANCE.createClassTypeDefinition();
for (int i = 0; i < NUMBER_OF_INNER_LOOP_EXECUTIONS; i++) {
Notification n = new ENotificationImpl(notifier, Notification.SET,
ClassesPackage.eINSTANCE.getMultiplicity_UpperMultiplicity(), 1, -1);
Statistics.getInstance().begin(NOTIFY_ATTRIBUTE_UPPER_MULTIPLICITY_487, ""+i);
eventManager.handleEMFEvent(n);
Statistics.getInstance().end(NOTIFY_ATTRIBUTE_UPPER_MULTIPLICITY_487, ""+i);
}
}
private void handle_Attribute_Snapshot_1() {
InternalEObject notifier;
// snapshot has 1 entry in TableForAttributeFilter
notifier = (InternalEObject) ExpressionsFactory.eINSTANCE.createAll();
for (int i = 0; i < NUMBER_OF_INNER_LOOP_EXECUTIONS; i++) {
Notification n = new ENotificationImpl(notifier, Notification.SET,
ExpressionsPackage.eINSTANCE.getAll_Snapshot(), 1, -1);
Statistics.getInstance().begin(NOTIFY_ATTRIBUTE_SNAPSHOT_1, ""+i);
eventManager.handleEMFEvent(n);
Statistics.getInstance().end(NOTIFY_ATTRIBUTE_SNAPSHOT_1, ""+i);
}
}
private void handle_Reference_Clazz_264() {
InternalEObject notifier;
// clazz has 264 entries in TableForAssociationFilter
notifier = (InternalEObject) ClassesFactory.eINSTANCE.createClassTypeDefinition();
SapClass c = ClassesFactory.eINSTANCE.createSapClass();
for (int i = 0; i < NUMBER_OF_INNER_LOOP_EXECUTIONS; i++) {
Notification n = new ENotificationImpl(notifier, Notification.SET,
ClassesPackage.eINSTANCE.getClassTypeDefinition_Clazz(), null, c);
Statistics.getInstance().begin(NOTIFY_REFERENCE_CLAZZ_264, ""+i);
eventManager.handleEMFEvent(n);
Statistics.getInstance().end(NOTIFY_REFERENCE_CLAZZ_264, ""+i);
}
}
private void handle_Reference_Facts_1() {
InternalEObject notifier;
// facts has 1 entry in TableForAssociationFilter
notifier = (InternalEObject) AnalyticsFactory.eINSTANCE.createDimensionExpression();
StringLiteral s = LiteralsFactory.eINSTANCE.createStringLiteral();
for (int i = 0; i < NUMBER_OF_INNER_LOOP_EXECUTIONS; i++) {
Notification n = new ENotificationImpl(notifier, Notification.SET,
AnalyticsPackage.eINSTANCE.getDimensionExpression_Facts(), null, s);
Statistics.getInstance().begin(NOTIFY_REFERENCE_FACTS_1, ""+i);
eventManager.handleEMFEvent(n);
Statistics.getInstance().end(NOTIFY_REFERENCE_FACTS_1, ""+i);
}
}
private void handle_Reference_InitExpression_478() {
StringLiteral s = LiteralsFactory.eINSTANCE.createStringLiteral();
InternalEObject notifier;
// initExpression has 478 entries in TableForAssociationFilter
notifier = (InternalEObject) ActionsFactory.eINSTANCE.createNamedValueDeclaration();
for (int i = 0; i < NUMBER_OF_INNER_LOOP_EXECUTIONS; i++) {
Notification n = new ENotificationImpl(notifier, Notification.SET,
ActionsPackage.eINSTANCE.getNamedValueWithOptionalInitExpression_InitExpression(), null, s);
Statistics.getInstance().begin(NOTIFY_REFERENCE_INIT_EXPRESSION_478, ""+i);
eventManager.handleEMFEvent(n);
Statistics.getInstance().end(NOTIFY_REFERENCE_INIT_EXPRESSION_478, ""+i);
}
}
private void handle_Reference_OwnedSignatures_41() {
InternalEObject notifier;
// ownerSignatures has 41 entries in TableForAssociationFilter and is composite
notifier = (InternalEObject) ClassesFactory.eINSTANCE.createSapClass();
MethodSignature m = ClassesFactory.eINSTANCE.createMethodSignature();
for (int i = 0; i < NUMBER_OF_INNER_LOOP_EXECUTIONS; i++) {
Notification n = new ENotificationImpl(notifier, Notification.SET,
ClassesPackage.eINSTANCE.getSignatureOwner_OwnedSignatures(), null, m);
Statistics.getInstance().begin(NOTIFY_REFERENCE_OWNED_SIGNATURES_41, ""+i);
eventManager.handleEMFEvent(n);
Statistics.getInstance().end(NOTIFY_REFERENCE_OWNED_SIGNATURES_41, ""+i);
}
}
private boolean isSmallRun() {
return NUMBER_OF_INNER_LOOP_EXECUTIONS<10;
}
private class NotificationReceiverWithFilter implements Adapter {
private final Set<Notification> received = isSmallRun() ? new HashSet<Notification>() : null;
private final EventFilter filter;
private Notifier target;
public NotificationReceiverWithFilter(EventFilter filter) {
this.filter = filter;
}
public void notifyChanged(Notification notification) {
notificationCount++;
if (received != null) {
if (received.contains(notification)) {
fail("Received same notification twice with filter "
+ filter);
} else {
received.add(notification);
// uncomment the following line in case you want to ensure
// that all notifications are actually matched by the filter;
// such a test would mostly be relevant for the table-based event manager to
// detect false positives
// assertTrue("Filter "+filter+" doesn't match notification "+notification,
// filter.matchesFor(notification));
}
}
}
public Notifier getTarget() {
return target;
}
public void setTarget(Notifier newTarget) {
target = newTarget;
}
public boolean isAdapterForType(Object type) {
return false;
}
}
private Object getRegistrationManager() throws NoSuchFieldException, IllegalAccessException {
Field registrationManagerField = eventManager.getClass().getDeclaredField("registrationManager");
registrationManagerField.setAccessible(true);
Object registrationManager = registrationManagerField.get(eventManager);
return registrationManager;
}
}