Skip to content

Commit e48d9d6

Browse files
committed
Optimizations of status change notifications
## Skip firing events for up-to-date Skip firing events for up-to-date files that are not yet in the cache, since UPTODATE is the default for managed files. This drastically reduces the time spent in the refreshStatusesBatch method on big repositories executed when Commit dialog opens. For example, on the Netbeans repository, from around 8 seconds to 20ms. ## Batch status change notifications Batch status change notifications to avoid per-file event overhead. Replace per-file PROP_FILE_STATUS_CHANGED firing in refreshStatusesBatch with a single PROP_FILES_STATUS_CHANGED batch event, eliminating 100k redundant propertyChange/schedule/SwingUtilities.invokeLater calls on first load. This improves performance a bit because it eliminates many method calls. However, in the end, the number of files changed is the same so the event handler still needs to process all of them. ## Move status updates to a background thread File status update requires I/O operation to refresh files metadata from FS. This is very slow when many files need to be updated. Moving them to a background thread offloads this slow operation from the main thread. Updates can run asynchrnously without blocking the main thread that fires the status events, they just update UI hints, they have no impact no behavior. For the whole Netbeans repository, this shortens the time it takes to complete the firePropertyChange event from 6 seconds to 1 second.
1 parent 869d480 commit e48d9d6

7 files changed

Lines changed: 172 additions & 130 deletions

File tree

ide/git/nbproject/project.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717
javac.compilerargs=-Xlint
18-
javac.release=17
18+
javac.release=21
1919
nbm.homepage=http://netbeans.org/projects/versioncontrol/pages/Git_main
2020
nbm.module.author=Ondrej Vrabec
2121
nbm.needs.restart=true

ide/git/src/org/netbeans/modules/git/FileStatusCache.java

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@
5757
public class FileStatusCache {
5858

5959
public static final String PROP_FILE_STATUS_CHANGED = "status.changed"; // NOI18N
60+
public static final String PROP_FILES_STATUS_CHANGED = "status.changed.batch"; // NOI18N
6061

6162
private final CacheIndex conflictedFiles, modifiedFiles, ignoredFiles;
6263
private static final Logger LOG = Logger.getLogger("org.netbeans.modules.git.status.cache"); //NOI18N
@@ -562,6 +563,7 @@ private void refreshStatusesBatch (Map<File, GitStatus> interestingFiles) {
562563
boolean fireEvent = true;
563564
current = getInfo(file);
564565
fi = checkForIgnore(fi, current, file);
566+
boolean upToDateOnFirstLoad = current == null && fi.getStatus().equals(EnumSet.of(Status.UPTODATE));
565567
if (equivalent(fi, current)) {
566568
// no need to fire an event
567569
if (Utilities.isWindows() || Utilities.isMac()) {
@@ -570,6 +572,10 @@ private void refreshStatusesBatch (Map<File, GitStatus> interestingFiles) {
570572
} else {
571573
continue;
572574
}
575+
} else if (upToDateOnFirstLoad) {
576+
// file is up-to-date and not yet in cache: UPTODATE is the implicit default,
577+
// no cache update or event needed
578+
continue;
573579
}
574580
boolean addToIndex = updateCachedValue(fi, file);
575581
indexUpdates.add(new IndexUpdateItem(file, fi, addToIndex));
@@ -579,8 +585,8 @@ private void refreshStatusesBatch (Map<File, GitStatus> interestingFiles) {
579585
}
580586
updateIndexBatch(indexUpdates);
581587
}
582-
for (ChangedEvent event : events) {
583-
fireFileStatusChanged(event);
588+
if (!events.isEmpty()) {
589+
listenerSupport.firePropertyChange(PROP_FILES_STATUS_CHANGED, null, events);
584590
}
585591
}
586592

ide/git/src/org/netbeans/modules/git/GitVCS.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@
2323
import java.beans.PropertyChangeListener;
2424
import java.util.MissingResourceException;
2525
import java.io.File;
26+
import java.util.HashSet;
27+
import java.util.List;
2628
import java.util.Set;
2729
import java.util.logging.Logger;
2830
import java.util.prefs.PreferenceChangeEvent;
@@ -38,17 +40,17 @@
3840
* @author ondra
3941
*/
4042
@VersioningSystem.Registration(
41-
displayName="#CTL_Git_DisplayName",
42-
menuLabel="#CTL_Git_MainMenu",
43-
metadataFolderNames={".git"},
43+
displayName="#CTL_Git_DisplayName",
44+
menuLabel="#CTL_Git_MainMenu",
45+
metadataFolderNames={".git"},
4446
actionsCategory="Git"
4547
)
4648
public class GitVCS extends VersioningSystem implements PropertyChangeListener, PreferenceChangeListener {
4749

4850
private static final Logger LOG = Logger.getLogger("org.netbeans.modules.git.GitVCS"); //NOI18N
4951

5052
public GitVCS() {
51-
putProperty(PROP_DISPLAY_NAME, getDisplayName());
53+
putProperty(PROP_DISPLAY_NAME, getDisplayName());
5254
putProperty(PROP_MENU_LABEL, org.openide.util.NbBundle.getMessage(GitVCS.class, "CTL_Git_MainMenu")); // NOI18N
5355
GitModuleConfig.getDefault().getPreferences().addPreferenceChangeListener(this);
5456
Git.getInstance().registerGitVCS(this);
@@ -77,7 +79,7 @@ public void getOriginalFile (File workingCopy, File originalFile) {
7779
public File getTopmostManagedAncestor(File file) {
7880
return Git.getInstance().getTopmostManagedAncestor(file);
7981
}
80-
82+
8183
@Override
8284
public CollocationQueryImplementation getCollocationQueryImplementation() {
8385
return collocationQueryImplementation;
@@ -111,6 +113,13 @@ public void propertyChange(PropertyChangeEvent event) {
111113
if (event.getPropertyName().equals(FileStatusCache.PROP_FILE_STATUS_CHANGED)) {
112114
FileStatusCache.ChangedEvent changedEvent = (FileStatusCache.ChangedEvent) event.getNewValue();
113115
fireStatusChanged(changedEvent.getFile());
116+
} else if (event.getPropertyName().equals(FileStatusCache.PROP_FILES_STATUS_CHANGED)) {
117+
List<FileStatusCache.ChangedEvent> changedEvents = (List<FileStatusCache.ChangedEvent>) event.getNewValue();
118+
Set<File> files = HashSet.newHashSet(changedEvents.size());
119+
for (FileStatusCache.ChangedEvent e : changedEvents) {
120+
files.add(e.getFile());
121+
}
122+
fireStatusChanged(files);
114123
} else if (event.getPropertyName().equals(Git.PROP_ANNOTATIONS_CHANGED)) {
115124
fireAnnotationsChanged((Set<File>) event.getNewValue());
116125
} else if (event.getPropertyName().equals(Git.PROP_VERSIONED_FILES_CHANGED)) {

ide/git/src/org/netbeans/modules/git/ui/diff/MultiDiffPanelController.java

Lines changed: 30 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ public class MultiDiffPanelController implements ActionListener, PropertyChangeL
196196

197197
private GitProgressSupport statusRefreshSupport;
198198
private PreferenceChangeListener prefList;
199-
199+
200200
private final Revision revisionOriginalLeft;
201201
private final Revision revisionOriginalRight;
202202
private Revision revisionLeft;
@@ -211,7 +211,7 @@ public class MultiDiffPanelController implements ActionListener, PropertyChangeL
211211
private int popupViewIndex;
212212
private int requestedRightLine = -1;
213213
private int requestedLeftLine = -1;
214-
214+
215215
private static final int VIEW_MODE_TABLE = 1;
216216
private static final int VIEW_MODE_TREE = 2;
217217
private int currentSetupDiffLengthChanged;
@@ -270,7 +270,7 @@ public MultiDiffPanelController (File file, Revision rev1, Revision rev2, int re
270270
prepareTask = RP.create(dpt);
271271
prepareTask.schedule(0);
272272
}
273-
273+
274274
private MultiDiffPanelController (VCSContext context, Revision revisionLeft, Revision revisionRight, boolean fixedRevisions) {
275275
this.context = context;
276276
this.revisionLeft = revisionOriginalLeft = revisionLeft;
@@ -315,7 +315,7 @@ private void setActiveComponent (DiffFileViewComponent<DiffNode> fileComponent)
315315
panel.splitPane.setTopComponent(getActiveFileComponent().getComponent());
316316
panel.splitPane.setDividerLocation(gg);
317317
}
318-
318+
319319
private void refreshSelectionCombos () {
320320
if (!fixedRevisions && GitUtils.getRepositoryRoots(context).size() == 1) {
321321
panel.cmbDiffTreeFirst.setEnabled(false);
@@ -432,7 +432,7 @@ public void componentClosed () {
432432
cancelBackgroundTasks();
433433
setups.clear();
434434
editorCookies.clear();
435-
435+
436436
if (list != null) {
437437
Git.getInstance().getFileStatusCache().removePropertyChangeListener(list);
438438
}
@@ -569,7 +569,7 @@ private void initToolbarButtons () {
569569
panel.treeButton.setVisible(false);
570570
}
571571
}
572-
572+
573573
private JComponent getInfoPanelLoading () {
574574
if (infoPanelLoadingFromRepo == null) {
575575
infoPanelLoadingFromRepo = new NoContentPanel(NbBundle.getMessage(MultiDiffPanel.class, "MSG_DiffPanel_NoContent"));
@@ -812,11 +812,11 @@ public void run () {
812812
public void run () {
813813
multiTextDiffSupport = null;
814814
}
815-
815+
816816
});
817817
}
818818
}
819-
819+
820820
private String findEncoding () {
821821
for (File f : actionRoots.getValue()) {
822822
FileObject fo = FileUtil.toFileObject(f);
@@ -861,7 +861,7 @@ private void exportDiff (ByteArrayOutputStream bos) throws GitException {
861861
actionRoots.getKey(), Bundle.MSG_DiffPanel_multiTextualDiff_preparing());
862862
}
863863
}
864-
864+
865865
});
866866
displayDiffView();
867867
}
@@ -891,7 +891,7 @@ private Map.Entry<File, File[]> getSelectedActionRoots () {
891891
ctx = GitUtils.getContextForFiles(filterExcluded(selectedFiles));
892892
return GitUtils.getActionRoots(ctx);
893893
}
894-
894+
895895
private File[] filterExcluded (File[] files) {
896896
List<File> filtered = new ArrayList<>(files.length);
897897
for (File f : files) {
@@ -903,7 +903,7 @@ private File[] filterExcluded (File[] files) {
903903
}
904904
return filtered.toArray(new File[0]);
905905
}
906-
906+
907907
private boolean showingFileComponent() {
908908
return getActiveFileComponent() != null;
909909
}
@@ -1064,7 +1064,7 @@ private DiffMode getDiffMode () {
10641064
}
10651065
return diffMode;
10661066
}
1067-
1067+
10681068
private Revision getSelectedRevision (JComboBox cmbDiffTree) {
10691069
Object selectedItem = cmbDiffTree.getSelectedItem();
10701070
Revision selection = null;
@@ -1123,15 +1123,25 @@ public void propertyChange (PropertyChangeEvent evt) {
11231123
if (FileStatusCache.PROP_FILE_STATUS_CHANGED.equals(evt.getPropertyName())) {
11241124
FileStatusCache.ChangedEvent changedEvent = (FileStatusCache.ChangedEvent) evt.getNewValue();
11251125
if (LOG.isLoggable(Level.FINE)) {
1126-
LOG.log(Level.FINE, "File status for file {0} changed from {1} to {2}", new Object[] {
1127-
changedEvent.getFile(),
1126+
LOG.log(Level.FINE, "File status for file {0} changed from {1} to {2}", new Object[] {
1127+
changedEvent.getFile(),
11281128
changedEvent.getOldInfo(),
11291129
changedEvent.getNewInfo() } );
11301130
}
1131-
if (revisionLeft == Revision.HEAD // remove when we're able to refresh single file changes for Local vs. any revision
1131+
if (revisionLeft == Revision.HEAD // remove when we're able to refresh single file changes for Local vs. any revision
11321132
&& revisionRight == Revision.LOCAL && affectsView(changedEvent)) {
11331133
applyChange(changedEvent);
11341134
}
1135+
} else if (FileStatusCache.PROP_FILES_STATUS_CHANGED.equals(evt.getPropertyName())) {
1136+
if (revisionLeft == Revision.HEAD && revisionRight == Revision.LOCAL) {
1137+
@SuppressWarnings("unchecked")
1138+
List<FileStatusCache.ChangedEvent> changedEvents = (List<FileStatusCache.ChangedEvent>) evt.getNewValue();
1139+
for (FileStatusCache.ChangedEvent changedEvent : changedEvents) {
1140+
if (affectsView(changedEvent)) {
1141+
applyChange(changedEvent);
1142+
}
1143+
}
1144+
}
11351145
} else if (DiffController.PROP_DIFFERENCES.equals(evt.getPropertyName())) {
11361146
// something has changed
11371147
Setup setup = currentSetup;
@@ -1537,7 +1547,7 @@ private boolean isLocal () {
15371547
* Eliminates unnecessary cache.listFiles call as well as the whole node creation process ()
15381548
*/
15391549
private final class ApplyChangesTask implements Runnable, Cancellable {
1540-
1550+
15411551
private volatile boolean canceled;
15421552

15431553
@Override
@@ -1651,7 +1661,7 @@ public boolean cancel () {
16511661
return true;
16521662
}
16531663
}
1654-
1664+
16551665
private class RefreshComboTask implements Runnable {
16561666

16571667
@Override
@@ -1663,7 +1673,7 @@ public void run () {
16631673
if (revisionOriginalLeft != Revision.HEAD) {
16641674
modelLeft.add(Revision.HEAD);
16651675
}
1666-
modelRight.add(revisionOriginalRight);
1676+
modelRight.add(revisionOriginalRight);
16671677
if (revisionOriginalRight != Revision.LOCAL) {
16681678
modelRight.add(Revision.LOCAL);
16691679
}
@@ -1767,7 +1777,7 @@ public void run() {
17671777
GitClientExceptionHandler.notifyException(exception, true);
17681778
}
17691779
}
1770-
1780+
17711781
private DiffNode[] prepareSetupsToRefresh () {
17721782
return Mutex.EVENT.readAccess(new Mutex.Action<DiffNode[]>() {
17731783
@Override
@@ -1810,7 +1820,7 @@ private int getMatchingLine (StreamSource ss2, StreamSource ss1, int requestedRi
18101820
}
18111821
}
18121822
}// </editor-fold>
1813-
1823+
18141824
@NbBundle.Messages({
18151825
"MSG_Revision_Select_Tooltip=Select a revision from the picker"
18161826
})

ide/git/src/org/netbeans/modules/git/ui/status/VersioningPanelController.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -408,6 +408,14 @@ public void propertyChange (PropertyChangeEvent evt) {
408408
if (affectsView((FileStatusCache.ChangedEvent) evt.getNewValue())) {
409409
applyChange(changedEvent);
410410
}
411+
} else if (FileStatusCache.PROP_FILES_STATUS_CHANGED.equals(evt.getPropertyName())) {
412+
@SuppressWarnings("unchecked")
413+
List<FileStatusCache.ChangedEvent> changedEvents = (List<FileStatusCache.ChangedEvent>) evt.getNewValue();
414+
for (FileStatusCache.ChangedEvent changedEvent : changedEvents) {
415+
if (affectsView(changedEvent)) {
416+
applyChange(changedEvent);
417+
}
418+
}
411419
}
412420
}
413421

0 commit comments

Comments
 (0)