From fac9ff6dfe51b9e858fedf7816bb02bca615e19a Mon Sep 17 00:00:00 2001 From: Henri Sara Date: Thu, 22 Aug 2013 11:03:47 +0300 Subject: [PATCH] Optimize resetting of state when detaching components (#10899, #11284) This has a significant impact on some older browsers, especially IE8. This change also adds hierarchy update profiling statements to help find hotspots. Change-Id: Id7025776f8606794ba1d7aef99e2c37832b0c6bb --- .../vaadin/client/ApplicationConnection.java | 70 ++++++++++++++++--- 1 file changed, 61 insertions(+), 9 deletions(-) diff --git a/client/src/com/vaadin/client/ApplicationConnection.java b/client/src/com/vaadin/client/ApplicationConnection.java index 0b64f56c3e..dc3378560e 100644 --- a/client/src/com/vaadin/client/ApplicationConnection.java +++ b/client/src/com/vaadin/client/ApplicationConnection.java @@ -1701,7 +1701,9 @@ public class ApplicationConnection { // hierarchy, unregister it and any possible // children. The UIConnector should never be // unregistered even though it has no parent. + Profiler.enter("unregisterRemovedConnectors unregisterConnector"); connectorMap.unregisterConnector(c); + Profiler.leave("unregisterRemovedConnectors unregisterConnector"); unregistered++; } @@ -1984,6 +1986,8 @@ public class ApplicationConnection { JsArrayString hierarchyKeys = hierarchies.getKeyArray(); for (int i = 0; i < hierarchyKeys.length(); i++) { try { + Profiler.enter("updateConnectorHierarchy hierarchy entry"); + String connectorId = hierarchyKeys.get(i); ServerConnector parentConnector = connectorMap .getConnector(connectorId); @@ -1991,6 +1995,8 @@ public class ApplicationConnection { .getJSStringArray(connectorId); int childConnectorSize = childConnectorIds.length(); + Profiler.enter("updateConnectorHierarchy find new connectors"); + List newChildren = new ArrayList(); List newComponents = new ArrayList(); for (int connectorIndex = 0; connectorIndex < childConnectorSize; connectorIndex++) { @@ -2029,6 +2035,8 @@ public class ApplicationConnection { } } + Profiler.leave("updateConnectorHierarchy find new connectors"); + // TODO This check should be done on the server side in // the future so the hierarchy update is only sent when // something actually has changed @@ -2041,6 +2049,8 @@ public class ApplicationConnection { continue; } + Profiler.enter("updateConnectorHierarchy handle HasComponentsConnector"); + if (parentConnector instanceof HasComponentsConnector) { HasComponentsConnector ccc = (HasComponentsConnector) parentConnector; List oldComponents = ccc @@ -2062,7 +2072,13 @@ public class ApplicationConnection { + " has component children even though it isn't a HasComponentsConnector"); } + Profiler.leave("updateConnectorHierarchy handle HasComponentsConnector"); + + Profiler.enter("updateConnectorHierarchy setChildren"); parentConnector.setChildren(newChildren); + Profiler.leave("updateConnectorHierarchy setChildren"); + + Profiler.enter("updateConnectorHierarchy find removed children"); /* * Find children removed from this parent and mark for @@ -2084,11 +2100,17 @@ public class ApplicationConnection { maybeDetached.add(oldChild.getConnectorId()); } } + + Profiler.leave("updateConnectorHierarchy find removed children"); } catch (final Throwable e) { VConsole.error(e); + } finally { + Profiler.leave("updateConnectorHierarchy hierarchy entry"); } } + Profiler.enter("updateConnectorHierarchy detach removed connectors"); + /* * Connector is in maybeDetached at this point if it has been * removed from its parent but not added to any other parent @@ -2100,6 +2122,8 @@ public class ApplicationConnection { recursivelyDetach(removed, result.events); } + Profiler.leave("updateConnectorHierarchy detach removed connectors"); + Profiler.leave("updateConnectorHierarchy"); return result; @@ -2116,27 +2140,44 @@ public class ApplicationConnection { * is the closest we can get without data from the server. * #10151 */ + Profiler.enter("ApplicationConnection recursivelyDetach reset state"); try { + Profiler.enter("ApplicationConnection recursivelyDetach reset state - getStateType"); Type stateType = AbstractConnector.getStateType(connector); + Profiler.leave("ApplicationConnection recursivelyDetach reset state - getStateType"); // Empty state instance to get default property values from + Profiler.enter("ApplicationConnection recursivelyDetach reset state - createInstance"); Object defaultState = stateType.createInstance(); + Profiler.leave("ApplicationConnection recursivelyDetach reset state - createInstance"); - SharedState state = connector.getState(); - - JsArrayObject properties = stateType - .getPropertiesAsArray(); - int size = properties.size(); - for (int i = 0; i < size; i++) { - Property property = properties.get(i); - property.setValue(state, - property.getValue(defaultState)); + if (connector instanceof AbstractConnector) { + // optimization as the loop setting properties is very + // slow, especially on IE8 + replaceState((AbstractConnector) connector, + defaultState); + } else { + SharedState state = connector.getState(); + + Profiler.enter("ApplicationConnection recursivelyDetach reset state - properties"); + JsArrayObject properties = stateType + .getPropertiesAsArray(); + int size = properties.size(); + for (int i = 0; i < size; i++) { + Property property = properties.get(i); + property.setValue(state, + property.getValue(defaultState)); + } + Profiler.leave("ApplicationConnection recursivelyDetach reset state - properties"); } } catch (NoDataException e) { throw new RuntimeException("Can't reset state for " + Util.getConnectorString(connector), e); + } finally { + Profiler.leave("ApplicationConnection recursivelyDetach reset state"); } + Profiler.enter("ApplicationConnection recursivelyDetach perform detach"); /* * Recursively detach children to make sure they get * setParent(null) and hierarchy change events as needed. @@ -2153,18 +2194,22 @@ public class ApplicationConnection { } recursivelyDetach(child, events); } + Profiler.leave("ApplicationConnection recursivelyDetach perform detach"); /* * Clear child list and parent */ + Profiler.enter("ApplicationConnection recursivelyDetach clear children and parent"); connector .setChildren(Collections. emptyList()); connector.setParent(null); + Profiler.leave("ApplicationConnection recursivelyDetach clear children and parent"); /* * Create an artificial hierarchy event for containers to give * it a chance to clean up after its children if it has any */ + Profiler.enter("ApplicationConnection recursivelyDetach create hierarchy event"); if (connector instanceof HasComponentsConnector) { HasComponentsConnector ccc = (HasComponentsConnector) connector; List oldChildren = ccc @@ -2185,8 +2230,15 @@ public class ApplicationConnection { events.add(event); } } + Profiler.leave("ApplicationConnection recursivelyDetach create hierarchy event"); } + private native void replaceState(AbstractConnector connector, + Object defaultState) + /*-{ + connector.@com.vaadin.client.ui.AbstractConnector::state = defaultState; + }-*/; + private void handleRpcInvocations(ValueMap json) { if (json.containsKey("rpc")) { Profiler.enter("handleRpcInvocations"); -- 2.39.5