Bash

git add builder/src/build_index.cpp && git commit -m "$(cat <<'EOF'
fix: remove sub-way count limit for backtracking retry

The 200 sub-way limit prevented backtracking retry for 10 planet relations.
The 100K call budget already prevents exponential blowup, making the size
limit redundant.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
EOF
)" && cat builder/src/build_index.cpp | ssh node3 "cat > /tmp/traccar-geocoder/builder/src/build_index.cpp" && ssh node3 "cd /tmp/traccar-geocoder/builder/build && make -j\$(nproc) 2>&1 | tail -3 && rm -rf ~/planet-parallel4 && mkdir -p ~/planet-parallel4 && nohup /tmp/traccar-geocoder/builder/build/build-index ~/planet-parallel4 ~/pbf/planet.osm.pbf --parallel-admin > ~/planet-parallel4.log 2>&1 & echo started" 2>&1

Commit, build, start planet


Command running in background with ID: b7mspbm0h. Output is being written to: /tmp/claude-0/-root-traccar-geocoder/af1c4baa-0f9a-4420-a8b3-ce20dc28c8b9/tasks/b7mspbm0h.output


sleep 60 && ssh node3 "tail -3 ~/planet-parallel4.log" 2>&1

Quick check build started


  Processed 4871000 node blocks...
  Processed 4872000 node blocks...
  Processed 4873000 node blocks...


Running. Let me check every 10 minutes.


sleep 600 && ssh node3 "grep -E 'Assembled.*admin|Parallel admin|polygon|Done|Writing|street ways|Pass 3' ~/planet-parallel4.log | tail -5" 2>&1

Check after 10 min


  47826371 street ways
  947425 admin polygon rings
Waiting for admin polygon S2 covering to complete...
Admin polygon S2 covering complete (2049797 cells).
  Processing 47826371 street ways...


**947,425 admin polygons** — up from 947,423! Gained 2 more from removing the sub-way limit. Still in S2 cell computation. Let me wait for completion.


sleep 600 && ssh node3 "tail -3 ~/planet-parallel4.log; ls ~/planet-parallel4/ | wc -l" 2>&1

Check after 10 min


  Processing 72817 interpolation ways...
  Interpolation ways done.
  Merging thread-local results...
0


sleep 600 && ssh node3 "tail -3 ~/planet-parallel4.log; ls ~/planet-parallel4/ | wc -l; ps aux | grep 'build-index.*planet-parallel4' | grep -v grep | wc -l" 2>&1

Check after 10 more min


Resolved 41798/72817 interpolation ways
Deduplicating...
Writing index files to /home/michtest/planet-parallel4...
0
2


sleep 600 && ssh node3 "tail -3 ~/planet-parallel4.log; ls ~/planet-parallel4/ | wc -l" 2>&1

Check after 10 more min


Resolved 41798/72817 interpolation ways
Deduplicating...
Writing index files to /home/michtest/planet-parallel4...
0


sleep 600 && ssh node3 "tail -5 ~/planet-parallel4.log; ls ~/planet-parallel4/ | wc -l" 2>&1

Check after 10 more


S2 cell computation complete.
Resolving interpolation endpoints...
Resolved 41798/72817 interpolation ways
Deduplicating...
Writing index files to /home/michtest/planet-parallel4...
1


sleep 600 && ssh node3 "tail -3 ~/planet-parallel4.log; ls ~/planet-parallel4/ | wc -l" 2>&1

Check after 10 more


The user doesn't want to proceed with this tool use. The tool use was rejected (eg. if it was a file edit, the new_string was NOT written to the file). STOP what you are doing and wait for the user to tell you how to proceed.

Note: The user's next message may contain a correction or preference. Pay close attention — if they explain what went wrong or how they'd prefer you to work, consider saving that to memory for future sessions.


[Request interrupted by user for tool use]


what is the statud and what are you working towatds now?


ssh node3 "tail -5 ~/planet-parallel4.log; ls ~/planet-parallel4/ | wc -l; ps aux | grep 'build-index.*planet-parallel4' | grep -v grep | wc -l" 2>&1

Current status


S2 cell computation complete.
Resolving interpolation endpoints...
Resolved 41798/72817 interpolation ways
Deduplicating...
Writing index files to /home/michtest/planet-parallel4...
4
2


**Current status**: Planet build #4 is writing index files (4/14 done). This is the version with no sub-way limit on the backtracking retry. It produced **947,425 admin polygons** (up from 947,423 — gained 2 more).

**What I'm working toward**: Closing the last 10 missing admin boundaries at planet scale. The previous run had 10 missing unique name|level pairs. This run removed the sub-way count limit so the backtracking retry can handle larger relations. Once it finishes writing (~20 min), I'll run the dedup comparison to see if any of the 10 are now found.

**Progress so far**:
- Ways: 100% ✓
- Addresses: 100% ✓
- Admin: was 12 missing → 10 missing → hopefully fewer now
- Target: 0 missing (or as close as possible)

The writes are slow because planet geo_cells.bin is ~6.6GB. Should be done in ~20 minutes.


atill feels ver slow for that size of file, this server is running on ssds, what is causing the spowdown?


ssh node3 "iostat -x 1 3 2>/dev/null | tail -20 || (cat /proc/diskstats | head -5; echo '---'; ls -lh ~/planet-parallel4/; echo '---'; df -h /home/michtest)" 2>&1

Check IO and what's actually happening


(Bash completed with no output)


ssh node3 "ls -lh ~/planet-parallel4/; echo '---'; cat /proc/meminfo | grep -E 'MemTotal|MemAvail|SwapTotal|SwapFree|Dirty|Writeback'" 2>&1

Check file sizes and memory/swap state


total 9.1G
-rw-rw-r-- 1 michtest michtest 704M Mar 22 09:55 addr_entries.bin
-rw-rw-r-- 1 michtest michtest 6.2G Mar 22 09:57 geo_cells.bin
-rw-rw-r-- 1 michtest michtest 1.4M Mar 22 09:55 interp_entries.bin
-rw-rw-r-- 1 michtest michtest 2.2G Mar 22 09:54 street_entries.bin
---
MemTotal:       183417444 kB
MemAvailable:   81244640 kB
SwapTotal:      53026812 kB
SwapFree:       52498576 kB
Dirty:                 4 kB
Writeback:             0 kB
WritebackTmp:          0 kB


81GB free RAM, 0 dirty pages, 0 writeback. The disk isn't the bottleneck — the process is CPU-bound on the **deduplication step** before writing each file. The `Deduplicating...` step in the code sorts and deduplicates cell maps, which is O(n log n) for hundreds of millions of entries.


ssh node3 "ps aux | grep 'build-index.*planet-parallel4' | grep -v grep | awk '{print \$3\"% CPU\", \$6/1024\"MB\"}'" 2>&1

Check CPU usage


0.0% CPU 0.875MB
634% CPU 97606.7MB


634% CPU, 95GB RSS — it's actively computing, not waiting on IO. The write phase includes per-file deduplication and serialization of the 332M cell entries. This is CPU-bound single-threaded work for each file.


sleep 300 && ssh node3 "ls -lh ~/planet-parallel4/ | wc -l; ls -lh ~/planet-parallel4/; tail -3 ~/planet-parallel4.log" 2>&1

Check after 5 min


could we write qll the files at once to increase parallisation of the dedupkication process?


1215:static void deduplicate(Map& cell_map) {
2847:        std::cerr << "Deduplicating..." << std::endl;
2849:            auto f1 = std::async(std::launch::async, [&]{ deduplicate(data.cell_to_ways); });
2850:            auto f2 = std::async(std::launch::async, [&]{ deduplicate(data.cell_to_addrs); });
2851:            auto f3 = std::async(std::launch::async, [&]{ deduplicate(data.cell_to_interps); });
2852:            auto f4 = std::async(std::launch::async, [&]{ deduplicate(data.cell_to_admin); });
2863:    std::cerr << "Writing index files to " << output_dir << "..." << std::endl;


Dedup is already parallel (4 async tasks). Let me check the write phase.


  2863→    std::cerr << "Writing index files to " << output_dir << "..." << std::endl;
  2864→
  2865→    if (multi_output) {
  2866→        // Write all 3 index modes in parallel (they read shared data, write to separate dirs)
  2867→        auto wf1 = std::async(std::launch::async, [&]{ write_index(data, output_dir + "/full", IndexMode::Full); });
  2868→        auto wf2 = std::async(std::launch::async, [&]{ write_index(data, output_dir + "/no-addresses", IndexMode::NoAddresses); });
  2869→        auto wf3 = std::async(std::launch::async, [&]{ write_index(data, output_dir + "/admin-only", IndexMode::AdminOnly); });
  2870→        wf1.get(); wf2.get(); wf3.get();
  2871→    } else {
  2872→        write_index(data, output_dir, mode);
  2873→    }
  2874→
  2875→    if (generate_continents) {
  2876→        // Process continents in parallel (each filters independently from shared data)
  2877→        std::vector<std::future<void>> continent_futures;
  2878→        std::mutex cerr_mutex;
  2879→        for (const auto& continent : kContinents) {
  2880→            continent_futures.push_back(std::async(std::launch::async, [&, continent]() {
  2881→                {
  2882→                    std::lock_guard<std::mutex> lock(cerr_mutex);
  2883→                    std::cerr << "Filtering for continent: " << continent.name << "..." << std::endl;
  2884→                }
  2885→                auto subset = filter_by_bbox(data, continent);
  2886→                std::string base = output_dir + "/" + continent.name;
  2887→                ensure_dir(base);
  2888→                if (multi_output) {
  2889→                    // Write modes in parallel within each continent
  2890→                    auto cf1 = std::async(std::launch::async, [&]{ write_index(subset, base + "/full", IndexMode::Full); });
  2891→                    auto cf2 = std::async(std::launch::async, [&]{ write_index(subset, base + "/no-addresses", IndexMode::NoAddresses); });
  2892→                    auto cf3 = std::async(std::launch::async, [&]{ write_index(subset, base + "/admin-only", IndexMode::AdminOnly); });
  2893→                    cf1.get(); cf2.get(); cf3.get();
  2894→                } else {
  2895→                    write_index(subset, base, mode);
  2896→                }
  2897→                {
  2898→                    std::lock_guard<std::mutex> lock(cerr_mutex);
  2899→                    std::cerr << "Done continent: " << continent.name << std::endl;
  2900→                }
  2901→            }));
  2902→        }
  2903→        for (auto& f : continent_futures) {
  2904→            f.get();
  2905→        }
  2906→    }
  2907→
  2908→    std::cerr << "Done." << std::endl;
  2909→    return 0;
  2910→}
  2911→

    std::cerr << "Writing index files to " << output_dir << "..." << std::endl;

    if (multi_output) {
        // Write all 3 index modes in parallel (they read shared data, write to separate dirs)
        auto wf1 = std::async(std::launch::async, [&]{ write_index(data, output_dir + "/full", IndexMode::Full); });
        auto wf2 = std::async(std::launch::async, [&]{ write_index(data, output_dir + "/no-addresses", IndexMode::NoAddresses); });
        auto wf3 = std::async(std::launch::async, [&]{ write_index(data, output_dir + "/admin-only", IndexMode::AdminOnly); });
        wf1.get(); wf2.get(); wf3.get();
    } else {
        write_index(data, output_dir, mode);
    }

    if (generate_continents) {
        // Process continents in parallel (each filters independently from shared data)
        std::vector<std::future<void>> continent_futures;
        std::mutex cerr_mutex;
        for (const auto& continent : kContinents) {
            continent_futures.push_back(std::async(std::launch::async, [&, continent]() {
                {
                    std::lock_guard<std::mutex> lock(cerr_mutex);
                    std::cerr << "Filtering for continent: " << continent.name << "..." << std::endl;
                }
                auto subset = filter_by_bbox(data, continent);
                std::string base = output_dir + "/" + continent.name;
                ensure_dir(base);
                if (multi_output) {
                    // Write modes in parallel within each continent
                    auto cf1 = std::async(std::launch::async, [&]{ write_index(subset, base + "/full", IndexMode::Full); });
                    auto cf2 = std::async(std::launch::async, [&]{ write_index(subset, base + "/no-addresses", IndexMode::NoAddresses); });
                    auto cf3 = std::async(std::launch::async, [&]{ write_index(subset, base + "/admin-only", IndexMode::AdminOnly); });
                    cf1.get(); cf2.get(); cf3.get();
                } else {
                    write_index(subset, base, mode);
                }
                {
                    std::lock_guard<std::mutex> lock(cerr_mutex);
                    std::cerr << "Done continent: " << continent.name << std::endl;
                }
            }));
        }
        for (auto& f : continent_futures) {
            f.get();
        }
    }

    std::cerr << "Done." << std::endl;
    return 0;
}


Now let me see `write_index` — that's where the sequential file writes happen.


Grep

^static void write_index|^void write_index


1769:static void write_index(const ParsedData& data, const std::string& output_dir, IndexMode mode) {


  1769→static void write_index(const ParsedData& data, const std::string& output_dir, IndexMode mode) {
  1770→    ensure_dir(output_dir);
  1771→
  1772→    bool write_streets = (mode != IndexMode::AdminOnly);
  1773→    bool write_addresses = (mode == IndexMode::Full);
  1774→
  1775→    if (write_streets) {
  1776→        // Merged geo cell index for streets, addresses, and interpolation
  1777→        std::set<uint64_t> all_geo_cells;
  1778→        for (const auto& [id, _] : data.cell_to_ways) all_geo_cells.insert(id);
  1779→        if (write_addresses) {
  1780→            for (const auto& [id, _] : data.cell_to_addrs) all_geo_cells.insert(id);
  1781→            for (const auto& [id, _] : data.cell_to_interps) all_geo_cells.insert(id);
  1782→        }
  1783→        std::vector<uint64_t> sorted_geo_cells(all_geo_cells.begin(), all_geo_cells.end());
  1784→
  1785→        auto street_offsets = write_entries(output_dir + "/street_entries.bin", sorted_geo_cells, data.cell_to_ways);
  1786→
  1787→        std::unordered_map<uint64_t, uint32_t> addr_offsets;
  1788→        std::unordered_map<uint64_t, uint32_t> interp_offsets;
  1789→        if (write_addresses) {
  1790→            addr_offsets = write_entries(output_dir + "/addr_entries.bin", sorted_geo_cells, data.cell_to_addrs);
  1791→            interp_offsets = write_entries(output_dir + "/interp_entries.bin", sorted_geo_cells, data.cell_to_interps);
  1792→        }
  1793→
  1794→        {
  1795→            std::ofstream f(output_dir + "/geo_cells.bin", std::ios::binary);
  1796→            for (uint64_t cell_id : sorted_geo_cells) {
  1797→                f.write(reinterpret_cast<const char*>(&cell_id), sizeof(cell_id));
  1798→                auto write_offset = [&](const std::unordered_map<uint64_t, uint32_t>& offsets) {
  1799→                    auto it = offsets.find(cell_id);
  1800→                    uint32_t offset = (it != offsets.end()) ? it->second : NO_DATA;
  1801→                    f.write(reinterpret_cast<const char*>(&offset), sizeof(offset));
  1802→                };
  1803→                write_offset(street_offsets);
  1804→                write_offset(addr_offsets);
  1805→                write_offset(interp_offsets);
  1806→            }
  1807→        }
  1808→
  1809→        std::cerr << "geo index: " << sorted_geo_cells.size() << " cells ("
  1810→                  << data.ways.size() << " ways, "
  1811→                  << data.addr_points.size() << " addrs, "
  1812→                  << data.interp_ways.size() << " interps)" << std::endl;
  1813→    }
  1814→
  1815→    write_cell_index(output_dir + "/admin_cells.bin", output_dir + "/admin_entries.bin", data.cell_to_admin);
  1816→    std::cerr << "admin index: " << data.cell_to_admin.size() << " cells, " << data.admin_polygons.size() << " polygons" << std::endl;
  1817→
  1818→    if (write_streets) {
  1819→        // Street ways
  1820→        {
  1821→            std::ofstream f(output_dir + "/street_ways.bin", std::ios::binary);
  1822→            f.write(reinterpret_cast<const char*>(data.ways.data()), data.ways.size() * sizeof(WayHeader));
  1823→        }
  1824→
  1825→        // Street nodes
  1826→        {
  1827→            std::ofstream f(output_dir + "/street_nodes.bin", std::ios::binary);
  1828→            f.write(reinterpret_cast<const char*>(data.street_nodes.data()), data.street_nodes.size() * sizeof(NodeCoord));
  1829→        }
  1830→
  1831→        if (write_addresses) {
  1832→            // Address points
  1833→            {
  1834→                std::ofstream f(output_dir + "/addr_points.bin", std::ios::binary);
  1835→                f.write(reinterpret_cast<const char*>(data.addr_points.data()), data.addr_points.size() * sizeof(AddrPoint));
  1836→            }
  1837→
  1838→            // Interpolation ways
  1839→            {
  1840→                std::ofstream f(output_dir + "/interp_ways.bin", std::ios::binary);
  1841→                f.write(reinterpret_cast<const char*>(data.interp_ways.data()), data.interp_ways.size() * sizeof(InterpWay));
  1842→            }
  1843→
  1844→            // Interpolation nodes
  1845→            {
  1846→                std::ofstream f(output_dir + "/interp_nodes.bin", std::ios::binary);
  1847→                f.write(reinterpret_cast<const char*>(data.interp_nodes.data()), data.interp_nodes.size() * sizeof(NodeCoord));
  1848→            }
  1849→        }
  1850→    }
  1851→
  1852→    // Admin polygons
  1853→    {
  1854→        std::ofstream f(output_dir + "/admin_polygons.bin", std::ios::binary);
  1855→        f.write(reinterpret_cast<const char*>(data.admin_polygons.data()), data.admin_polygons.size() * sizeof(AdminPolygon));
  1856→    }
  1857→
  1858→    // Admin vertices

static void write_index(const ParsedData& data, const std::string& output_dir, IndexMode mode) {
    ensure_dir(output_dir);

    bool write_streets = (mode != IndexMode::AdminOnly);
    bool write_addresses = (mode == IndexMode::Full);

    if (write_streets) {
        // Merged geo cell index for streets, addresses, and interpolation
        std::set<uint64_t> all_geo_cells;
        for (const auto& [id, _] : data.cell_to_ways) all_geo_cells.insert(id);
        if (write_addresses) {
            for (const auto& [id, _] : data.cell_to_addrs) all_geo_cells.insert(id);
            for (const auto& [id, _] : data.cell_to_interps) all_geo_cells.insert(id);
        }
        std::vector<uint64_t> sorted_geo_cells(all_geo_cells.begin(), all_geo_cells.end());

        auto street_offsets = write_entries(output_dir + "/street_entries.bin", sorted_geo_cells, data.cell_to_ways);

        std::unordered_map<uint64_t, uint32_t> addr_offsets;
        std::unordered_map<uint64_t, uint32_t> interp_offsets;
        if (write_addresses) {
            addr_offsets = write_entries(output_dir + "/addr_entries.bin", sorted_geo_cells, data.cell_to_addrs);
            interp_offsets = write_entries(output_dir + "/interp_entries.bin", sorted_geo_cells, data.cell_to_interps);
        }

        {
            std::ofstream f(output_dir + "/geo_cells.bin", std::ios::binary);
            for (uint64_t cell_id : sorted_geo_cells) {
                f.write(reinterpret_cast<const char*>(&cell_id), sizeof(cell_id));
                auto write_offset = [&](const std::unordered_map<uint64_t, uint32_t>& offsets) {
                    auto it = offsets.find(cell_id);
                    uint32_t offset = (it != offsets.end()) ? it->second : NO_DATA;
                    f.write(reinterpret_cast<const char*>(&offset), sizeof(offset));
                };
                write_offset(street_offsets);
                write_offset(addr_offsets);
                write_offset(interp_offsets);
            }
        }

        std::cerr << "geo index: " << sorted_geo_cells.size() << " cells ("
                  << data.ways.size() << " ways, "
                  << data.addr_points.size() << " addrs, "
                  << data.interp_ways.size() << " interps)" << std::endl;
    }

    write_cell_index(output_dir + "/admin_cells.bin", output_dir + "/admin_entries.bin", data.cell_to_admin);
    std::cerr << "admin index: " << data.cell_to_admin.size() << " cells, " << data.admin_polygons.size() << " polygons" << std::endl;

    if (write_streets) {
        // Street ways
        {
            std::ofstream f(output_dir + "/street_ways.bin", std::ios::binary);
            f.write(reinterpret_cast<const char*>(data.ways.data()), data.ways.size() * sizeof(WayHeader));
        }

        // Street nodes
        {
            std::ofstream f(output_dir + "/street_nodes.bin", std::ios::binary);
            f.write(reinterpret_cast<const char*>(data.street_nodes.data()), data.street_nodes.size() * sizeof(NodeCoord));
        }

        if (write_addresses) {
            // Address points
            {
                std::ofstream f(output_dir + "/addr_points.bin", std::ios::binary);
                f.write(reinterpret_cast<const char*>(data.addr_points.data()), data.addr_points.size() * sizeof(AddrPoint));
            }

            // Interpolation ways
            {
                std::ofstream f(output_dir + "/interp_ways.bin", std::ios::binary);
                f.write(reinterpret_cast<const char*>(data.interp_ways.data()), data.interp_ways.size() * sizeof(InterpWay));
            }

            // Interpolation nodes
            {
                std::ofstream f(output_dir + "/interp_nodes.bin", std::ios::binary);
                f.write(reinterpret_cast<const char*>(data.interp_nodes.data()), data.interp_nodes.size() * sizeof(NodeCoord));
            }
        }
    }

    // Admin polygons
    {
        std::ofstream f(output_dir + "/admin_polygons.bin", std::ios::binary);
        f.write(reinterpret_cast<const char*>(data.admin_polygons.data()), data.admin_polygons.size() * sizeof(AdminPolygon));
    }

    // Admin vertices


I see the bottleneck. The `write_entries` calls at lines 1785-1791 are sequential — `street_entries.bin` must finish before `addr_entries.bin` starts. And `write_entries` likely iterates through the sorted cell list doing hash lookups. The `geo_cells.bin` write at line 1796 also iterates 332M cells sequentially. Plus the `std::set` construction at lines 1777-1782 is O(n log n) for hundreds of millions of entries.

The fix: parallelize the entry writes and the raw data writes.


Edit

    if (write_streets) {
        // Merged geo cell index for streets, addresses, and interpolation
        std::set<uint64_t> all_geo_cells;
        for (const auto& [id, _] : data.cell_to_ways) all_geo_cells.insert(id);
        if (write_addresses) {
            for (const auto& [id, _] : data.cell_to_addrs) all_geo_cells.insert(id);
            for (const auto& [id, _] : data.cell_to_interps) all_geo_cells.insert(id);
        }
        std::vector<uint64_t> sorted_geo_cells(all_geo_cells.begin(), all_geo_cells.end());

        auto street_offsets = write_entries(output_dir + "/street_entries.bin", sorted_geo_cells, data.cell_to_ways);

        std::unordered_map<uint64_t, uint32_t> addr_offsets;
        std::unordered_map<uint64_t, uint32_t> interp_offsets;
        if (write_addresses) {
            addr_offsets = write_entries(output_dir + "/addr_entries.bin", sorted_geo_cells, data.cell_to_addrs);
            interp_offsets = write_entries(output_dir + "/interp_entries.bin", sorted_geo_cells, data.cell_to_interps);
        }

    if (write_streets) {
        // Merged geo cell index for streets, addresses, and interpolation
        // Use sorted vector instead of std::set for faster construction
        std::vector<uint64_t> sorted_geo_cells;
        {
            std::unordered_set<uint64_t> all_geo_cells;
            all_geo_cells.reserve(data.cell_to_ways.size() + data.cell_to_addrs.size());
            for (const auto& [id, _] : data.cell_to_ways) all_geo_cells.insert(id);
            if (write_addresses) {
                for (const auto& [id, _] : data.cell_to_addrs) all_geo_cells.insert(id);
                for (const auto& [id, _] : data.cell_to_interps) all_geo_cells.insert(id);
            }
            sorted_geo_cells.assign(all_geo_cells.begin(), all_geo_cells.end());
            std::sort(sorted_geo_cells.begin(), sorted_geo_cells.end());
        }

        // Write entry files in parallel
        std::unordered_map<uint64_t, uint32_t> street_offsets, addr_offsets, interp_offsets;
        {
            auto f1 = std::async(std::launch::async, [&]() {
                return write_entries(output_dir + "/street_entries.bin", sorted_geo_cells, data.cell_to_ways);
            });
            if (write_addresses) {
                auto f2 = std::async(std::launch::async, [&]() {
                    return write_entries(output_dir + "/addr_entries.bin", sorted_geo_cells, data.cell_to_addrs);
                });
                auto f3 = std::async(std::launch::async, [&]() {
                    return write_entries(output_dir + "/interp_entries.bin", sorted_geo_cells, data.cell_to_interps);
                });
                addr_offsets = f2.get();
                interp_offsets = f3.get();
            }
            street_offsets = f1.get();
        }