SideBySide2: Batch adjustments of padding widgets

Right after the view opens a number of deferred tasks fire, one for
each padding region and comment widget to rescale the padding size.
Every time these update a CM3 controlled property the CM3 instance
has to reflow its DOM, which is relatively expensive in the browser.

Batch the deferred work inside of the SideBySide2 instance as a list
of Runnables.  If any tasks are scheduled, schedule only one deferred
task to run all batched items inside of an operation() wrapper. This
allows CM3 to batch up the updates and reflow its DOM only once per
side, rather than once per padding widget.

In one example file (some version of ChangeScreen2.java) with a
handful of diff chunks and test comments this batching process saves
40.9 ms during the initial display of the file.

The exact amount of time saved depends on the complexity of the file,
the delta chunks and the number of comments, but this was a fairly
typical trivial example.  Real world files may see an even larger
savings.

Change-Id: Ib84c67d2f999fb7df5731ca3389a79b53da45a01
This commit is contained in:
Shawn Pearce
2013-11-22 20:16:44 -08:00
committed by Michael Zhou
parent 5c8a75b4b5
commit c962cc0bd6
3 changed files with 36 additions and 16 deletions

View File

@@ -17,8 +17,6 @@ package com.google.gerrit.client.diff;
import com.google.gerrit.client.changes.CommentInfo;
import com.google.gerrit.client.diff.PaddingManager.PaddingWidgetWrapper;
import com.google.gerrit.client.diff.SidePanel.GutterWrapper;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.event.dom.client.MouseOutEvent;
import com.google.gwt.event.dom.client.MouseOutHandler;
import com.google.gwt.event.dom.client.MouseOverEvent;
@@ -82,9 +80,9 @@ abstract class CommentBox extends Composite {
if (!getCommentInfo().has_line()) {
return;
}
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
parent.defer(new Runnable() {
@Override
public void execute() {
public void run() {
assert selfWidgetWrapper != null;
selfWidgetWrapper.getWidget().changed();
if (diffChunkInfo != null) {

View File

@@ -25,7 +25,6 @@ import com.google.gwt.core.client.GWT;
import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.RepeatingCommand;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
@@ -236,12 +235,7 @@ class DraftBox extends CommentBox {
getCm().focus();
getSelfWidgetWrapper().getWidget().clear();
getGutterWrapper().remove();
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
resizePaddingWidget();
}
});
resizePaddingWidget();
}
@UiHandler("message")

View File

@@ -134,6 +134,7 @@ public class SideBySide2 extends Screen {
private KeyCommandSet keysComment;
private KeyCommandSet keysOpenByEnter;
private List<HandlerRegistration> handlers;
private List<Runnable> deferred;
public SideBySide2(
PatchSet.Id base,
@@ -605,6 +606,7 @@ public class SideBySide2 extends Screen {
}
CommentBox addCommentBox(CommentInfo info, CommentBox box) {
box.setParent(this);
diffTable.add(box);
DisplaySide side = box.getSide();
CodeMirror cm = getCmFromSide(side);
@@ -643,7 +645,6 @@ public class SideBySide2 extends Screen {
LineWidget boxWidget = addLineWidget(cm, line, box, config);
box.setPaddingManager(manager);
box.setSelfWidgetWrapper(new PaddingWidgetWrapper(boxWidget, box.getElement()));
box.setParent(this);
if (otherChunk == null) {
box.setDiffChunkInfo(myChunk);
}
@@ -966,9 +967,9 @@ public class SideBySide2 extends Screen {
* key (or j/k) is held down. Performance on Chrome is fine
* without the deferral.
*/
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
defer(new Runnable() {
@Override
public void execute() {
public void run() {
LineHandle handle = cm.getLineHandleVisualStart(
cm.getCursor("end").getLine());
if (cm.hasActiveLine() && cm.getActiveLine().equals(handle)) {
@@ -1203,6 +1204,33 @@ public class SideBySide2 extends Screen {
return (index + diffChunks.size()) % diffChunks.size();
}
void defer(Runnable thunk) {
if (deferred == null) {
final ArrayList<Runnable> list = new ArrayList<Runnable>();
deferred = list;
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
deferred = null;
cmA.operation(new Runnable() {
@Override
public void run() {
cmB.operation(new Runnable() {
@Override
public void run() {
for (Runnable thunk : list) {
thunk.run();
}
}
});
}
});
}
});
}
deferred.add(thunk);
}
void resizePaddingOnOtherSide(DisplaySide mySide, int line) {
CodeMirror cm = getCmFromSide(mySide);
LineHandle handle = cm.getLineHandle(line);
@@ -1258,9 +1286,9 @@ public class SideBySide2 extends Screen {
lineActiveBoxMap.get(handle).resizePaddingWidget();
}
if (linePaddingOnOtherSideMap.containsKey(handle)) {
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
defer(new Runnable() {
@Override
public void execute() {
public void run() {
resizePaddingOnOtherSide(side, instance.getLineNumber(handle));
}
});