Slow tile loading performance during zoom with ImageMapType overlay

4,114 views
Skip to first unread message

John Laur

unread,
Jan 25, 2011, 10:38:43 AM1/25/11
to google-map...@googlegroups.com
Hello,

I'm new to the group here; hello everyone.

I'm trying to figure out a technique for speeding up tile loading performance for a custom image tile overlay. The trouble is that when doing a fast zoom with the mouse wheel or by rapidly clicking zoom in/out buttons the API will queue a bunch of requests for layers that have been "zoomed through" and loading the overlay for the current layer will not actually happen until these pending requests are satisfied. Initially I thought Google was doing some magic with its base tile set to influence the retrieval order of the base tiles during zooms, but upon closer inspection it appears they are simply servicing the tile requests with ridiculous speed.

Since we are dynamically generating tiles, we can't hope to achieve a similar result with ImageMapType by reducing latency alone; best we are doing is about 100ms/tile. However we are able to horizontally scale our tile generation, so are there alternative techniques we can try? My initial thought is to use a different http hostname per layer to allow the browser to initiate immediate requests upon zooming to a layer, but there are some drawbacks to this approach on the server end. Another possibility might be to create an entirely new version of ImageMapType that performs better with a high-latency and highly-parallel tile generation, but that seems like a lot of work to jump into without first examining alternatives.

Thanks,
John Laur

Rossko

unread,
Jan 25, 2011, 2:25:35 PM1/25/11
to Google Maps JavaScript API v3
This old discussion touches on tile queuing
http://groups.google.com/group/google-maps-js-api-v3/browse_thread/thread/755aa44e17196ab6/632b0e01587e4c99

Perhaps you could cache generated tiles server-side to improve
response.

> My initial
thought is to use a different http hostname per layer

Or use Googles load-sharing technique multiple domain technique on
each layer, "odds" and "evens" tiles if you like.

Ben Appleton

unread,
Jan 25, 2011, 5:42:42 PM1/25/11
to google-map...@googlegroups.com
Hi John,

The API will initiate requests for tiles on screen, but cancels any in-flight requests after you zoom in again. It should not wait until pending requests are satisfied. Further, your browser will limit the number of concurrent requests to your tile server, so your tile server should not have to generate every tile during a scrollwheel zoom over multiple zoom levels. Different browsers use different limits here.

For example, if I load http://code.google.com/apis/maps/documentation/javascript/examples/map-simple.html in Firefox with Firebug enabled, then scrollwheel zoom a few zoom levels, Firebug's Net tab reports many canceled tile requests and the final tiles appear reasonably fast.

Which browser are you using? Can you confirm that tile requests are never being cancelled?

Thanks
Ben

--
You received this message because you are subscribed to the Google Groups "Google Maps JavaScript API v3" group.
To post to this group, send email to google-map...@googlegroups.com.
To unsubscribe from this group, send email to google-maps-js-a...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/google-maps-js-api-v3?hl=en.

John Laur

unread,
Jan 25, 2011, 7:30:44 PM1/25/11
to Google Maps JavaScript API v3
Ben,

I am seeing the behavior in Chrome and using the Network tab to check
the issue. If I load my map with a single tile overlay (ImageMapType)
requests queue for all tiles at all zoom levels, and the requests wait
to complete. In fairness *sometimes* i can zoom fast enough that
requests for an entire (or partial) layer are skipped, but for sure I
never see requests that begin and are aborted. The base tiles are also
loaded from google for all layers, but since they never have to be
generated and are smaller in file size, the problem is not visible.

If I zoom from the my map's default zoom 5 in to its max zoom of 17,
the network pane will report the following times for the request for a
tile at layer 5:

Conversely a tile at layer 5 has the following profile:

Proxy 0, Blocking 0, Sending 0, Waiting 76ms, Receiving 209ms

But by the time a layer 17 file is requested so many pending requests
have built up progressively to a much longer blocking time:

Proxy 0, Blocking 6.08s, Sending 0, Waiting 276ms, Receiving 0

Currently, I am requesting all of my overlay tiles using the same
hostname. I do understand that I could use one hostname per layer and
this would allow more concurrent connections and the ability of the
browser to immediately initiate tile requests in a new layer without
blocking, but only so long as the browser will still be willing to
open new requests. It also introduces some other problems -- the load
to generate all the interim tiles still unnecessarily goes to the
servers. I have actually tested the multiple hostnames per layer and
it helps a bit but not enough, and doesn't really solve the core
problem.

In firefox I do occasionally see Aborted requests in firebug, but
still most of the time I see hundreds of lines with spinners as the
downloads for each interim layer complete.

John

On Jan 25, 4:42 pm, Ben Appleton <apple...@google.com> wrote:
> Hi John,
>
> The API will initiate requests for tiles on screen, but cancels any
> in-flight requests after you zoom in again. It should not wait until pending
> requests are satisfied. Further, your browser will limit the number of
> concurrent requests to your tile server, so your tile server should not have
> to generate every tile during a scrollwheel zoom over multiple zoom levels.
> Different browsers use different limits here.
>
> For example, if I loadhttp://code.google.com/apis/maps/documentation/javascript/examples/ma...
> > google-maps-js-a...@googlegroups.com<google-maps-js-api-v3%2B unsub...@googlegroups.com>
> > .

Ben Appleton

unread,
Jan 25, 2011, 8:35:10 PM1/25/11
to google-map...@googlegroups.com
Regarding Firefox: interesting; I see at least 50% of tile requests aborted, so it's working for me. I'm in Australia with ~200ms round trip time; I'm guessing you have a low RTT to your tile server which might affect this.

Regarding Chrome: I see the problem now, it looks like a bad interaction with continuous zoom. I'll file a bug internally. If you'd like to track progress on the issue, please file an issue in the issue tracker: http://code.google.com/p/gmaps-api-issues/.

Thanks
Ben

To unsubscribe from this group, send email to google-maps-js-a...@googlegroups.com.

John Laur

unread,
Jan 25, 2011, 11:21:33 PM1/25/11
to Google Maps JavaScript API v3
Ben,

Thank you for your assistance. I have filed Issue 3011 to track this
externally http://code.google.com/p/gmaps-api-issues/issues/detail?id=3011

In my particular test case, I have several tile generators running on
EC2, so my latency to the tile servers is not 200ms but it's also not
particularly great (about 55ms). Knowing what to look for now, I would
expect a much larger number of requests to be aborted than actually
are aborted when performing the same tests (doing this in Firefox
now). Can you share additional specifics on how this is supposed to
work for the ImageMapType class? I would assume this is buried deep in
the internals of the class's releaseTile and whatever part of the map
code is responsible for calling it, so I would therefore conclude that
it's fairly fruitless for me to attempt to debug anything.

We are using ImageMapType rather than creating our own subclass of
MapType because it would seem to have the advantages of both saving
both work and complexity. I know it is not documented, but is there
any easy way to trap the getTile/releaseTile calls to ImageMapType to
better see whats going on there?

John
> > > > google-maps-js-a...@googlegroups.com<google-maps-js-api-v3%2B unsub...@googlegroups.com><google-maps-js-api-v3%2B

Ben Appleton

unread,
Jan 26, 2011, 2:33:30 AM1/26/11
to google-map...@googlegroups.com
Hi John,

On Wed, Jan 26, 2011 at 3:21 PM, John Laur <john...@gmail.com> wrote:
Ben,

Thank you for your assistance. I have filed Issue 3011 to track this
externally http://code.google.com/p/gmaps-api-issues/issues/detail?id=3011

You mention you have 10 layers. Do I understand correctly that you're making 10 HTTP requests per zoom level per tile on screen? You must be loading thousands of tiles for a quick scrollwheel zoom, ouch!

Is there any chance you could composite your layers in a server? For example, you could launch another EC2 service which loads the tiles from each individual tile renderer, composites them into a single image, and returns this to the browser.

In my particular test case, I have several tile generators running on
EC2, so my latency to the tile servers is not 200ms but it's also not
particularly great (about 55ms). Knowing what to look for now, I would
expect a much larger number of requests to be aborted than actually
are aborted when performing the same tests (doing this in Firefox
now). Can you share additional specifics on how this is supposed to
work for the ImageMapType class? I would assume this is buried deep in
the internals of the class's releaseTile and whatever part of the map
code is responsible for calling it, so I would therefore conclude that
it's fairly fruitless for me to attempt to debug anything.

ImageMapType cancels tile requests when .releaseTile() is called. In most browsers it cancels an in-flight image request by setting image.src to a transparent image. That does not work in WebKit browsers, in which case ImageMapType loads images in iframes and calls iframe.window.stop() to cancel in-flight image requests.

Browsers will only allow a certain maximum number of concurrent HTTP requests, ranging from 2 to 15. Even though ImageMapType requests all tiles in view, browsers will send the first few requests then queue the rest, trickling out more requests as responses arrive. So when you zoom twice in quick succession, there will never be more than 2 to 15 in-flight requests to abort.

We are using ImageMapType rather than creating our own subclass of
MapType because it would seem to have the advantages of both saving
both work and complexity. I know it is not documented, but is there
any easy way to trap the getTile/releaseTile calls to ImageMapType to
better see whats going on there?

Sorry, there aren't any hooks (not even undocumented) for tracing ImageMapType's internals. You could step through the obfuscated code in a debugger (starting from a breakpoint in your .getTileUrl() implementation), but you would have to be keen.

Cheers
Ben

To unsubscribe from this group, send email to google-maps-js-a...@googlegroups.com.

John Laur

unread,
Jan 26, 2011, 8:23:46 AM1/26/11
to google-map...@googlegroups.com
Ben,

Egads no! One overlay only. I meant to say something like "zoom through 10 levels". Sorry for the confusion. I know we are hitting the browser's maximums in our one hostname per payer tests but it is still marginally better. 

Your response does reinforce though how much work it would be to reimplement an ImageMapType equivalent -- we will hang tight and see what happens on the issue tracker. 

John

bratliff

unread,
Jan 26, 2011, 12:40:58 PM1/26/11
to Google Maps JavaScript API v3
On Jan 26, 1:23 pm, John Laur <johnl...@gmail.com> wrote:

> Your response does reinforce though how much work it would be to reimplement an ImageMapType equivalent -- we will hang tight and see what happens on the issue tracker.

You might find

http://www.polylib.us/sparse

http://www.polylib.us/sparsetile.js

to be useful. It preceded ImageMapType overlay layers.

I faced the same situation you have with too many zoom changes
happening in rapid succession. I solved it with deferred rendering.
I will be glad to extract the relevant pieces of code from PolyCluster
to incorporate in "sparsetile.js". Also, "sparsetile.js" is missing
the code to handle spanning of the International date line.

For what it is worth, I believe aborting tiles in-flight & 50%
complete on average is not optimal. Instead, the tile ought to be
allowed to continue in an "IMG" element not attached to the DOM. A
perfectly good tile will be cached for future visits to the same zoom
level. It does not involve the overhead of aborting an http request.

In conjunction, the API cannot dispatch all tile requests
immediately. A throttle or queue is required. It restricts the tiles
in flight simultaneously to some number like four. If the map
position changes, the queue is reprioritized to match the map
position. Tiles out of view are discarded. The tile's "onload" event
& "onerror" event are used to keep the pipeline flowing. It works
will with flight simulation where course corrections can occur
rapidly.

Ben Appleton

unread,
Jan 26, 2011, 5:58:29 PM1/26/11
to google-map...@googlegroups.com
On Thu, Jan 27, 2011 at 4:40 AM, bratliff <brat...@umich.edu> wrote:
On Jan 26, 1:23 pm, John Laur <johnl...@gmail.com> wrote:

> Your response does reinforce though how much work it would be to reimplement an ImageMapType equivalent -- we will hang tight and see what happens on the issue tracker.

You might find

   http://www.polylib.us/sparse

   http://www.polylib.us/sparsetile.js

to be useful.  It preceded ImageMapType overlay layers.

I faced the same situation you have with too many zoom changes
happening in rapid succession.  I solved it with deferred rendering.
I will be glad to extract the relevant pieces of code from PolyCluster
to incorporate in "sparsetile.js".  Also, "sparsetile.js" is missing
the code to handle spanning of the International date line.

For what it is worth, I believe aborting tiles in-flight & 50%
complete on average is not optimal.  Instead, the tile ought to be
allowed to continue in an "IMG" element not attached to the DOM.  A
perfectly good tile will be cached for future visits to the same zoom
level.  It does not involve the overhead of aborting an http request.

No, the behavior you recommend is the cause of the slowness that John observes.
 
In conjunction, the API cannot dispatch all tile requests
immediately.  A throttle or queue is required.  It restricts the tiles
in flight simultaneously to some number like four.  If the map
position changes, the queue is reprioritized to match the map
position.  Tiles out of view are discarded.  The tile's "onload" event
& "onerror" event are used to keep the pipeline flowing.  It works
will with flight simulation where course corrections can occur
rapidly.

We have implemented tile queues in the past but they add significant latency to load a new viewport.

"Optimal" depends on your goal. Most Maps API sites are not a flight simulator.

John Laur

unread,
Jan 26, 2011, 7:01:51 PM1/26/11
to google-map...@googlegroups.com
Thanks for your post -- I had seen your tiling class before though without any context.

For what it's worth I had initially started some time ago by implementing my own tile loader by subclassing MapType directly but quickly found that I was running into having to write a lot of browser specific code, problems with optimizing dom manipulation performance, needing to write my own algorithms for determining the tile request order (center out spiral) and a lot of other stuff. So I ultimately decided the perceived advantages of being able to do "deferred rendering" (I was calling it zoom/pan debounce) and managing my own request queue (I had an array of 50 tiles "holders" that I would dump into the dom and then manipulate my tileset within), etc. All this stuff was not really worth the hassle of trying to duplicate the efforts of the entire team of Maps developers who I imagine spend all day every day thinking about how to make tiles serve up faster... So I've spent enough time going down that I'd rather find an alternative.

Knowing more about how ImageMapType operates internally and being able to experiment and change some things about the way our backend handles requests has been very helpful. Tiles are now coming in much faster after rapid zooming although the problem of interim tiles not being cancelled aggressively enough is still present.

I disagree that cancelling in-flight requests is a bad performance move -- as Ben said the pending downloads are the whole crux of the problem in this case! After learning that request cancellation is supposed to happen and is currently not working in Chrome (and perhaps isnt aggressive enough in other browsers either,) all of my mitigation strategy today has been attempting to give the browser the best chance to keep making new non-blocking requests upon a zoom or pan by manipulating the hostname used to make the request (the number of hostnames and the mapping of tile/zoom->hostname). Cancelling requests is probably only a bad move if your tile generation is really expensive, your tiles are ridiculously huge in filesize, you don't or can't cache your tiles, and you have a reasonable expectation that your users will return to tiles they just abandoned. Plus, if you only have a few "slots" to download tiles you don't want these slots filled with slow multi-second tile downloads -- in that case it doesn't really matter what the next tile to download is.

In our case though more aggressively cancellation of requests by the API would be quite welcome. If a tile has not yet been generated, our backend will generate it as fast as possible and immediately return it to the client; however before saving the tile to the cache it will continue to perform additional (read: slow) optimization and compression. Thus if the client aborts the initial request and then returns after a couple of seconds/minutes/hours/days to re-request the tile, they'll not only be fetching the tile from cache (no generation latency!) but it will probably be much smaller than it would have been originally.

bratliff

unread,
Jan 26, 2011, 7:16:13 PM1/26/11
to Google Maps JavaScript API v3
OK - admittedly, flight animation is different than simple panning &
zooming. I do a lot of backtracking but perhaps others do not.

I will be glad to add the fetch delay to "sparsetile.js" if it will be
useful. If it is a waste of time, I will not bother. It is about a
dozen lines of code I can steal from PolyCluster.
Reply all
Reply to author
Forward
0 new messages