| /******************************************************************************* |
| * 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; |
| } |
| |
| } |