From 6943276357bed2b7c5e2919d6e9f80d7f226ee4b Mon Sep 17 00:00:00 2001 From: geoneutrino Date: Wed, 2 Sep 2026 17:07:41 +0200 Subject: [PATCH] Avoid redundant work when writing features Two pre-existing redundancies in the tile write path, neither of which changes what is written: - writeMultiLinestring() called scaleLatpLon() twice for every point, once to count the distinct points and once to emit them. Scale each point once into a reused buffer instead. - The multipoint vector was allocated per point feature. Reuse a thread_local buffer, as scaledMultiPolygon already does. Verified byte-identical to the parent commit on a Bangladesh extract (93930 tiles, 25544140 features). The comparison has to run with --threads 1: with several threads tilemaker's own output varies by a feature or two between runs of the same binary, because addObjectToSmallIndex() falls back to a per-thread pending list under lock contention. --- src/tile_worker.cpp | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/tile_worker.cpp b/src/tile_worker.cpp index 30d09ebe..222b626c 100644 --- a/src/tile_worker.cpp +++ b/src/tile_worker.cpp @@ -12,6 +12,9 @@ extern bool verbose; thread_local bool enabledUserSignal = false; thread_local MultiPolygon scaledMultiPolygon; +// Reused scratch buffers, so writing a feature does not allocate per feature. +thread_local std::vector> scaledLinestring; +thread_local std::vector> multipointBuffer; typedef std::vector::const_iterator OutputObjectsConstIt; typedef std::pair OutputObjectsConstItPair; @@ -138,9 +141,14 @@ void writeMultiLinestring( // vtzero dislikes linesegments that have zero-length segments, // e.g. where p(x) == p(x + 1). So filter those out. + // Both passes below need the same tile coordinates, so scale each point + // once into a reused buffer rather than calling scaleLatpLon() twice. + scaledLinestring.clear(); + scaledLinestring.reserve(ls.size()); int points = 0; for (const Point& p : ls) { pair xy = bbox.scaleLatpLon(p.get<1>(), p.get<0>()); + scaledLinestring.push_back(xy); if (points == 0 || xy != lastXy) { points++; lastXy = xy; @@ -154,8 +162,7 @@ void writeMultiLinestring( hadLine = true; fbuilder.add_linestring(points); bool firstPoint = true; - for (const Point& p : ls) { - pair xy = bbox.scaleLatpLon(p.get<1>(), p.get<0>()); + for (const pair& xy : scaledLinestring) { if (firstPoint || xy != lastXy) { // vtzero doesn't like linesegments with zero-length segments, // so filter those out @@ -324,7 +331,8 @@ void ProcessObjects( // The very first point; below we check if there are more compatible points // so that we can write a multipoint instead of many point features - std::vector> multipoint; + multipointBuffer.clear(); + std::vector>& multipoint = multipointBuffer; const int tile_extent = bbox.hires ? 8192 : 4096; const int margin = 256; // Just in case something happens near the edges.