Thursday, 19 October 2017

A simpler MgCooker tile seeding process

I don't know if you've ever seen the guts of the tile seeding code used by MgCooker, it isn't the most prettiest of things, but it for the most part works.

Besides some cosmetic restructuring of the code, I haven't really touched this part of Maestro ever.

Consider the history of this tiling code. It originated around 2009. Things we now take for granted like async/await and the Task Parallel Library probably didn't exist around that time, so you had no choice but to dive deep into wait handles, auto-reset events and manual thread management.

If I had to write MgCooker from scratch today, I'd cook up (pun intended) probably something like this

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
...
public class TileSeeder
{
    public void SeedTiles()
    {
        List<(int row, int col, int scale)> tiles = ComputeTileRequestList();
        int total = tiles.Count;
        int rendered = 0;
        var sw = new Stopwatch();
        sw.Start();

        //The magic sauce that takes multi-threads our tile seeding and takes care of all our multi-threading concerns!
        Parallel.ForEach(tiles, (tile) => 
        {
            //Send a HTTP request to GETTILE mapagent API with tile.row, tile.col and tile.scale
            ...
            Interlocked.Increment(ref rendered);
            Console.WriteLine($"Rendered {rendered}/{total} tiles");
        });

        //And this method blocks too, so if we get to this point, the tiling process has finished.
        sw.Stop();
        Console.WriteLine($"Rendered {rendered} tiles in {sw.Elapsed}");
    }
}

Isn't this much easier to read and comprehend?

The implementation of the ComputeTileRequestList method referenced here is omitted for brevity, but for the implementation we can just reuse what is in the current iteration of MgCooker. Most of the settings in MgCooker mainly affects the generation of the list of row/col/scale anyways.

The core multi-threading "render/cache all these tiles" is just one simple Parallel.ForEach method baked right in the .net Framework itself!

MgCooker is overdue for a rewrite anyways. I just didn't really think it would be so conceptually simple with today's .net libraries and C# language constructs!

No comments: