Skip to content

⚡️ Speed up method GlobalMercator.TileBounds by 69%#17

Open
codeflash-ai[bot] wants to merge 1 commit intomasterfrom
codeflash/optimize-GlobalMercator.TileBounds-mh4jllz8
Open

⚡️ Speed up method GlobalMercator.TileBounds by 69%#17
codeflash-ai[bot] wants to merge 1 commit intomasterfrom
codeflash/optimize-GlobalMercator.TileBounds-mh4jllz8

Conversation

@codeflash-ai
Copy link
Copy Markdown

@codeflash-ai codeflash-ai Bot commented Oct 24, 2025

📄 69% (0.69x) speedup for GlobalMercator.TileBounds in opendm/tiles/gdal2tiles.py

⏱️ Runtime : 2.02 milliseconds 1.20 milliseconds (best of 398 runs)

📝 Explanation and details

The optimization achieves a 68% speedup through two key improvements:

1. Resolution Caching with Memoization
Added _resolution_cache dictionary to store computed resolution values per zoom level. Since Resolution(zoom) involves expensive division by 2**zoom, caching eliminates redundant calculations when the same zoom level is accessed multiple times. The line profiler shows this dramatically reduces time spent in Resolution() calls.

2. TileBounds Method Restructuring
The original TileBounds made two separate calls to PixelsToMeters, each triggering a Resolution(zoom) lookup. The optimized version:

  • Calls Resolution(zoom) only once and stores the result
  • Performs the coordinate calculations inline using cached local variables (ts, res, ox)
  • Eliminates the overhead of 2 function calls and 4 parameter passing operations

Performance Impact by Test Case:

  • High-zoom scenarios (zoom 8-10): 56-69% faster due to resolution caching benefits
  • Repeated tile calculations: 48-60% faster from reduced function call overhead
  • Large-scale operations: Most dramatic gains (68-69%) when processing many tiles at the same zoom level

The optimizations are particularly effective for tile mapping applications where the same zoom levels are accessed frequently, making the resolution cache highly beneficial while the streamlined TileBounds reduces per-tile computation overhead.

Correctness verification report:

Test Status
⚙️ Existing Unit Tests 🔘 None Found
🌀 Generated Regression Tests 1939 Passed
⏪ Replay Tests 🔘 None Found
🔎 Concolic Coverage Tests 🔘 None Found
📊 Tests Coverage 100.0%
🌀 Generated Regression Tests and Runtime
import math

# imports
import pytest  # used for our unit tests
from opendm.tiles.gdal2tiles import GlobalMercator

# unit tests

# Helper for floating point comparison
def almost_equal_tuple(t1, t2, tol=1e-7):
    for a, b in zip(t1, t2):
        pass

# ---- Basic Test Cases ----

def test_tilebounds_origin_tile_zoom0():
    """
    Test the bounds of the only tile at zoom level 0 (the world).
    """
    gm = GlobalMercator()
    codeflash_output = gm.TileBounds(0, 0, 0); bounds = codeflash_output # 1.89μs -> 1.28μs (48.0% faster)
    expected = (-gm.originShift, -gm.originShift, gm.originShift, gm.originShift)
    almost_equal_tuple(bounds, expected)

def test_tilebounds_first_tile_zoom1():
    """
    Test the bounds of the bottom-left tile at zoom level 1.
    """
    gm = GlobalMercator()
    codeflash_output = gm.TileBounds(0, 0, 1); bounds = codeflash_output # 2.17μs -> 1.40μs (54.9% faster)
    # At zoom 1, world is split into 2x2 tiles
    half = gm.originShift
    expected = (-half, -half, 0.0, 0.0)
    almost_equal_tuple(bounds, expected)

def test_tilebounds_second_tile_zoom1():
    """
    Test the bounds of the tile (1, 0) at zoom level 1.
    """
    gm = GlobalMercator()
    codeflash_output = gm.TileBounds(1, 0, 1); bounds = codeflash_output # 2.18μs -> 1.40μs (56.4% faster)
    half = gm.originShift
    expected = (0.0, -half, half, 0.0)
    almost_equal_tuple(bounds, expected)

def test_tilebounds_top_right_tile_zoom1():
    """
    Test the bounds of the top-right tile at zoom level 1.
    """
    gm = GlobalMercator()
    codeflash_output = gm.TileBounds(1, 1, 1); bounds = codeflash_output # 2.19μs -> 1.42μs (54.4% faster)
    half = gm.originShift
    expected = (0.0, 0.0, half, half)
    almost_equal_tuple(bounds, expected)

def test_tilebounds_middle_tile_zoom2():
    """
    Test the bounds of a middle tile at zoom level 2.
    """
    gm = GlobalMercator()
    codeflash_output = gm.TileBounds(1, 1, 2); bounds = codeflash_output # 2.14μs -> 1.34μs (60.3% faster)
    quarter = gm.originShift / 2
    expected = (0.0, 0.0, quarter, quarter)
    almost_equal_tuple(bounds, expected)

def test_tilebounds_custom_tilesize():
    """
    Test with a non-default tile size.
    """
    gm = GlobalMercator(tileSize=512)
    codeflash_output = gm.TileBounds(0, 0, 0); bounds = codeflash_output # 1.88μs -> 1.26μs (48.3% faster)
    expected = (-gm.originShift, -gm.originShift, gm.originShift, gm.originShift)
    almost_equal_tuple(bounds, expected)

# ---- Edge Test Cases ----

def test_tilebounds_negative_tile_indices():
    """
    Negative tile indices should produce bounds outside the world extent.
    """
    gm = GlobalMercator()
    codeflash_output = gm.TileBounds(-1, -1, 1); bounds = codeflash_output # 2.34μs -> 1.58μs (47.9% faster)
    res = gm.Resolution(1)
    expected = (-gm.originShift - gm.tileSize*res, -gm.originShift - gm.tileSize*res,
                -gm.originShift, -gm.originShift)
    almost_equal_tuple(bounds, expected)

def test_tilebounds_large_zoom_level():
    """
    Test bounds at a high zoom level (e.g., 20).
    """
    gm = GlobalMercator()
    zoom = 20
    tx, ty = 2**20 - 1, 2**20 - 1  # last tile at this zoom
    codeflash_output = gm.TileBounds(tx, ty, zoom); bounds = codeflash_output # 2.50μs -> 1.64μs (52.6% faster)
    res = gm.Resolution(zoom)
    minx = (tx)*gm.tileSize*res - gm.originShift
    miny = (ty)*gm.tileSize*res - gm.originShift
    maxx = (tx+1)*gm.tileSize*res - gm.originShift
    maxy = (ty+1)*gm.tileSize*res - gm.originShift
    expected = (minx, miny, maxx, maxy)
    almost_equal_tuple(bounds, expected)

def test_tilebounds_zero_tilesize():
    """
    Test with tileSize=0 (should raise ZeroDivisionError).
    """
    with pytest.raises(ZeroDivisionError):
        gm = GlobalMercator(tileSize=0)
        gm.TileBounds(0, 0, 0)

def test_tilebounds_non_integer_indices():
    """
    Test with float tile indices.
    """
    gm = GlobalMercator()
    codeflash_output = gm.TileBounds(0.5, 0.5, 1); bounds = codeflash_output # 2.52μs -> 1.68μs (50.3% faster)
    res = gm.Resolution(1)
    minx = (0.5)*gm.tileSize*res - gm.originShift
    miny = (0.5)*gm.tileSize*res - gm.originShift
    maxx = (1.5)*gm.tileSize*res - gm.originShift
    maxy = (1.5)*gm.tileSize*res - gm.originShift
    expected = (minx, miny, maxx, maxy)
    almost_equal_tuple(bounds, expected)

def test_tilebounds_negative_zoom():
    """
    Negative zoom levels should produce larger-than-world bounds.
    """
    gm = GlobalMercator()
    codeflash_output = gm.TileBounds(0, 0, -1); bounds = codeflash_output # 2.58μs -> 2.01μs (28.3% faster)
    res = gm.Resolution(-1)
    minx = 0 * gm.tileSize * res - gm.originShift
    miny = 0 * gm.tileSize * res - gm.originShift
    maxx = 1 * gm.tileSize * res - gm.originShift
    maxy = 1 * gm.tileSize * res - gm.originShift
    expected = (minx, miny, maxx, maxy)
    almost_equal_tuple(bounds, expected)

def test_tilebounds_large_tile_indices():
    """
    Test with very large tile indices to ensure no overflow.
    """
    gm = GlobalMercator()
    tx, ty, zoom = 999999, 999999, 10
    codeflash_output = gm.TileBounds(tx, ty, zoom); bounds = codeflash_output # 2.44μs -> 1.53μs (59.7% faster)
    res = gm.Resolution(zoom)
    minx = tx*gm.tileSize*res - gm.originShift
    miny = ty*gm.tileSize*res - gm.originShift
    maxx = (tx+1)*gm.tileSize*res - gm.originShift
    maxy = (ty+1)*gm.tileSize*res - gm.originShift
    expected = (minx, miny, maxx, maxy)
    almost_equal_tuple(bounds, expected)

# ---- Large Scale Test Cases ----

def test_tilebounds_many_tiles_zoom4():
    """
    Test bounds for all tiles at zoom level 4 (16x16 = 256 tiles).
    """
    gm = GlobalMercator()
    zoom = 4
    for tx in range(16):
        for ty in range(16):
            codeflash_output = gm.TileBounds(tx, ty, zoom); bounds = codeflash_output
            res = gm.Resolution(zoom)
            minx = tx*gm.tileSize*res - gm.originShift
            miny = ty*gm.tileSize*res - gm.originShift
            maxx = (tx+1)*gm.tileSize*res - gm.originShift
            maxy = (ty+1)*gm.tileSize*res - gm.originShift
            expected = (minx, miny, maxx, maxy)
            almost_equal_tuple(bounds, expected)

def test_tilebounds_performance_large_zoom():
    """
    Performance and correctness for 1000 random tiles at zoom 10.
    """
    gm = GlobalMercator()
    zoom = 10
    max_index = 2**zoom
    for i in range(1000):
        tx = i % max_index
        ty = (i * 13) % max_index  # pseudo-random but deterministic
        codeflash_output = gm.TileBounds(tx, ty, zoom); bounds = codeflash_output # 1.05ms -> 619μs (69.8% faster)
        res = gm.Resolution(zoom)
        minx = tx*gm.tileSize*res - gm.originShift
        miny = ty*gm.tileSize*res - gm.originShift
        maxx = (tx+1)*gm.tileSize*res - gm.originShift
        maxy = (ty+1)*gm.tileSize*res - gm.originShift
        expected = (minx, miny, maxx, maxy)
        almost_equal_tuple(bounds, expected)

def test_tilebounds_large_tilesize_many_tiles():
    """
    Test with a large tileSize and many tiles at zoom 2.
    """
    gm = GlobalMercator(tileSize=900)
    zoom = 2
    for tx in range(10):
        for ty in range(10):
            codeflash_output = gm.TileBounds(tx, ty, zoom); bounds = codeflash_output
            res = gm.Resolution(zoom)
            minx = tx*gm.tileSize*res - gm.originShift
            miny = ty*gm.tileSize*res - gm.originShift
            maxx = (tx+1)*gm.tileSize*res - gm.originShift
            maxy = (ty+1)*gm.tileSize*res - gm.originShift
            expected = (minx, miny, maxx, maxy)
            almost_equal_tuple(bounds, expected)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.
#------------------------------------------------
import math

# imports
import pytest  # used for our unit tests
from opendm.tiles.gdal2tiles import GlobalMercator

# unit tests

# Helper function for approximate equality of bounds
def bounds_close(a, b, rel_tol=1e-9, abs_tol=1e-6):
    return all(math.isclose(x, y, rel_tol=rel_tol, abs_tol=abs_tol) for x, y in zip(a, b))

@pytest.fixture
def mercator():
    # Standard tile size for most tests
    return GlobalMercator(tileSize=256)

@pytest.fixture
def mercator_512():
    # Non-standard tile size for edge cases
    return GlobalMercator(tileSize=512)

# ------------------ Basic Test Cases ------------------

def test_tilebounds_zoom0_tile0_0(mercator):
    # At zoom 0, only one tile exists: (0,0)
    codeflash_output = mercator.TileBounds(0, 0, 0); bounds = codeflash_output # 1.83μs -> 1.22μs (49.5% faster)
    # The bounds should be the full extent of the projection
    expected = (-mercator.originShift, -mercator.originShift, mercator.originShift, mercator.originShift)

def test_tilebounds_zoom1_tiles(mercator):
    # At zoom 1, there are 2x2 tiles: (0,0), (1,0), (0,1), (1,1)
    # Each tile covers half the world in each direction
    half_shift = mercator.originShift / 2
    # (0,0): bottom-left quarter
    codeflash_output = mercator.TileBounds(0, 0, 1); bounds_00 = codeflash_output # 2.29μs -> 1.45μs (58.1% faster)
    expected_00 = (-mercator.originShift, -mercator.originShift, 0, 0)
    # (1,0): bottom-right quarter
    codeflash_output = mercator.TileBounds(1, 0, 1); bounds_10 = codeflash_output # 1.46μs -> 993ns (47.0% faster)
    expected_10 = (0, -mercator.originShift, mercator.originShift, 0)
    # (0,1): top-left quarter
    codeflash_output = mercator.TileBounds(0, 1, 1); bounds_01 = codeflash_output # 1.29μs -> 815ns (57.8% faster)
    expected_01 = (-mercator.originShift, 0, 0, mercator.originShift)
    # (1,1): top-right quarter
    codeflash_output = mercator.TileBounds(1, 1, 1); bounds_11 = codeflash_output # 1.17μs -> 698ns (66.9% faster)
    expected_11 = (0, 0, mercator.originShift, mercator.originShift)

def test_tilebounds_zoom2_tile_center(mercator):
    # At zoom 2, check the center tile (1,1)
    codeflash_output = mercator.TileBounds(1, 1, 2); bounds = codeflash_output # 2.09μs -> 1.36μs (53.8% faster)
    # Each tile covers a quarter of the half, so 1/4 of the world
    res = mercator.Resolution(2)
    minx = 1 * mercator.tileSize * res - mercator.originShift
    miny = 1 * mercator.tileSize * res - mercator.originShift
    maxx = 2 * mercator.tileSize * res - mercator.originShift
    maxy = 2 * mercator.tileSize * res - mercator.originShift
    expected = (minx, miny, maxx, maxy)

def test_tilebounds_tileSize_512(mercator_512):
    # With tileSize=512, world extent remains the same, but resolution changes
    codeflash_output = mercator_512.TileBounds(0, 0, 0); bounds = codeflash_output # 1.86μs -> 1.21μs (53.6% faster)
    expected = (-mercator_512.originShift, -mercator_512.originShift, mercator_512.originShift, mercator_512.originShift)

def test_tilebounds_negative_tile_indices(mercator):
    # Negative tile indices should produce bounds outside the world
    codeflash_output = mercator.TileBounds(-1, -1, 1); bounds = codeflash_output # 2.49μs -> 1.69μs (47.3% faster)

# ------------------ Edge Test Cases ------------------

def test_tilebounds_max_tile_index_at_zoom0(mercator):
    # At zoom 0, only tile (0,0) is valid, but test (1,1) which is out of bounds
    codeflash_output = mercator.TileBounds(1, 1, 0); bounds = codeflash_output # 1.98μs -> 1.33μs (48.2% faster)

def test_tilebounds_large_zoom_tile_edge(mercator):
    # At zoom 10, test the last tile in each direction
    zoom = 10
    num_tiles = 2**zoom
    codeflash_output = mercator.TileBounds(num_tiles-1, num_tiles-1, zoom); bounds = codeflash_output # 2.44μs -> 1.58μs (54.2% faster)

def test_tilebounds_zero_tileSize():
    # tileSize=0 should result in division by zero error
    with pytest.raises(ZeroDivisionError):
        GlobalMercator(tileSize=0)

def test_tilebounds_non_integer_tile_indices(mercator):
    # Non-integer tile indices should work (since math allows it), but check correctness
    codeflash_output = mercator.TileBounds(0.5, 0.5, 1); bounds = codeflash_output # 2.55μs -> 1.79μs (42.4% faster)
    # Should be halfway between (0,0) and (1,1) at zoom 1
    expected_minx = mercator.PixelsToMeters(0.5*mercator.tileSize, 0.5*mercator.tileSize, 1)[0]
    expected_miny = mercator.PixelsToMeters(0.5*mercator.tileSize, 0.5*mercator.tileSize, 1)[1]
    expected_maxx = mercator.PixelsToMeters(1.5*mercator.tileSize, 1.5*mercator.tileSize, 1)[0]
    expected_maxy = mercator.PixelsToMeters(1.5*mercator.tileSize, 1.5*mercator.tileSize, 1)[1]
    expected = (expected_minx, expected_miny, expected_maxx, expected_maxy)

def test_tilebounds_float_zoom_level(mercator):
    # Zoom level as float should work (since math allows it), but check correctness
    codeflash_output = mercator.TileBounds(0, 0, 1.5); bounds = codeflash_output # 2.64μs -> 1.99μs (33.1% faster)
    res = mercator.Resolution(1.5)
    minx = 0 * mercator.tileSize * res - mercator.originShift
    miny = 0 * mercator.tileSize * res - mercator.originShift
    maxx = 1 * mercator.tileSize * res - mercator.originShift
    maxy = 1 * mercator.tileSize * res - mercator.originShift
    expected = (minx, miny, maxx, maxy)

def test_tilebounds_large_tileSize(mercator):
    # Very large tileSize, e.g. 999
    merc = GlobalMercator(tileSize=999)
    codeflash_output = merc.TileBounds(0, 0, 0); bounds = codeflash_output # 1.96μs -> 1.34μs (46.7% faster)
    expected = (-merc.originShift, -merc.originShift, merc.originShift, merc.originShift)

# ------------------ Large Scale Test Cases ------------------

def test_tilebounds_many_tiles_at_zoom8(mercator):
    # At zoom 8, there are 256x256 tiles. Test a sample of them.
    zoom = 8
    num_tiles = 2**zoom
    # Test a few tiles: corners and center
    for tx, ty in [(0, 0), (num_tiles-1, 0), (0, num_tiles-1), (num_tiles-1, num_tiles-1), (num_tiles//2, num_tiles//2)]:
        codeflash_output = mercator.TileBounds(tx, ty, zoom); bounds = codeflash_output # 7.13μs -> 4.55μs (56.8% faster)

def test_tilebounds_performance_large_zoom(mercator):
    # At zoom 9, test all tiles in a single row (ty=0), tx in [0, 511]
    zoom = 9
    num_tiles = 2**zoom
    for tx in range(num_tiles):
        codeflash_output = mercator.TileBounds(tx, 0, zoom); bounds = codeflash_output # 525μs -> 311μs (68.7% faster)

def test_tilebounds_large_tileSize_and_zoom():
    # Test with large tileSize and large zoom
    merc = GlobalMercator(tileSize=999)
    zoom = 9
    num_tiles = 2**zoom
    # Test center tile
    tx = num_tiles // 2
    ty = num_tiles // 2
    codeflash_output = merc.TileBounds(tx, ty, zoom); bounds = codeflash_output # 2.31μs -> 1.48μs (55.9% faster)

def test_tilebounds_all_tiles_at_zoom7(mercator):
    # At zoom 7, there are 128x128 tiles. Test a sample of 10 random tiles.
    zoom = 7
    num_tiles = 2**zoom
    # Pick 10 tiles spread across the grid
    for i in range(10):
        tx = i * (num_tiles // 10)
        ty = (9-i) * (num_tiles // 10)
        codeflash_output = mercator.TileBounds(tx, ty, zoom); bounds = codeflash_output # 12.3μs -> 7.64μs (61.1% faster)
# codeflash_output is used to check that the output of the original code is the same as that of the optimized code.

To edit these changes git checkout codeflash/optimize-GlobalMercator.TileBounds-mh4jllz8 and push.

Codeflash

The optimization achieves a 68% speedup through two key improvements:

**1. Resolution Caching with Memoization**
Added `_resolution_cache` dictionary to store computed resolution values per zoom level. Since `Resolution(zoom)` involves expensive division by `2**zoom`, caching eliminates redundant calculations when the same zoom level is accessed multiple times. The line profiler shows this dramatically reduces time spent in `Resolution()` calls.

**2. TileBounds Method Restructuring**
The original `TileBounds` made two separate calls to `PixelsToMeters`, each triggering a `Resolution(zoom)` lookup. The optimized version:
- Calls `Resolution(zoom)` only once and stores the result
- Performs the coordinate calculations inline using cached local variables (`ts`, `res`, `ox`)
- Eliminates the overhead of 2 function calls and 4 parameter passing operations

**Performance Impact by Test Case:**
- **High-zoom scenarios** (zoom 8-10): 56-69% faster due to resolution caching benefits
- **Repeated tile calculations**: 48-60% faster from reduced function call overhead
- **Large-scale operations**: Most dramatic gains (68-69%) when processing many tiles at the same zoom level

The optimizations are particularly effective for tile mapping applications where the same zoom levels are accessed frequently, making the resolution cache highly beneficial while the streamlined `TileBounds` reduces per-tile computation overhead.
@codeflash-ai codeflash-ai Bot requested a review from mashraf-222 October 24, 2025 07:41
@codeflash-ai codeflash-ai Bot added ⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash labels Oct 24, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

⚡️ codeflash Optimization PR opened by Codeflash AI 🎯 Quality: High Optimization Quality according to Codeflash

Projects

None yet

Development

Successfully merging this pull request may close these issues.

0 participants