Changeset 7059 in josm


Ignore:
Timestamp:
2014-05-04T13:20:39+02:00 (10 years ago)
Author:
bastiK
Message:

mapcss: use multiple threads for style creation (see #9691)

significant speed-up on multi core processors
(factor ~3.5 on my 4 core Intel Core i7)

This makes a lot of code run parallel which wasn't written with
multithreading in mind, so keep fingers crossed. :)

Location:
trunk/src/org/openstreetmap/josm
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/visitor/paint/StyledMapRenderer.java

    r7043 r7059  
    3030import java.util.Iterator;
    3131import java.util.List;
     32import java.util.concurrent.Callable;
     33import java.util.concurrent.ExecutionException;
     34import java.util.concurrent.ExecutorService;
     35import java.util.concurrent.Executors;
     36import java.util.concurrent.Future;
    3237
    3338import javax.swing.AbstractButton;
     
    3843import org.openstreetmap.josm.data.coor.EastNorth;
    3944import org.openstreetmap.josm.data.osm.BBox;
     45import org.openstreetmap.josm.data.osm.Changeset;
    4046import org.openstreetmap.josm.data.osm.DataSet;
    4147import org.openstreetmap.josm.data.osm.Node;
     
    4652import org.openstreetmap.josm.data.osm.Way;
    4753import org.openstreetmap.josm.data.osm.WaySegment;
     54import org.openstreetmap.josm.data.osm.visitor.Visitor;
    4855import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
    4956import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon.PolyData;
     
    7279public class StyledMapRenderer extends AbstractMapRenderer {
    7380
     81    final public static int noThreads;
     82    final public static ExecutorService styleCreatorPool;
     83   
     84    static {
     85        noThreads = Runtime.getRuntime().availableProcessors();
     86        styleCreatorPool = Executors.newFixedThreadPool(noThreads);
     87    }
     88
    7489    /**
    7590     * Iterates over a list of Way Nodes and returns screen coordinates that
     
    169184        public void remove() {
    170185            throw new UnsupportedOperationException();
    171         }
    172     }
    173 
    174     private class StyleCollector {
    175         private final boolean drawArea;
    176         private final boolean drawMultipolygon;
    177         private final boolean drawRestriction;
    178 
    179         private final List<StyleRecord> styleElems;
    180 
    181         public StyleCollector(boolean drawArea, boolean drawMultipolygon, boolean drawRestriction) {
    182             this.drawArea = drawArea;
    183             this.drawMultipolygon = drawMultipolygon;
    184             this.drawRestriction = drawRestriction;
    185             styleElems = new ArrayList<>();
    186         }
    187 
    188         public void add(Node osm, int flags) {
    189             StyleList sl = styles.get(osm, circum, nc);
    190             for (ElemStyle s : sl) {
    191                 styleElems.add(new StyleRecord(s, osm, flags));
    192             }
    193         }
    194 
    195         public void add(Relation osm, int flags) {
    196             StyleList sl = styles.get(osm, circum, nc);
    197             for (ElemStyle s : sl) {
    198                 if (drawMultipolygon && drawArea && s instanceof AreaElemStyle && (flags & FLAG_DISABLED) == 0) {
    199                     styleElems.add(new StyleRecord(s, osm, flags));
    200                 } else if (drawRestriction && s instanceof NodeElemStyle) {
    201                     styleElems.add(new StyleRecord(s, osm, flags));
    202                 }
    203             }
    204         }
    205 
    206         public void add(Way osm, int flags) {
    207             StyleList sl = styles.get(osm, circum, nc);
    208             for (ElemStyle s : sl) {
    209                 if (!(drawArea && (flags & FLAG_DISABLED) == 0) && s instanceof AreaElemStyle) {
    210                     continue;
    211                 }
    212                 styleElems.add(new StyleRecord(s, osm, flags));
    213             }
    214         }
    215 
    216         public void drawAll() {
    217             Collections.sort(styleElems);
    218             for (StyleRecord r : styleElems) {
    219                 r.style.paintPrimitive(
    220                         r.osm,
    221                         paintSettings,
    222                         StyledMapRenderer.this,
    223                         (r.flags & FLAG_SELECTED) != 0,
    224                         (r.flags & FLAG_MEMBER_OF_SELECTED) != 0
    225                 );
    226             }
    227186        }
    228187    }
     
    369328    }
    370329
    371     private void collectNodeStyles(DataSet data, StyleCollector sc, BBox bbox) {
    372         for (final Node n: data.searchNodes(bbox)) {
    373             if (n.isDrawable()) {
    374                 if (n.isDisabled()) {
    375                     sc.add(n, FLAG_DISABLED);
    376                 } else if (data.isSelected(n)) {
    377                     sc.add(n, FLAG_SELECTED);
    378                 } else if (n.isMemberOfSelected()) {
    379                     sc.add(n, FLAG_MEMBER_OF_SELECTED);
    380                 } else {
    381                     sc.add(n, FLAG_NORMAL);
    382                 }
    383             }
    384         }
    385     }
    386 
    387     private void collectWayStyles(DataSet data, StyleCollector sc, BBox bbox) {
    388         for (final Way w : data.searchWays(bbox)) {
    389             if (w.isDrawable()) {
    390                 if (w.isDisabled()) {
    391                     sc.add(w, FLAG_DISABLED);
    392                 } else if (data.isSelected(w)) {
    393                     sc.add(w, FLAG_SELECTED);
    394                 } else if (w.isMemberOfSelected()) {
    395                     sc.add(w, FLAG_MEMBER_OF_SELECTED);
    396                 } else {
    397                     sc.add(w, FLAG_NORMAL);
    398                 }
    399             }
    400         }
    401     }
    402 
    403     private void collectRelationStyles(DataSet data, StyleCollector sc, BBox bbox) {
    404         for (Relation r: data.searchRelations(bbox)) {
    405             if (r.isDrawable()) {
    406                 if (r.isDisabled()) {
    407                     sc.add(r, FLAG_DISABLED);
    408                 } else if (data.isSelected(r)) {
    409                     sc.add(r, FLAG_SELECTED);
    410                 } else {
    411                     sc.add(r, FLAG_NORMAL);
    412                 }
    413             }
    414         }
    415     }
    416 
    417330    private void displaySegments(GeneralPath path, GeneralPath orientationArrows, GeneralPath onewayArrows, GeneralPath onewayArrowsCasing,
    418331            Color color, BasicStroke line, BasicStroke dashes, Color dashedColor) {
     
    13901303        return null;
    13911304    }
    1392 
     1305   
    13931306    @Override
    13941307    public void render(final DataSet data, boolean renderVirtualNodes, Bounds bounds) {
     
    13961309        getSettings(renderVirtualNodes);
    13971310
    1398         boolean drawArea = circum <= Main.pref.getInteger("mappaint.fillareas", 10000000);
    1399         boolean drawMultipolygon = drawArea && Main.pref.getBoolean("mappaint.multipolygon", true);
    1400         boolean drawRestriction = Main.pref.getBoolean("mappaint.restriction", true);
     1311        final boolean drawArea = circum <= Main.pref.getInteger("mappaint.fillareas", 10000000);
     1312        final boolean drawMultipolygon = drawArea && Main.pref.getBoolean("mappaint.multipolygon", true);
     1313        final boolean drawRestriction = Main.pref.getBoolean("mappaint.restriction", true);
    14011314
    14021315        styles = MapPaintStyles.getStyles();
     
    14121325        }
    14131326
    1414         StyleCollector sc = new StyleCollector(drawArea, drawMultipolygon, drawRestriction);
    1415         collectNodeStyles(data, sc, bbox);
    1416         collectWayStyles(data, sc, bbox);
    1417         collectRelationStyles(data, sc, bbox);
     1327        class ComputeStyleListWorker implements Callable<List<StyleRecord>>, Visitor {
     1328            private final List<StyleRecord> styleList;
     1329            private final List<? extends OsmPrimitive> input;
     1330            private final int from;
     1331            private final int to;
     1332
     1333            /**
     1334             * Constructor for CreateStyleRecordsWorker.
     1335             * @param input the primitives to process
     1336             * @param from first index of <code>input</code> to use
     1337             * @param to last index + 1
     1338             */
     1339            public ComputeStyleListWorker(List<? extends OsmPrimitive> input, int from, int to) {
     1340                this.styleList = new ArrayList<>(to - from);
     1341                this.input = input;
     1342                this.from = from;
     1343                this.to = to;
     1344            }
     1345           
     1346            @Override
     1347            public List<StyleRecord> call() throws Exception {
     1348                for (int i = from; i<to; i++) {
     1349                    OsmPrimitive osm = input.get(i);
     1350                    if (osm.isDrawable()) {
     1351                        osm.accept(this);
     1352                    }
     1353                }
     1354                return styleList;
     1355            }
     1356           
     1357            @Override
     1358            public void visit(Node n) {
     1359                if (n.isDisabled()) {
     1360                    add(n, FLAG_DISABLED);
     1361                } else if (data.isSelected(n)) {
     1362                    add(n, FLAG_SELECTED);
     1363                } else if (n.isMemberOfSelected()) {
     1364                    add(n, FLAG_MEMBER_OF_SELECTED);
     1365                } else {
     1366                    add(n, FLAG_NORMAL);
     1367                }
     1368            }
     1369
     1370            @Override
     1371            public void visit(Way w) {
     1372                if (w.isDisabled()) {
     1373                    add(w, FLAG_DISABLED);
     1374                } else if (data.isSelected(w)) {
     1375                    add(w, FLAG_SELECTED);
     1376                } else if (w.isMemberOfSelected()) {
     1377                    add(w, FLAG_MEMBER_OF_SELECTED);
     1378                } else {
     1379                    add(w, FLAG_NORMAL);
     1380                }
     1381            }
     1382
     1383            @Override
     1384            public void visit(Relation r) {
     1385                if (r.isDisabled()) {
     1386                    add(r, FLAG_DISABLED);
     1387                } else if (data.isSelected(r)) {
     1388                    add(r, FLAG_SELECTED);
     1389                } else {
     1390                    add(r, FLAG_NORMAL);
     1391                }
     1392            }
     1393
     1394            @Override
     1395            public void visit(Changeset cs) {
     1396                throw new UnsupportedOperationException();
     1397            }
     1398
     1399            public void add(Node osm, int flags) {
     1400                StyleList sl = styles.get(osm, circum, nc);
     1401                for (ElemStyle s : sl) {
     1402                    styleList.add(new StyleRecord(s, osm, flags));
     1403                }
     1404            }
     1405
     1406            public void add(Relation osm, int flags) {
     1407                StyleList sl = styles.get(osm, circum, nc);
     1408                for (ElemStyle s : sl) {
     1409                    if (drawMultipolygon && drawArea && s instanceof AreaElemStyle && (flags & FLAG_DISABLED) == 0) {
     1410                        styleList.add(new StyleRecord(s, osm, flags));
     1411                    } else if (drawRestriction && s instanceof NodeElemStyle) {
     1412                        styleList.add(new StyleRecord(s, osm, flags));
     1413                    }
     1414                }
     1415            }
     1416
     1417            public void add(Way osm, int flags) {
     1418                StyleList sl = styles.get(osm, circum, nc);
     1419                for (ElemStyle s : sl) {
     1420                    if (!(drawArea && (flags & FLAG_DISABLED) == 0) && s instanceof AreaElemStyle) {
     1421                        continue;
     1422                    }
     1423                    styleList.add(new StyleRecord(s, osm, flags));
     1424                }
     1425            }
     1426        }
     1427
     1428        final List<ComputeStyleListWorker> tasks = new ArrayList<>();
     1429        final List<StyleRecord> allStyleElems = new ArrayList<>();
     1430       
     1431        class ConcurrentTasksHelper {
     1432            void createTasks(List<? extends OsmPrimitive> prims) {
     1433                int bucketsize = Math.max(100, prims.size()/noThreads/3);
     1434                for (int i=0; i*bucketsize < prims.size(); i++) {
     1435                    tasks.add(new ComputeStyleListWorker(prims, i*bucketsize, Math.min((i+1)*bucketsize, prims.size())));
     1436                }
     1437            }
     1438           
     1439            void runIt() {
     1440                if (tasks.size() == 1) {
     1441                    try {
     1442                        allStyleElems.addAll(tasks.get(0).call());
     1443                    } catch (Exception ex) {
     1444                        throw new RuntimeException(ex);
     1445                    }
     1446                } else if (tasks.size() > 1) {
     1447                    try {
     1448                        for (Future<List<StyleRecord>> future : styleCreatorPool.invokeAll(tasks)) {
     1449                                allStyleElems.addAll(future.get());
     1450                        }
     1451                    } catch (InterruptedException | ExecutionException ex) {
     1452                        throw new RuntimeException(ex);
     1453                    }
     1454                }
     1455            }
     1456        }
     1457        ConcurrentTasksHelper helper = new ConcurrentTasksHelper();
     1458       
     1459        // Need to process all relations first.
     1460        // Reason: Make sure, ElemStyles.getStyleCacheWithRange is
     1461        // not called for the same primtive in parallel threads.
     1462        // (Could be synchronized, but try to avoid this for
     1463        // performance reasons.)
     1464        helper.createTasks(data.searchRelations(bbox));
     1465        helper.runIt();
     1466       
     1467        tasks.clear();
     1468        helper.createTasks(data.searchNodes(bbox));
     1469        helper.createTasks(data.searchWays(bbox));
     1470        helper.runIt();
     1471       
    14181472       
    14191473        if (Main.isTraceEnabled()) {
     
    14221476        }
    14231477       
    1424         sc.drawAll();
    1425         sc = null;
     1478        Collections.sort(allStyleElems);
     1479       
     1480        for (StyleRecord r : allStyleElems) {
     1481            r.style.paintPrimitive(
     1482                    r.osm,
     1483                    paintSettings,
     1484                    StyledMapRenderer.this,
     1485                    (r.flags & FLAG_SELECTED) != 0,
     1486                    (r.flags & FLAG_MEMBER_OF_SELECTED) != 0
     1487            );
     1488        }
    14261489
    14271490        if (Main.isTraceEnabled()) {
  • trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyle.java

    r7005 r7059  
    8686     * Two preference values and the set of created fonts are cached in order to avoid
    8787     * expensive lookups and to avoid too many font objects
    88      * (in analogy to flyweight pattern).
    8988     *
    9089     * FIXME: cached preference values are not updated if the user changes them during
    9190     * a JOSM session. Should have a listener listening to preference changes.
    9291     */
    93     private static String DEFAULT_FONT_NAME = null;
    94     private static Float DEFAULT_FONT_SIZE = null;
    95     private static void initDefaultFontParameters() {
    96         if (DEFAULT_FONT_NAME != null) return; // already initialized - skip initialization
    97         DEFAULT_FONT_NAME = Main.pref.get("mappaint.font", "Helvetica");
    98         DEFAULT_FONT_SIZE = (float) Main.pref.getInteger("mappaint.fontsize", 8);
     92    private static volatile String DEFAULT_FONT_NAME = null;
     93    private static volatile Float DEFAULT_FONT_SIZE = null;
     94    private static final Object lock = new Object();
     95   
     96    // thread save access (double-checked locking)
     97    private static Float getDefaultFontSize() {
     98        Float s = DEFAULT_FONT_SIZE;
     99        if (s == null) {
     100            synchronized (lock) {
     101                s = DEFAULT_FONT_SIZE;
     102                if (s == null) {
     103                    DEFAULT_FONT_SIZE = s = (float) Main.pref.getInteger("mappaint.fontsize", 8);
     104                }
     105            }
     106        }
     107        return s;
     108    }
     109
     110    private static String getDefaultFontName() {
     111        String n = DEFAULT_FONT_NAME;
     112        if (n == null) {
     113            synchronized (lock) {
     114                n = DEFAULT_FONT_NAME;
     115                if (n == null) {
     116                    DEFAULT_FONT_NAME = n = Main.pref.get("mappaint.font", "Helvetica");
     117                }
     118            }
     119        }
     120        return n;
    99121    }
    100122
     
    155177
    156178    protected static Font getFont(Cascade c) {
    157         initDefaultFontParameters(); // populated cached preferences, if necessary
    158         String name = c.get("font-family", DEFAULT_FONT_NAME, String.class);
    159         float size = c.get("font-size", DEFAULT_FONT_SIZE, Float.class);
     179        String name = c.get("font-family", getDefaultFontName(), String.class);
     180        float size = c.get("font-size", getDefaultFontSize(), Float.class);
    160181        int weight = Font.PLAIN;
    161182        if ("bold".equalsIgnoreCase(c.get("font-weight", null, String.class))) {
  • trunk/src/org/openstreetmap/josm/gui/mappaint/ElemStyles.java

    r7014 r7059  
    192192
    193193                    if (!hasIndependentLineStyle) {
    194                         Pair<StyleList, Range> mpElemStyles = getStyleCacheWithRange(r, scale, nc);
     194                        Pair<StyleList, Range> mpElemStyles;
     195                        synchronized(r) {
     196                            mpElemStyles = getStyleCacheWithRange(r, scale, nc);
     197                        }
    195198                        ElemStyle mpLine = null;
    196199                        for (ElemStyle s : mpElemStyles.a) {
     
    249252                    }
    250253                    if (!hasIndependentElemStyle && !multipolygon.getOuterWays().isEmpty()) {
    251                         StyleList mpElemStyles = get(ref, scale, nc);
    252254                        Color mpColor = null;
     255                        StyleList mpElemStyles = null;
     256                        synchronized (ref) {
     257                            mpElemStyles = get(ref, scale, nc);
     258                        }
    253259                        for (ElemStyle mpS : mpElemStyles) {
    254260                            if (mpS instanceof AreaElemStyle) {
Note: See TracChangeset for help on using the changeset viewer.