From a898341011e19cb99307eff7f67443bcc2d81edb Mon Sep 17 00:00:00 2001 From: crschnick Date: Sun, 9 Mar 2025 03:45:20 +0000 Subject: [PATCH] Various performance fixes --- .../xpipe/app/comp/base/ListBoxViewComp.java | 13 +- .../app/comp/store/StoreEntryWrapper.java | 5 - .../io/xpipe/app/comp/store/StoreSection.java | 2 +- .../xpipe/app/comp/store/StoreSortMode.java | 117 +++++++++--------- .../xpipe/app/comp/store/StoreViewState.java | 7 +- .../xpipe/app/util/DerivedObservableList.java | 15 ++- 6 files changed, 84 insertions(+), 75 deletions(-) diff --git a/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java b/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java index 531fac025..ccba5e2b3 100644 --- a/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java +++ b/app/src/main/java/io/xpipe/app/comp/base/ListBoxViewComp.java @@ -62,12 +62,6 @@ public class ListBoxViewComp extends Comp> { refresh(scroll, vbox, c.getList(), all, cache, true, true); }); - all.addListener((ListChangeListener) c -> { - synchronized (cache) { - cache.keySet().retainAll(c.getList()); - } - }); - if (scrollBar) { scroll.setVbarPolicy(ScrollPane.ScrollBarPolicy.ALWAYS); scroll.skinProperty().subscribe(newValue -> { @@ -178,9 +172,16 @@ public class ListBoxViewComp extends Comp> { } private void updateVisibilities(ScrollPane scroll, VBox vbox) { + int count = 0; for (Node child : vbox.getChildren()) { var v = isVisible(scroll, vbox, child); child.setVisible(v); + if (v) { + count++; + } + } + if (count > 10) { + // System.out.println("Visible: " + count); } } diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryWrapper.java b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryWrapper.java index 09d310278..c3f284b30 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreEntryWrapper.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreEntryWrapper.java @@ -32,7 +32,6 @@ public class StoreEntryWrapper { private final Property name; private final DataStoreEntry entry; private final Property lastAccess; - private final Property lastAccessApplied = new SimpleObjectProperty<>(); private final BooleanProperty disabled = new SimpleBooleanProperty(); private final BooleanProperty busy = new SimpleBooleanProperty(); private final Property validity = new SimpleObjectProperty<>(); @@ -104,10 +103,6 @@ public class StoreEntryWrapper { setupListeners(); } - public void applyLastAccess() { - this.lastAccessApplied.setValue(lastAccess.getValue()); - } - public void moveTo(DataStoreCategory category) { ThreadHelper.runAsync(() -> { DataStorage.get().moveEntryToCategory(entry, category); diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreSection.java b/app/src/main/java/io/xpipe/app/comp/store/StoreSection.java index ee07c7b77..4736456e1 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreSection.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreSection.java @@ -96,7 +96,7 @@ public class StoreSection { var current = mappedSortMode.getValue(); if (current != null) { - return current.comparator().compare(current.representative(o1), current.representative(o2)); + return current.comparator().compare(o1, o2); } else { return 0; } diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreSortMode.java b/app/src/main/java/io/xpipe/app/comp/store/StoreSortMode.java index 4ea5cecc1..45be823d8 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreSortMode.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreSortMode.java @@ -1,19 +1,12 @@ package io.xpipe.app.comp.store; import java.time.Instant; -import java.util.Comparator; -import java.util.List; -import java.util.Locale; -import java.util.Optional; +import java.util.*; import java.util.stream.Stream; public interface StoreSortMode { StoreSortMode ALPHABETICAL_DESC = new StoreSortMode() { - @Override - public StoreSection representative(StoreSection s) { - return s; - } @Override public String getId() { @@ -27,11 +20,6 @@ public interface StoreSortMode { } }; StoreSortMode ALPHABETICAL_ASC = new StoreSortMode() { - @Override - public StoreSection representative(StoreSection s) { - return s; - } - @Override public String getId() { return "alphabetical-asc"; @@ -44,10 +32,10 @@ public interface StoreSortMode { .reversed(); } }; - StoreSortMode DATE_DESC = new StoreSortMode() { + StoreSortMode DATE_DESC = new StoreSortMode.DateSortMode() { - private Instant date(StoreSection s) { - var la = s.getWrapper().getLastAccessApplied().getValue(); + protected Instant date(StoreSection s) { + var la = s.getWrapper().getLastAccess().getValue(); if (la == null) { return Instant.MAX; } @@ -56,35 +44,19 @@ public interface StoreSortMode { } @Override - public StoreSection representative(StoreSection s) { - return Stream.concat( - s.getShownChildren().getList().stream() - .filter(section -> section.getWrapper() - .getEntry() - .getValidity() - .isUsable()) - .map(this::representative), - Stream.of(s)) - .max(Comparator.comparing(section -> date(section))) - .orElseThrow(); + protected int compare(Instant s1, Instant s2) { + return s2.compareTo(s1); } @Override public String getId() { return "date-desc"; } - - @Override - public Comparator comparator() { - return Comparator.comparing(e -> { - return date(e); - }); - } }; - StoreSortMode DATE_ASC = new StoreSortMode() { + StoreSortMode DATE_ASC = new StoreSortMode.DateSortMode() { - private Instant date(StoreSection s) { - var la = s.getWrapper().getLastAccessApplied().getValue(); + protected Instant date(StoreSection s) { + var la = s.getWrapper().getLastAccess().getValue(); if (la == null) { return Instant.MIN; } @@ -93,32 +65,16 @@ public interface StoreSortMode { } @Override - public StoreSection representative(StoreSection s) { - return Stream.concat( - s.getShownChildren().getList().stream() - .filter(section -> section.getWrapper() - .getEntry() - .getValidity() - .isUsable()) - .map(this::representative), - Stream.of(s)) - .max(Comparator.comparing(section -> date(section))) - .orElseThrow(); + protected int compare(Instant s1, Instant s2) { + return s1.compareTo(s2); } @Override public String getId() { return "date-asc"; } - - @Override - public Comparator comparator() { - return Comparator.comparing(e -> { - return date(e); - }) - .reversed(); - } }; + List ALL = List.of(ALPHABETICAL_DESC, ALPHABETICAL_ASC, DATE_DESC, DATE_ASC); static Optional fromId(String id) { @@ -131,9 +87,54 @@ public interface StoreSortMode { return DATE_ASC; } - StoreSection representative(StoreSection s); - String getId(); Comparator comparator(); + + abstract class DateSortMode implements StoreSortMode { + + private int sortModeIndex = -1; + private final Map cachedRepresentatives = new IdentityHashMap<>(); + + private StoreSection computeRepresentative(StoreSection s) { + return Stream.concat( + s.getShownChildren().getList().stream() + .filter(section -> section.getWrapper() + .getEntry() + .getValidity() + .isUsable()) + .map(this::getRepresentative), + Stream.of(s)) + .max(Comparator.comparing(section -> date(section))) + .orElseThrow(); + } + + private StoreSection getRepresentative(StoreSection s) { + if (StoreViewState.get().getSortModeObservable().get() != sortModeIndex) { + cachedRepresentatives.clear(); + sortModeIndex = StoreViewState.get().getSortModeObservable().get(); + } + + if (cachedRepresentatives.containsKey(s)) { + return cachedRepresentatives.get(s); + } + + var r = computeRepresentative(s); + cachedRepresentatives.put(s, r); + return r; + } + + protected abstract Instant date(StoreSection s); + + protected abstract int compare(Instant s1, Instant s2); + + @Override + public Comparator comparator() { + return (o1, o2) -> { + var r1 = getRepresentative(o1); + var r2 = getRepresentative(o2); + return DateSortMode.this.compare(date(r1), date(r2)); + }; + } + } } diff --git a/app/src/main/java/io/xpipe/app/comp/store/StoreViewState.java b/app/src/main/java/io/xpipe/app/comp/store/StoreViewState.java index 0f5dfa8e2..40523ba06 100644 --- a/app/src/main/java/io/xpipe/app/comp/store/StoreViewState.java +++ b/app/src/main/java/io/xpipe/app/comp/store/StoreViewState.java @@ -36,6 +36,9 @@ public class StoreViewState { @Getter private final IntegerProperty entriesListUpdateObservable = new SimpleIntegerProperty(); + @Getter + private final IntegerProperty sortModeObservable = new SimpleIntegerProperty(); + @Getter private final Property activeCategory = new SimpleObjectProperty<>(); @@ -121,7 +124,6 @@ public class StoreViewState { .setAll(FXCollections.observableArrayList(DataStorage.get().getStoreEntries().stream() .map(StoreEntryWrapper::new) .toList())); - allEntries.getList().forEach(e -> e.applyLastAccess()); categories .getList() .setAll(FXCollections.observableArrayList(DataStorage.get().getStoreCategories().stream() @@ -153,7 +155,7 @@ public class StoreViewState { } public void updateDisplay() { - allEntries.getList().forEach(e -> e.applyLastAccess()); + sortModeObservable.setValue(sortModeObservable.get() + 1); toggleStoreListUpdate(); } @@ -192,7 +194,6 @@ public class StoreViewState { var l = Arrays.stream(entry) .map(StoreEntryWrapper::new) .peek(storeEntryWrapper -> storeEntryWrapper.update()) - .peek(wrapper -> wrapper.applyLastAccess()) .toList(); // Don't update anything if we have already reset diff --git a/app/src/main/java/io/xpipe/app/util/DerivedObservableList.java b/app/src/main/java/io/xpipe/app/util/DerivedObservableList.java index dd6808528..16b8c3c35 100644 --- a/app/src/main/java/io/xpipe/app/util/DerivedObservableList.java +++ b/app/src/main/java/io/xpipe/app/util/DerivedObservableList.java @@ -85,9 +85,19 @@ public class DerivedObservableList { target.setAll(newList); } + private int indexOfFromStart(List list, T value, int start) { + for (int i = start; i < list.size(); i++) { + if (Objects.equals(list.get(i), value)) { + return i; + } + } + return -1; + } + private void setContentUnique(List newList) { var listSet = new HashSet<>(list); var newSet = new HashSet<>(newList); + // Addition if (newSet.containsAll(list)) { var l = new ArrayList<>(newList); @@ -100,7 +110,7 @@ public class DerivedObservableList { var start = 0; for (int end = 0; end <= list.size(); end++) { - var index = end < list.size() ? newList.indexOf(list.get(end)) : newList.size(); + var index = end < list.size() ? indexOfFromStart(newList, list.get(end), end) : newList.size(); for (; start < index; start++) { list.add(start, newList.get(start)); } @@ -133,7 +143,8 @@ public class DerivedObservableList { var cache = new HashMap(); var l1 = this.createNewDerived(); Runnable runnable = () -> { - cache.keySet().removeIf(t -> !getList().contains(t)); + var listSet = new HashSet<>(list); + cache.keySet().removeIf(t -> !listSet.contains(t)); l1.setContent(list.stream() .map(v -> { if (!cache.containsKey(v)) {