Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Slow rendering on low zoom #3534

Open
rearden-steel opened this issue Nov 28, 2018 · 16 comments
Open

Slow rendering on low zoom #3534

rearden-steel opened this issue Nov 28, 2018 · 16 comments

Comments

@rearden-steel
Copy link

Expected behavior

Actual behavior

Slow rendering on the latest version on full planet.
Slow query:

explain analyze SELECT ST_AsBinary("way") AS geom,"feature","way_pixels" FROM (SELECT
            way, name, way_pixels, religion,
            COALESCE(wetland, landuse, "natural") AS feature
          FROM (SELECT
              way, COALESCE(name, '') AS name, religion,
              ('landuse_' || (CASE WHEN landuse IN ('forest', 'farmland', 'residential', 'commercial', 'retail', 'industrial',
                                                    'meadow', 'vineyard', 'orchard') THEN landuse ELSE NULL END)) AS landuse,
              ('natural_' || (CASE WHEN "natural" IN ('wood', 'sand', 'scree', 'shingle', 'bare_rock', 'heath', 'grassland', 'scrub') THEN "natural" ELSE NULL END)) AS "natural",
              ('wetland_' || (CASE WHEN "natural" IN ('wetland', 'mud') THEN (CASE WHEN "natural" IN ('mud') THEN "natural" ELSE tags->'wetland' END) ELSE NULL END)) AS wetland,
              way_area/NULLIF(2445.98::real*2445.98::real,0) AS way_pixels,
              way_area
            FROM planet_osm_polygon
            WHERE (landuse IN ('forest', 'farmland', 'residential', 'commercial', 'retail', 'industrial', 'meadow', 'vineyard', 'orchard')
              OR "natural" IN ('wood', 'wetland', 'mud', 'sand', 'scree', 'shingle', 'bare_rock', 'heath', 'grassland', 'scrub'))
              AND way_area > 0.01*2445.98::real*2445.98::real
              AND building IS NULL
          ) AS features
          ORDER BY way_area DESC, feature
        ) AS landcover_low_zoom WHERE "way" && ST_SetSRID('BOX3D(-626172.1357124997 -626172.1357124997,10644926.3071125 10644926.3071125)'::box3d, 3857);


Subquery Scan on landcover_low_zoom  (cost=9467430.08..9479321.99 rows=78404 width=68) (actual time=39172.202..99529.120 rows=6726182 loops=1)
  ->  Gather Merge  (cost=9467430.08..9476577.85 rows=78404 width=320) (actual time=39031.807..49451.054 rows=6726182 loops=1)
        Workers Planned: 2
        Workers Launched: 2
        ->  Sort  (cost=9466430.05..9466528.06 rows=39202 width=320) (actual time=38823.573..40224.295 rows=2242061 loops=3)
              Sort Key: planet_osm_polygon.way_area DESC, (COALESCE(('wetland_'::text || CASE WHEN (planet_osm_polygon."natural" = ANY ('{wetland,mud}'::text[])) THEN CASE WHEN (planet_osm_polygon."natural" = 'mud'::text) THEN planet_osm_polygon."natural" ELSE (planet_osm_polygon.tags -> 'wetland'::text) END ELSE NULL::text END), ('landuse_'::text || CASE WHEN (planet_osm_polygon.landuse = ANY ('{forest,farmland,residential,commercial,retail,industrial,meadow,vineyard,orchard}'::text[])) THEN planet_osm_polygon.landuse ELSE NULL::text END), ('natural_'::text || CASE WHEN (planet_osm_polygon."natural" = ANY ('{wood,sand,scree,shingle,bare_rock,heath,grassland,scrub}'::text[])) THEN planet_osm_polygon."natural" ELSE NULL::text END)))
              Sort Method: external merge  Disk: 1989280kB
              ->  Parallel Bitmap Heap Scan on planet_osm_polygon  (cost=294234.59..9463439.21 rows=39202 width=320) (actual time=6978.355..30797.830 rows=2242061 loops=3)
                    Recheck Cond: ((way && '01030000A0110F00000100000005000000D81B7C45F81B23C1D81B7C45F81B23C10000000000000000D81B7C45F81B23C198DDD3C9B74D6441000000000000000098DDD3C9B74D644198DDD3C9B74D6441000000000000000098DDD3C9B74D6441D81B7C45F81B23C10000000000000000D81B7C45F81B23C1D81B7C45F81B23C10000000000000000'::geometry) AND (way_area > '23300'::double precision))
                    Filter: ((building IS NULL) AND (way_area > '59828.1806485391'::double precision) AND ((landuse = ANY ('{forest,farmland,residential,commercial,retail,industrial,meadow,vineyard,orchard}'::text[])) OR ("natural" = ANY ('{wood,wetland,mud,sand,scree,shingle,bare_rock,heath,grassland,scrub}'::text[]))))
                    Rows Removed by Filter: 2178129
                    Heap Blocks: exact=1296998
                    ->  Bitmap Index Scan on planet_osm_polygon_way_area_z10  (cost=0.00..294211.07 rows=13419447 width=0) (actual time=4470.495..4470.495 rows=13260568 loops=1)
                          Index Cond: (way && '01030000A0110F00000100000005000000D81B7C45F81B23C1D81B7C45F81B23C10000000000000000D81B7C45F81B23C198DDD3C9B74D6441000000000000000098DDD3C9B74D644198DDD3C9B74D6441000000000000000098DDD3C9B74D6441D81B7C45F81B23C10000000000000000D81B7C45F81B23C1D81B7C45F81B23C10000000000000000'::geometry)
Planning time: 0.834 ms
Execution time: 101487.170 ms
(16 rows)

Links and screenshots illustrating the problem

@kocio-pl
Copy link
Collaborator

Hi, thanks for the report. Low zoom rendering slowdown is expected side effect of #3458 probably. I'm not sure if we can do anything about it. Do you have any idea?

@kocio-pl kocio-pl changed the title Slow rendering Slow rendering on low zoom Nov 28, 2018
@rearden-steel
Copy link
Author

Which version does not have this performance regression? I'll try to downgrade and test.

@rearden-steel
Copy link
Author

Another slow query detected:

SELECT ST_AsBinary("way") AS geom,"int_intermittent","landuse","natural","waterway","way_pixels" FROM (SELECT
            way,
            "natural",
            waterway,
            landuse,
            name,
            way_area/NULLIF(2445.98::real*2445.98::real,0) AS way_pixels,
            CASE WHEN tags->'intermittent' IN ('yes')
              OR tags->'seasonal' IN ('yes', 'spring', 'summer', 'autumn', 'winter', 'wet_season', 'dry_season')
              THEN 'yes' ELSE 'no' END AS int_intermittent
          FROM planet_osm_polygon
          WHERE
            (waterway IN ('dock', 'riverbank')
              OR landuse IN ('reservoir', 'basin')
              OR "natural" IN ('water', 'glacier'))
            AND building IS NULL
            AND way_area > 1*2445.98::real*2445.98::real
          ORDER BY COALESCE(layer,0), way_area DESC
        ) AS water_areas WHERE "way" && ST_SetSRID('BOX3D(-626172.1357124997 -626172.1357124997,10644926.3071125 10644926.3071125)'::box3d, 3857)

@kocio-pl
Copy link
Collaborator

It was merged in the latest version, so v4.16.0 should be much better in this respect.

Could you also check if there is any difference in speed without AND building IS NULL line? There are no buildings on low zooms, so at least this should be removed to speed up a bit, I guess.

@rearden-steel
Copy link
Author

Same

 Subquery Scan on landcover_low_zoom  (cost=9138117.92..9216093.64 rows=514098 width=68) (actual time=111080.940..160803.317 rows=6726331 loops=1)
   ->  Gather Merge  (cost=9138117.92..9198100.21 rows=514098 width=322) (actual time=111079.982..120744.601 rows=6726331 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Sort  (cost=9137117.90..9137760.52 rows=257049 width=322) (actual time=111000.217..111808.478 rows=2242110 loops=3)
               Sort Key: planet_osm_polygon.way_area DESC, (COALESCE(('wetland_'::text || CASE WHEN (planet_osm_polygon."natural" = ANY ('{wetland,mud}'::text[])) THEN CASE WHEN (planet_osm_polygon."natural" = 'mud'::text) THEN planet_osm_polygon."natural" ELSE (planet_osm_polygon.tags -> 'wetland'::text) END ELSE NULL::text END), ('landuse_'::text || CASE WHEN (planet_osm_polygon.landuse = ANY ('{forest,farmland,residential,commercial,retail,industrial,meadow,vineyard,orchard}'::text[])) THEN planet_osm_polygon.landuse ELSE NULL::text END), ('natural_'::text || CASE WHEN (planet_osm_polygon."natural" = ANY ('{wood,sand,scree,shingle,bare_rock,heath,grassland,scrub}'::text[])) THEN planet_osm_polygon."natural" ELSE NULL::text END)))
               Sort Method: quicksort  Memory: 2977536kB
               ->  Parallel Bitmap Heap Scan on planet_osm_polygon  (cost=268310.16..9114019.88 rows=257049 width=322) (actual time=15107.299..106185.400 rows=2242110 loops=3)
                     Recheck Cond: ((way && '01030000A0110F00000100000005000000D81B7C45F81B23C1D81B7C45F81B23C10000000000000000D81B7C45F81B23C198DDD3C9B74D6441000000000000000098DDD3C9B74D644198DDD3C9B74D6441000000000000000098DDD3C9B74D6441D81B7C45F81B23C10000000000000000D81B7C45F81B23C1D81B7C45F81B23C10000000000000000'::geometry) AND (way_area > '23300'::double precision))
                     Filter: ((way_area > '59828.1806485391'::double precision) AND ((landuse = ANY ('{forest,farmland,residential,commercial,retail,industrial,meadow,vineyard,orchard}'::text[])) OR ("natural" = ANY ('{wood,wetland,mud,sand,scree,shingle,bare_rock,heath,grassland,scrub}'::text[]))))
                     Rows Removed by Filter: 2178079
                     Heap Blocks: exact=1291624
                     ->  Bitmap Index Scan on planet_osm_polygon_way_area_z10  (cost=0.00..268155.93 rows=13014469 width=0) (actual time=12102.114..12102.114 rows=13260568 loops=1)
                           Index Cond: (way && '01030000A0110F00000100000005000000D81B7C45F81B23C1D81B7C45F81B23C10000000000000000D81B7C45F81B23C198DDD3C9B74D6441000000000000000098DDD3C9B74D644198DDD3C9B74D6441000000000000000098DDD3C9B74D6441D81B7C45F81B23C10000000000000000D81B7C45F81B23C1D81B7C45F81B23C10000000000000000'::geometry)
 Planning time: 5.482 ms
 Execution time: 162487.065 ms
(16 rows)

@kocio-pl
Copy link
Collaborator

In water_areas the slowdown might be the result of rendering glaciers earlier (z5+ instead of z8+).

@kocio-pl
Copy link
Collaborator

Same

What changes you were testing?

Have you ever tried rendering this style sheet before, especially low zooms? What is the deployment you are testing?

@rearden-steel
Copy link
Author

I mean the same result without AND building IS NULL line.
No, I have not tried this stylesheet before. We had an old deployment and decided to make a new one with up-to-date software and hi-res maps.
Postgis: mdillon/postgis:10
Settings:

listen_addresses = '*'
max_connections = 300                   # (change requires restart)
shared_buffers = 24GB                   # min 128kB
work_mem = 6GB                          # min 64kB
maintenance_work_mem = 4096MB           # min 1MB
dynamic_shared_memory_type = posix      # the default is the first option
effective_io_concurrency = 30           # 1-1000; 0 disables prefetching
fsync = off                             # flush data to disk for crash safety
synchronous_commit = off                # synchronization level;
wal_buffers = 16MB                      # min 32kB, -1 sets based on shared_buffers
checkpoint_timeout = 30min              # range 30s-1d
max_wal_size = 16GB
random_page_cost = 1.0                  # same scale as above
effective_cache_size = 30GB
default_statistics_target = 1000        # range 1-10000
log_min_duration_statement = 10000      # -1 is disabled, 0 logs all statements
log_temp_files = 0                      # log temporary files equal or larger
log_timezone = 'UTC'
autovacuum = on                 # Enable autovacuum subprocess?  'on'
datestyle = 'iso, mdy'
timezone = 'UTC'
lc_messages = 'en_US.utf8'                      # locale for system error message
lc_monetary = 'en_US.utf8'                      # locale for monetary formatting
lc_numeric = 'en_US.utf8'                       # locale for number formatting
lc_time = 'en_US.utf8'                          # locale for time formatting
default_text_search_config = 'pg_catalog.english'
root@tile1:~/tile-srv# egrep -v "^$|^\s*\#|^\#" postgis/postgresql.conf
listen_addresses = '*'
max_connections = 300                   # (change requires restart)
shared_buffers = 24GB                   # min 128kB
work_mem = 6GB                          # min 64kB
maintenance_work_mem = 4096MB           # min 1MB
dynamic_shared_memory_type = posix      # the default is the first option
effective_io_concurrency = 30           # 1-1000; 0 disables prefetching
fsync = off                             # flush data to disk for crash safety
synchronous_commit = off                # synchronization level;
wal_buffers = 16MB                      # min 32kB, -1 sets based on shared_buffers
checkpoint_timeout = 30min              # range 30s-1d
max_wal_size = 16GB
random_page_cost = 1.0                  # same scale as above
effective_cache_size = 30GB
default_statistics_target = 1000        # range 1-10000
log_min_duration_statement = 10000      # -1 is disabled, 0 logs all statements
log_temp_files = 0                      # log temporary files equal or larger
log_timezone = 'UTC'
autovacuum = on                 # Enable autovacuum subprocess?  'on'
datestyle = 'iso, mdy'
timezone = 'UTC'
lc_messages = 'en_US.utf8'                      # locale for system error message
lc_monetary = 'en_US.utf8'                      # locale for monetary formatting
lc_numeric = 'en_US.utf8'                       # locale for number formatting
lc_time = 'en_US.utf8'                          # locale for time formatting
default_text_search_config = 'pg_catalog.english'

@kocio-pl
Copy link
Collaborator

Primary deployment of this style is default map layer on OSM.org. They render zoom levels z0-z12 only about every 2 weeks on the weekend because it's a big task performance wise. Rendering natural areas from z5 makes things worse, but with their rendering schedule it's still acceptable, because it takes only few hours per month, the main feedback loop is about fast updates on z13+.

Rendering the whole planet on low zoom is demanding job for the current hardware, no matter how powerful it is, see for example:

https://help.openstreetmap.org/questions/66296/rendering-on-low-zoom-levels-is-slow?page=1&focusedAnswerId=66374#66374

https://munin.openstreetmap.org/openstreetmap.org/render.openstreetmap.org/index.html

@geostonemarten
Copy link

witch indexes are used and how there are built?

@rearden-steel
Copy link
Author

Indexes which were created by osm2pgsql and indexes from the output of indexes.py

@kocio-pl
Copy link
Collaborator

kocio-pl commented Nov 29, 2018

Since there is no easy way to help that, I will close this ticket. However feel free to discuss issues if you have some other problems.

@Nakaner
Copy link
Contributor

Nakaner commented Feb 15, 2019

@jeisenbe wrote in #3670:

One change that has NOT yet been made is to the 1 pixel limit for the size of landcover areas that are rendered. While reducing this limit would improve the rendering at low zoom levels, it also might affect performance, so this is one significant difference from the alternative-colors style. Further improving this should be considered in another PR.

I compared the performance of OSM-Carto on zoom levels 5 to 10 of a 1 pixel and a 0.01 pixel limit for the landcover-low-zoom layer as part of an style upgrade from 4.13.0 to 4.19.0 this week. Tests were rendering a 2048x2048 px metatile using Nik4, PostgreSQL 10 and a whole planet database on a production-ready machine (not in use currently and therefore idle). The presence of landcover on lower zoom levels increases the rendering time of a metatile in central/eastern Europe as following (times in seconds):

version 4.13.0:

rendering 11 6.3327 50.7352 7.7374 51.6183 osm-carto-v4.13.0-geofabrik-international//11.png
Duration: 64                                                                                
rendering 10 5.6295 50.7299 8.4241 52.4782 osm-carto-v4.13.0-geofabrik-international//10.png
Duration: 110                                                                               
rendering 9 5.6295 48.9156 11.2696 52.4782 osm-carto-v4.13.0-geofabrik-international//9.png
Duration: 59
rendering 8 5.6295 45.0971 16.8946 52.4782 osm-carto-v4.13.0-geofabrik-international//8.png
Duration: 165                                                                              
rendering 7 -0.1274 40.8224 22.4757 55.7724 osm-carto-v4.13.0-geofabrik-international//7.png
Duration: 136                                                                               
rendering 6 -0.1275 40.8224 44.9758 66.5104 osm-carto-v4.13.0-geofabrik-international//6.png
Duration: 191                                                                               
rendering 5 -0.1275 -0.3842 90.1516 66.5104 osm-carto-v4.13.0-geofabrik-international//5.png
Duration: 177

version 4.19.0:

rendering 11 6.3327 50.7352 7.7374 51.6183 osm-carto-v4.19.0-geofabrik-international//11.png
Duration: 70
rendering 10 5.6295 50.7299 8.4241 52.4782 osm-carto-v4.19.0-geofabrik-international//10.png
Duration: 157
rendering 9 5.6295 48.9156 11.2696 52.4782 osm-carto-v4.19.0-geofabrik-international//9.png
Duration: 106
rendering 8 5.6295 45.0971 16.8946 52.4782 osm-carto-v4.19.0-geofabrik-international//8.png
Duration: 354
rendering 7 -0.1274 40.8224 22.4757 55.7724 osm-carto-v4.19.0-geofabrik-international//7.png
Duration: 623
rendering 6 -0.1275 40.8224 44.9758 66.5104 osm-carto-v4.19.0-geofabrik-international//6.png
Duration: 586
rendering 5 -0.1275 -0.3842 90.1516 66.5104 osm-carto-v4.19.0-geofabrik-international//5.png
Duration: 602

I tried to build a special index for landcover-low-zoom. Although the database queries were using that index (I tested them with EXPLAIN), there was no significant improvement. The disadvantages of the an index (bloat, slower updates, …) overweight the advantages.

landcover-low-zoom currently uses an area size limit of 0.01 pixel (see #3458 (comment) for rendering examples). If I set this limit to 1.0 pixel, the slow zoom levels are 50% faster to render. Using ST_Simplify(way, 0.05*!way_pixels!) saves about 30%.

However, landuser-area-symbols is slow as well. I tried following changes:

  • The layer is rendered on zoom >= 5 but it renders forests on zoom >= 13 only. Given that forests cover a lot of area in OSM which is covered by landuse polygons, I split the layer into two layers and removed all forest-related WHERE conditions from the SQL query of the new layer for zoom levels 5 to 12. The layer rendered on zoom level >= 13 still has the same SQL query. I added an index for the low zoom landuse-area-symbols layer:
CREATE INDEX planet_osm_polygon_landuse_area_symbols_med 
  ON planet_osm_polygon
  USING GIST (way)
  WHERE 
    ("natural" = ANY (ARRAY['marsh'::text, 'mud'::text, 'wetland'::text, 'beach'::text, 'shoal'::text, 'reef'::text, 'scrub'::text, 'sand'::text]))
    AND way_area > 1300::real;

Splitting the layer and creating a special index does not any significant effect.

The landover-area-symbols layer renders patterns for forests, deserts and wetlands above their polygons. This makes not a lot of sense if the polygons are very small. If I set the minimum limit to 4 square pixel instead of 1 square pixel, rendering does not become faster either.

@rearden-steel If you want to boost low zoom rendering, consider using shape files with simplified geometries (and don't forget to build an index on these shape files using shapeindex (part of Mapnik)).

@kocio-pl
Copy link
Collaborator

Thanks for testing that! We currently lack performance analysis tools (#1287) and persons involved in this specific aspect. If you would like to make something about it, that would be really great.

@imagico
Copy link
Collaborator

imagico commented Feb 15, 2019

The numbers are more or less how i would expect them.

I tried to build a special index for landcover-low-zoom. Although the database queries were using that index (I tested them with EXPLAIN), there was no significant improvement. The disadvantages of the an index (bloat, slower updates, …) overweight the advantages.

The question is at which of the zoom levels the way_area based partial indices currently used are actually most efficient. It is likely that all low zoom landcover and water queries are using one of these indices. The real question is if creating an additional one and moving the existing ones can create enough improvement to justify the additional bloat. Just creating one additional index, possibly very similar in size to the existing ones, will not answer this question.

The landover-area-symbols layer renders patterns for forests, deserts and wetlands above their polygons. This makes not a lot of sense if the polygons are very small. If I set the minimum limit to 4 square pixel instead of 1 square pixel, rendering does not become faster either.

landover-area-symbols before #2746 started at z10 and this was for a good reason - pattern rendering does not make much sense below. A high way_area threshold is problematic there for the same reason as for the base color - and not really that useful ultimately for z10 and above, landover-area-symbols in contrast to the base landcover layer does not render tags that are particularly common in use for small polygons.

At the risk of sounding like a broken record - at the low zoom levels rendering each resolution separately is increasingly wasteful in terms of computing resources. And if you have times of six to ten minutes per metatile for z<=8 that is quite a lot even if you can skim this through some optimization or by sacrificing quality. If like on the OSMF servers you only update these scales once per month that is not a big practical problem but still it should be clear that this comes from the fundamental structure of the rendering framework and is not just a matter of insufficient optimization. This is what i tried to show with the low zoom demo: http://blog.imagico.de/on-basic-small-scale-landcover-rendering/.

Reopening because this is a serious issue that cannot just be ignored while successively extending rendering of existing layers to lower zoom levels (water from z6 to z0, landcover from z8 to z5, landover-area-symbols from z10 to z5). This also strongly relates to the question what the vision for the low zoom levels actually is here (see #1975 (comment)).

@imagico imagico reopened this Feb 15, 2019
@jeisenbe
Copy link
Collaborator

jeisenbe commented Feb 15, 2019 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

6 participants