Ticket #13275: 13275.patch
| File 13275.patch, 18.6 KB (added by , 10 years ago) |
|---|
-
src/org/openstreetmap/josm/data/osm/MultipolygonBuilder.java
diff --git a/src/org/openstreetmap/josm/data/osm/MultipolygonBuilder.java b/src/org/openstreetmap/josm/data/osm/MultipolygonBuilder.java index 1951a84..030d1e3 100644
a b 5 5 6 6 import java.awt.Rectangle; 7 7 import java.awt.geom.Area; 8 import java.io.IOException;9 import java.io.ObjectInputStream;10 import java.io.ObjectOutputStream;11 8 import java.util.ArrayList; 12 9 import java.util.Collection; 13 10 import java.util.Collections; 14 11 import java.util.HashSet; 15 12 import java.util.List; 13 import java.util.Objects; 16 14 import java.util.Set; 17 import java.util.concurrent.ForkJoinPool;18 import java.util.concurrent.ForkJoinTask;19 import java.util.concurrent.RecursiveTask;20 15 21 16 import org.openstreetmap.josm.Main; 22 17 import org.openstreetmap.josm.tools.Geometry; 23 18 import org.openstreetmap.josm.tools.Geometry.PolygonIntersection; 24 19 import org.openstreetmap.josm.tools.MultiMap; 25 20 import org.openstreetmap.josm.tools.Pair; 26 import org.openstreetmap.josm.tools.Utils;27 21 28 22 /** 29 23 * Helper class to build multipolygons from multiple ways. … … 33 27 */ 34 28 public class MultipolygonBuilder { 35 29 36 private static final ForkJoinPool THREAD_POOL =37 Utils.newForkJoinPool("multipolygon_creation.numberOfThreads", "multipolygon-builder-%d", Thread.NORM_PRIORITY);38 39 30 /** 40 31 * Represents one polygon that consists of multiple ways. 41 32 */ … … private String makeFromPolygons(List<JoinedPolygon> polygons) { 306 297 * @return the outermostWay, or {@code null} if intersection found. 307 298 */ 308 299 private static List<PolygonLevel> findOuterWaysMultiThread(List<JoinedPolygon> boundaryWays) { 309 return THREAD_POOL.invoke(new Worker(boundaryWays, 0, boundaryWays.size(), new ArrayList<PolygonLevel>(), 310 Math.max(32, boundaryWays.size() / THREAD_POOL.getParallelism() / 3))); 300 final List<PolygonLevel> output = new ArrayList<>(); 301 return boundaryWays.parallelStream() 302 .map(way -> Worker.processOuterWay(0, boundaryWays, output, way)) 303 .allMatch(Objects::nonNull) ? output : null; 311 304 } 312 305 313 private static class Worker extends RecursiveTask<List<PolygonLevel>> { 314 315 // Needed for Findbugs / Coverity because parent class is serializable 316 private static final long serialVersionUID = 1L; 317 318 private final transient List<JoinedPolygon> input; 319 private final int from; 320 private final int to; 321 private final transient List<PolygonLevel> output; 322 private final int directExecutionTaskSize; 323 324 Worker(List<JoinedPolygon> input, int from, int to, List<PolygonLevel> output, int directExecutionTaskSize) { 325 this.input = input; 326 this.from = from; 327 this.to = to; 328 this.output = output; 329 this.directExecutionTaskSize = directExecutionTaskSize; 330 } 306 private static class Worker { 331 307 332 308 /** 333 309 * Collects outer way and corresponding inner ways from all boundaries. … … private String makeFromPolygons(List<JoinedPolygon> polygons) { 380 356 } 381 357 return result; 382 358 } 383 384 @Override385 protected List<PolygonLevel> compute() {386 if (to - from <= directExecutionTaskSize) {387 return computeDirectly();388 } else {389 final Collection<ForkJoinTask<List<PolygonLevel>>> tasks = new ArrayList<>();390 for (int fromIndex = from; fromIndex < to; fromIndex += directExecutionTaskSize) {391 tasks.add(new Worker(input, fromIndex, Math.min(fromIndex + directExecutionTaskSize, to),392 new ArrayList<PolygonLevel>(), directExecutionTaskSize));393 }394 for (ForkJoinTask<List<PolygonLevel>> task : ForkJoinTask.invokeAll(tasks)) {395 List<PolygonLevel> res = task.join();396 if (res == null) {397 return null;398 }399 output.addAll(res);400 }401 return output;402 }403 }404 405 List<PolygonLevel> computeDirectly() {406 for (int i = from; i < to; i++) {407 if (processOuterWay(0, input, output, input.get(i)) == null) {408 return null;409 }410 }411 return output;412 }413 414 private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException {415 // Needed for Findbugs / Coverity because parent class is serializable416 ois.defaultReadObject();417 }418 419 private void writeObject(ObjectOutputStream oos) throws IOException {420 // Needed for Findbugs / Coverity because parent class is serializable421 oos.defaultWriteObject();422 }423 359 } 424 360 } -
src/org/openstreetmap/josm/data/osm/visitor/paint/RenderBenchmarkCollector.java
diff --git a/src/org/openstreetmap/josm/data/osm/visitor/paint/RenderBenchmarkCollector.java b/src/org/openstreetmap/josm/data/osm/visitor/paint/RenderBenchmarkCollector.java index 3acae15..3573347 100644
a b 2 2 package org.openstreetmap.josm.data.osm.visitor.paint; 3 3 4 4 import java.io.PrintStream; 5 import java.util. List;5 import java.util.Collection; 6 6 import java.util.function.Supplier; 7 7 8 8 import org.openstreetmap.josm.Main; … … public boolean renderSort() { 39 39 * @param allStyleElems All the elements that are painted. 40 40 * @return <code>true</code> if the renderer should continue to render 41 41 */ 42 public boolean renderDraw( List<StyleRecord> allStyleElems) {42 public boolean renderDraw(Collection<StyleRecord> allStyleElems) { 43 43 // nop 44 44 return true; 45 45 } … … public boolean renderSort() { 74 74 } 75 75 76 76 @Override 77 public boolean renderDraw( List<StyleRecord> allStyleElems) {77 public boolean renderDraw(Collection<StyleRecord> allStyleElems) { 78 78 timeSortingDone = System.currentTimeMillis(); 79 79 return super.renderDraw(allStyleElems); 80 80 } … … public void renderStart(double circum) { 126 126 } 127 127 128 128 @Override 129 public boolean renderDraw( List<StyleRecord> allStyleElems) {129 public boolean renderDraw(Collection<StyleRecord> allStyleElems) { 130 130 boolean res = super.renderDraw(allStyleElems); 131 131 outStream.print("phase 1 (calculate styles): " + Utils.getDurationString(timeSortingDone - timeStart)); 132 132 return res; -
src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java
diff --git a/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java b/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java index 55405c7..7f43efb 100644
a b 27 27 import java.awt.geom.Rectangle2D; 28 28 import java.util.ArrayList; 29 29 import java.util.Collection; 30 import java.util.Collections;31 30 import java.util.HashMap; 32 31 import java.util.Iterator; 33 32 import java.util.List; 34 33 import java.util.Map; 35 34 import java.util.NoSuchElementException; 35 import java.util.TreeSet; 36 36 import java.util.concurrent.ForkJoinPool; 37 import java.util.concurrent.ForkJoinTask;38 import java.util.concurrent.RecursiveTask;39 37 import java.util.function.Supplier; 38 import java.util.stream.Collectors; 39 import java.util.stream.Stream; 40 40 41 41 import javax.swing.AbstractButton; 42 42 import javax.swing.FocusManager; … … 45 45 import org.openstreetmap.josm.data.Bounds; 46 46 import org.openstreetmap.josm.data.coor.EastNorth; 47 47 import org.openstreetmap.josm.data.osm.BBox; 48 import org.openstreetmap.josm.data.osm.Changeset;49 48 import org.openstreetmap.josm.data.osm.DataSet; 50 49 import org.openstreetmap.josm.data.osm.Node; 51 50 import org.openstreetmap.josm.data.osm.OsmPrimitive; … … 54 53 import org.openstreetmap.josm.data.osm.RelationMember; 55 54 import org.openstreetmap.josm.data.osm.Way; 56 55 import org.openstreetmap.josm.data.osm.WaySegment; 57 import org.openstreetmap.josm.data.osm.visitor.Visitor;58 56 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon; 59 57 import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData; 60 58 import org.openstreetmap.josm.data.osm.visitor.paint.relations.MultipolygonCache; 61 59 import org.openstreetmap.josm.gui.NavigatableComponent; 62 60 import org.openstreetmap.josm.gui.mappaint.ElemStyles; 63 61 import org.openstreetmap.josm.gui.mappaint.MapPaintStyles; 64 import org.openstreetmap.josm.gui.mappaint.StyleElementList;65 62 import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource; 66 63 import org.openstreetmap.josm.gui.mappaint.styleelement.AreaElement; 67 64 import org.openstreetmap.josm.gui.mappaint.styleelement.BoxTextElement; … … 73 70 import org.openstreetmap.josm.gui.mappaint.styleelement.RepeatImageElement.LineImageAlignment; 74 71 import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement; 75 72 import org.openstreetmap.josm.gui.mappaint.styleelement.TextLabel; 76 import org.openstreetmap.josm.tools.CompositeList;77 73 import org.openstreetmap.josm.tools.Geometry; 78 74 import org.openstreetmap.josm.tools.Geometry.AreaAndPerimeter; 79 75 import org.openstreetmap.josm.tools.ImageProvider; 76 import org.openstreetmap.josm.tools.StreamUtils; 80 77 import org.openstreetmap.josm.tools.Utils; 81 78 82 79 /** … … public static int computeFlags(OsmPrimitive primitive, boolean checkOuterMember) 1779 1776 } 1780 1777 } 1781 1778 1782 private class ComputeStyleListWorker extends RecursiveTask<List<StyleRecord>> implements Visitor { 1783 private final transient List<? extends OsmPrimitive> input; 1784 private final transient List<StyleRecord> output; 1785 1779 private class ComputeStyleListWorker { 1786 1780 private final transient ElemStyles styles = MapPaintStyles.getStyles(); 1787 private final int directExecutionTaskSize;1788 1789 1781 private final boolean drawArea = circum <= Main.pref.getInteger("mappaint.fillareas", 10000000); 1790 1782 private final boolean drawMultipolygon = drawArea && Main.pref.getBoolean("mappaint.multipolygon", true); 1791 1783 private final boolean drawRestriction = Main.pref.getBoolean("mappaint.restriction", true); 1792 1784 1793 1785 /** 1794 1786 * Constructs a new {@code ComputeStyleListWorker}. 1795 * @param input the primitives to process1796 * @param output the list of styles to which styles will be added1797 * @param directExecutionTaskSize the threshold deciding whether to subdivide the tasks1798 1787 */ 1799 ComputeStyleListWorker(final List<? extends OsmPrimitive> input, List<StyleRecord> output, int directExecutionTaskSize) { 1800 this.input = input; 1801 this.output = output; 1802 this.directExecutionTaskSize = directExecutionTaskSize; 1788 ComputeStyleListWorker() { 1803 1789 this.styles.setDrawMultipolygon(drawMultipolygon); 1804 1790 } 1805 1791 1806 @Override 1807 protected List<StyleRecord> compute() { 1808 if (input.size() <= directExecutionTaskSize) { 1809 return computeDirectly(); 1810 } else { 1811 final Collection<ForkJoinTask<List<StyleRecord>>> tasks = new ArrayList<>(); 1812 for (int fromIndex = 0; fromIndex < input.size(); fromIndex += directExecutionTaskSize) { 1813 final int toIndex = Math.min(fromIndex + directExecutionTaskSize, input.size()); 1814 final List<StyleRecord> output = new ArrayList<>(directExecutionTaskSize); 1815 tasks.add(new ComputeStyleListWorker(input.subList(fromIndex, toIndex), output, directExecutionTaskSize).fork()); 1816 } 1817 for (ForkJoinTask<List<StyleRecord>> task : tasks) { 1818 output.addAll(task.join()); 1819 } 1820 return output; 1821 } 1822 } 1823 1824 public List<StyleRecord> computeDirectly() { 1825 MapCSSStyleSource.STYLE_SOURCE_LOCK.readLock().lock(); 1826 try { 1827 for (final OsmPrimitive osm : input) { 1828 if (osm.isDrawable()) { 1829 osm.accept(this); 1830 } 1831 } 1832 return output; 1833 } finally { 1834 MapCSSStyleSource.STYLE_SOURCE_LOCK.readLock().unlock(); 1835 } 1836 } 1837 1838 @Override 1839 public void visit(Node n) { 1840 add(n, computeFlags(n, false)); 1841 } 1842 1843 @Override 1844 public void visit(Way w) { 1845 add(w, computeFlags(w, true)); 1846 } 1847 1848 @Override 1849 public void visit(Relation r) { 1850 add(r, computeFlags(r, true)); 1851 } 1852 1853 @Override 1854 public void visit(Changeset cs) { 1855 throw new UnsupportedOperationException(); 1856 } 1857 1858 public void add(Node osm, int flags) { 1859 StyleElementList sl = styles.get(osm, circum, nc); 1860 for (StyleElement s : sl) { 1861 output.add(new StyleRecord(s, osm, flags)); 1862 } 1863 } 1864 1865 public void add(Relation osm, int flags) { 1866 StyleElementList sl = styles.get(osm, circum, nc); 1867 for (StyleElement s : sl) { 1868 if (drawMultipolygon && drawArea && s instanceof AreaElement && (flags & FLAG_DISABLED) == 0) { 1869 output.add(new StyleRecord(s, osm, flags)); 1870 } else if (drawRestriction && s instanceof NodeElement) { 1871 output.add(new StyleRecord(s, osm, flags)); 1872 } 1873 } 1874 } 1875 1876 public void add(Way osm, int flags) { 1877 StyleElementList sl = styles.get(osm, circum, nc); 1878 for (StyleElement s : sl) { 1879 if (!(drawArea && (flags & FLAG_DISABLED) == 0) && s instanceof AreaElement) { 1880 continue; 1881 } 1882 output.add(new StyleRecord(s, osm, flags)); 1883 } 1792 Stream<StyleRecord> computeNodeStyle(Node osm) { 1793 final int flags = computeFlags(osm, false); 1794 return StreamUtils.toStream(styles.get(osm, circum, nc)) 1795 .map(s -> new StyleRecord(s, osm, flags)); 1796 } 1797 1798 Stream<StyleRecord> computeRelationStyle(Relation osm) { 1799 final int flags = computeFlags(osm, true); 1800 return StreamUtils.toStream(styles.get(osm, circum, nc)) 1801 .filter(s -> 1802 drawMultipolygon && drawArea && s instanceof AreaElement && (flags & FLAG_DISABLED) == 0 1803 || drawRestriction && s instanceof NodeElement 1804 ) 1805 .map(s -> new StyleRecord(s, osm, flags)); 1806 } 1807 1808 Stream<StyleRecord> computeWayStyle(Way osm) { 1809 final int flags = computeFlags(osm, true); 1810 return StreamUtils.toStream(styles.get(osm, circum, nc)) 1811 .filter(s -> !(!(drawArea && (flags & FLAG_DISABLED) == 0) && s instanceof AreaElement)) 1812 .map(s -> new StyleRecord(s, osm, flags)); 1884 1813 } 1885 1814 } 1886 1815 … … public void render(final DataSet data, boolean renderVirtualNodes, Bounds bounds 1909 1838 List<Way> ways = data.searchWays(bbox); 1910 1839 List<Relation> relations = data.searchRelations(bbox); 1911 1840 1912 final List<StyleRecord> allStyleElems = new ArrayList<>(nodes.size()+ways.size()+relations.size());1841 final Collection<StyleRecord> allStyleElems; 1913 1842 1914 1843 // Need to process all relations first. 1915 1844 // Reason: Make sure, ElemStyles.getStyleCacheWithRange is 1916 1845 // not called for the same primitive in parallel threads. 1917 1846 // (Could be synchronized, but try to avoid this for 1918 1847 // performance reasons.) 1919 THREAD_POOL.invoke(new ComputeStyleListWorker(relations, allStyleElems, 1920 Math.max(20, relations.size() / THREAD_POOL.getParallelism() / 3))); 1921 THREAD_POOL.invoke(new ComputeStyleListWorker(new CompositeList<>(nodes, ways), allStyleElems, 1922 Math.max(100, (nodes.size() + ways.size()) / THREAD_POOL.getParallelism() / 3))); 1848 MapCSSStyleSource.STYLE_SOURCE_LOCK.readLock().lock(); 1849 try { 1850 final ComputeStyleListWorker worker = new ComputeStyleListWorker(); 1851 // run parallel stream in THREAD_POOL, see https://stackoverflow.com/a/22269778/205629 1852 allStyleElems = THREAD_POOL.submit(() -> 1853 relations.parallelStream().filter(OsmPrimitive::isDrawable).flatMap(worker::computeRelationStyle) 1854 .collect(Collectors.toCollection(TreeSet::new))).get(); 1855 allStyleElems.addAll(THREAD_POOL.submit(() -> Stream.concat( 1856 nodes.parallelStream().filter(OsmPrimitive::isDrawable).flatMap(worker::computeNodeStyle), 1857 ways.parallelStream().filter(OsmPrimitive::isDrawable).flatMap(worker::computeWayStyle) 1858 ).collect(Collectors.toList())).get()); 1859 } catch (Exception ex) { 1860 throw new RuntimeException(ex); 1861 } finally { 1862 MapCSSStyleSource.STYLE_SOURCE_LOCK.readLock().unlock(); 1863 } 1923 1864 1924 1865 if (!benchmark.renderSort()) { 1925 1866 return; 1926 1867 } 1927 1868 1928 Collections.sort(allStyleElems); // TODO: try parallel sort when switching to Java 81929 1930 1869 if (!benchmark.renderDraw(allStyleElems)) { 1931 1870 return; 1932 1871 } -
test/performance/org/openstreetmap/josm/gui/mappaint/MapRendererPerformanceTest.java
diff --git a/test/performance/org/openstreetmap/josm/gui/mappaint/MapRendererPerformanceTest.java b/test/performance/org/openstreetmap/josm/gui/mappaint/MapRendererPerformanceTest.java index 8e5d8d5..aeae2cf 100644
a b 8 8 import java.io.IOException; 9 9 import java.io.InputStream; 10 10 import java.util.ArrayList; 11 import java.util.Collection; 11 12 import java.util.Collections; 12 13 import java.util.EnumMap; 13 14 import java.util.HashMap; … … public static void dumpElementCount(BenchmarkData bd) { 331 332 332 333 public static class BenchmarkData extends CapturingBenchmark { 333 334 334 private List<StyleRecord> allStyleElems;335 private Collection<StyleRecord> allStyleElems; 335 336 336 337 @Override 337 public boolean renderDraw( List<StyleRecord> allStyleElems) {338 public boolean renderDraw(Collection<StyleRecord> allStyleElems) { 338 339 this.allStyleElems = allStyleElems; 339 340 return super.renderDraw(allStyleElems); 340 341 }
