Changeset 18794 in josm for trunk


Ignore:
Timestamp:
2023-08-07T21:10:16+02:00 (14 months ago)
Author:
taylor.smock
Message:

Fix #23085: Improve speed of selecting large amounts of objects

  • JVM CPU usage was down ~50%, EDT CPU usage was down ~25%. EDT memory usage was down
  • Avoid Stream allocations in ConflictCollection.hasConflictForMy by only looking for a conflict if conflicts exist
  • Avoid many string instantiations in DefaultNameFormatter by using cached properties. This significantly reduces memory allocations CPU usage for DefaultNameFormatter methods.
  • Avoid some Stream allocations by using standard for loops in DefaultNameFormatter
  • Use "" to get the component in PrimitiveRenderer.getListCellRendererComponent -- this reduced the memory allocations by ~50% and CPU usage by ~70% for getListCellRendererComponent by itself, and appears to have no side-effects. We should ask users on different systems with different UI systems if it works properly.
  • Significantly reduce cost of Way.hasOnlyLocatableNodes by using a standard for loop instead of a stream -- this isn't as important for this ticket, but was found while profiling. This makes that method have no effective memory allocations and reduces the CPU usage by ~90%.
Location:
trunk/src/org/openstreetmap/josm
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/conflict/ConflictCollection.java

    r18014 r18794  
    146146     */
    147147    public Conflict<?> getConflictForMy(OsmPrimitive my) {
     148        if (conflicts.isEmpty()) {
     149            return null;
     150        }
    148151        return conflicts.stream()
    149152                .filter(c -> c.isMatchingMy(my))
     
    161164     */
    162165    public Conflict<?> getConflictForTheir(OsmPrimitive their) {
     166        if (conflicts.isEmpty()) {
     167            return null;
     168        }
    163169        return conflicts.stream()
    164170                .filter(c -> c.isMatchingTheir(their))
  • trunk/src/org/openstreetmap/josm/data/osm/DefaultNameFormatter.java

    r18542 r18794  
    3030import org.openstreetmap.josm.data.osm.history.HistoryRelation;
    3131import org.openstreetmap.josm.data.osm.history.HistoryWay;
     32import org.openstreetmap.josm.data.preferences.BooleanProperty;
     33import org.openstreetmap.josm.data.preferences.CachingProperty;
    3234import org.openstreetmap.josm.gui.tagging.presets.TaggingPreset;
    3335import org.openstreetmap.josm.gui.tagging.presets.TaggingPresetNameTemplateList;
     
    4951
    5052    private static final List<NameFormatterHook> formatHooks = new LinkedList<>();
     53    private static final CachingProperty<Boolean> PROPERTY_SHOW_ID =
     54            new BooleanProperty("osm-primitives.showid", false).cached();
     55    private static final CachingProperty<Boolean> PROPERTY_SHOW_ID_NEW_PRIMITIVES =
     56            new BooleanProperty("osm-primitives.showid.new-primitives", false).cached();
     57    private static final CachingProperty<Boolean> PROPERTY_SHOW_VERSION =
     58            new BooleanProperty("osm-primitives.showversion", false).cached();
     59    private static final CachingProperty<Boolean> PROPERTY_SHOW_COOR =
     60            new BooleanProperty("osm-primitives.showcoor", false).cached();
     61    private static final CachingProperty<Boolean> PROPERTY_LOCALIZE_NAME =
     62            new BooleanProperty("osm-primitives.localize-name", true).cached();
    5163
    5264    private static final List<String> HIGHWAY_RAILWAY_WATERWAY_LANDUSE_BUILDING = Arrays.asList(
     
    143155    protected void decorateNameWithId(StringBuilder name, IPrimitive primitive) {
    144156        int version = primitive.getVersion();
    145         if (Config.getPref().getBoolean("osm-primitives.showid")) {
    146             long id = Config.getPref().getBoolean("osm-primitives.showid.new-primitives") ?
     157        if (Boolean.TRUE.equals(PROPERTY_SHOW_ID.get())) {
     158            long id = Boolean.TRUE.equals(PROPERTY_SHOW_ID_NEW_PRIMITIVES.get()) ?
    147159                    primitive.getUniqueId() : primitive.getId();
    148             if (Config.getPref().getBoolean("osm-primitives.showversion") && version > 0) {
     160            if (version > 0 && Boolean.TRUE.equals(PROPERTY_SHOW_VERSION.get())) {
    149161                name.append(tr(" [id: {0}, v{1}]", id, version));
    150162            } else {
    151163                name.append(tr(" [id: {0}]", id));
    152164            }
    153         } else if (Config.getPref().getBoolean("osm-primitives.showversion")) {
     165        } else if (Boolean.TRUE.equals(PROPERTY_SHOW_VERSION.get())) {
    154166            name.append(tr(" [v{0}]", version));
    155167        }
     
    188200                preset.nameTemplate.appendText(name, (TemplateEngineDataProvider) node);
    189201            }
    190             if (node.isLatLonKnown() && Config.getPref().getBoolean("osm-primitives.showcoor")) {
     202            if (node.isLatLonKnown() && Boolean.TRUE.equals(PROPERTY_SHOW_COOR.get())) {
    191203                name.append(" \u200E(");
    192204                name.append(CoordinateFormatManager.getDefaultFormat().toString(node, ", "));
     
    197209
    198210        String result = name.toString();
    199         return formatHooks.stream().map(hook -> hook.checkFormat(node, result))
    200                 .filter(Objects::nonNull)
    201                 .findFirst().orElse(result);
    202 
     211        // This avoids memallocs from Stream map, filter and Optional creation.
     212        for (NameFormatterHook hook : formatHooks) {
     213            final String checkFormat = hook.checkFormat(node, result);
     214            if (checkFormat != null) {
     215                return checkFormat;
     216            }
     217        }
     218        return result;
    203219    }
    204220
     
    275291
    276292    private static String formatLocalName(IPrimitive osm) {
    277         if (Config.getPref().getBoolean("osm-primitives.localize-name", true)) {
     293        if (Boolean.TRUE.equals(PROPERTY_LOCALIZE_NAME.get())) {
    278294            return osm.getLocalName();
    279295        } else {
     
    283299
    284300    private static String formatLocalName(HistoryOsmPrimitive osm) {
    285         if (Config.getPref().getBoolean("osm-primitives.localize-name", true)) {
     301        if (Boolean.TRUE.equals(PROPERTY_LOCALIZE_NAME.get())) {
    286302            return osm.getLocalName();
    287303        } else {
     
    530546     */
    531547    protected void decorateNameWithId(StringBuilder name, HistoryOsmPrimitive primitive) {
    532         if (Config.getPref().getBoolean("osm-primitives.showid")) {
     548        if (Boolean.TRUE.equals(PROPERTY_SHOW_ID.get())) {
    533549            name.append(tr(" [id: {0}]", primitive.getId()));
    534550        }
  • trunk/src/org/openstreetmap/josm/data/osm/Way.java

    r18553 r18794  
    635635     */
    636636    public boolean hasOnlyLocatableNodes() {
    637         return Arrays.stream(nodes).allMatch(Node::isLatLonKnown);
     637        // This is used in various places, some of which are on the UI thread.
     638        // If this is called many times, the memory allocations can become prohibitive, if
     639        // we use Java streams.
     640        // This can be easily tested by loading a large amount of ways into JOSM, and then
     641        // selecting everything.
     642        for (Node node : nodes) {
     643            if (!node.isLatLonKnown()) {
     644                return false;
     645            }
     646        }
     647        return true;
    638648    }
    639649
  • trunk/src/org/openstreetmap/josm/gui/PrimitiveRenderer.java

    r18215 r18794  
    4848    public Component getListCellRendererComponent(JList<? extends IPrimitive> list, IPrimitive value, int index,
    4949            boolean isSelected, boolean cellHasFocus) {
    50         Component def = defaultListCellRenderer.getListCellRendererComponent(list, value, index, isSelected, cellHasFocus);
     50        // Use an empty string for the value to avoid expensive Object.toString calls, which will happen again in renderer.
     51        Component def = defaultListCellRenderer.getListCellRendererComponent(list, "", index, isSelected, cellHasFocus);
    5152        return renderer(def, value, list.getModel().getSize() > 1000);
    5253    }
Note: See TracChangeset for help on using the changeset viewer.