Now modify the S2 phase to keep sorted pairs for ways (instead of building hash map), and update the write phase to use them:


Edit

            auto build_cell_map = [](std::vector<std::vector<CellItem>>& thread_pairs,
                                      std::unordered_map<uint64_t, std::vector<uint32_t>>& out) {
                // Concatenate all thread-local vectors
                size_t total = 0;
                for (auto& v : thread_pairs) total += v.size();
                std::vector<CellItem> all;
                all.reserve(total);
                for (auto& v : thread_pairs) {
                    all.insert(all.end(), v.begin(), v.end());
                    v.clear(); v.shrink_to_fit();
                }
                // Sort by cell_id (cache-friendly sequential access)
                std::sort(all.begin(), all.end(), [](const CellItem& a, const CellItem& b) {
                    return a.cell_id < b.cell_id;
                });
                // Group into cell map
                out.reserve(total / 2);
                for (size_t i = 0; i < all.size(); ) {
                    size_t j = i;
                    while (j < all.size() && all[j].cell_id == all[i].cell_id) j++;
                    auto& vec = out[all[i].cell_id];
                    vec.reserve(j - i);
                    for (size_t k = i; k < j; k++) vec.push_back(all[k].item_id);
                    i = j;
                }
            };

            // Build way and interp maps in parallel
            auto f_ways = std::async(std::launch::async, [&] {
                build_cell_map(way_pairs, data.cell_to_ways);
            });
            auto f_interps = std::async(std::launch::async, [&] {
                build_cell_map(interp_pairs, data.cell_to_interps);
            });
            f_ways.get();
            f_interps.get();

            // Concatenate + sort helper for flat CellItem pairs
            auto concat_and_sort = [](std::vector<std::vector<CellItem>>& thread_pairs) -> std::vector<CellItemPair> {
                size_t total = 0;
                for (auto& v : thread_pairs) total += v.size();
                std::vector<CellItemPair> all;
                all.reserve(total);
                for (auto& v : thread_pairs) {
                    for (auto& ci : v) all.push_back({ci.cell_id, ci.item_id});
                    v.clear(); v.shrink_to_fit();
                }
                std::sort(all.begin(), all.end(), [](const CellItemPair& a, const CellItemPair& b) {
                    return a.cell_id < b.cell_id || (a.cell_id == b.cell_id && a.item_id < b.item_id);
                });
                return all;
            };

            // Build hash map from sorted pairs (for interps + cell_to_addrs union)
            auto build_cell_map = [](const std::vector<CellItemPair>& sorted,
                                      std::unordered_map<uint64_t, std::vector<uint32_t>>& out) {
                out.reserve(sorted.size() / 2);
                for (size_t i = 0; i < sorted.size(); ) {
                    size_t j = i;
                    while (j < sorted.size() && sorted[j].cell_id == sorted[i].cell_id) j++;
                    auto& vec = out[sorted[i].cell_id];
                    vec.reserve(j - i);
                    for (size_t k = i; k < j; k++) vec.push_back(sorted[k].item_id);
                    i = j;
                }
            };

            // Sort ways and interps in parallel. Keep way pairs sorted for direct writing.
            std::vector<CellItemPair> sorted_way_pairs, sorted_interp_pairs;
            auto f_ways = std::async(std::launch::async, [&] {
                sorted_way_pairs = concat_and_sort(way_pairs);
            });
            auto f_interps = std::async(std::launch::async, [&] {
                sorted_interp_pairs = concat_and_sort(interp_pairs);
                build_cell_map(sorted_interp_pairs, data.cell_to_interps);
            });
            f_ways.get();
            f_interps.get();

            // Build cell_to_ways from sorted pairs (still needed for sorted_geo_cells union + dedup)
            build_cell_map(sorted_way_pairs, data.cell_to_ways);