From 58ad6e30081e76a311324eae4a374900c58e6f77 Mon Sep 17 00:00:00 2001 From: Claude Date: Wed, 23 Sep 2026 20:02:49 +0000 Subject: [PATCH] Use the faster bit interleave for encode_quadkey itself encode_vertex() computed exactly the same thing as encode_quadkey(), so there is no reason to have both. Give encode_quadkey() the branch-free implementation, which also speeds up the default encode_index, and have the shared node code call it directly. The unit test now compares it against the old bit-at-a-time loop and checks that decode_quadkey() reverses it. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01P2nBqZisxNQfmEmon3vE9v --- geometry.cpp | 2 +- main.cpp | 2 +- projection.cpp | 42 +++++++++++++----------------------------- projection.hpp | 1 - serial.cpp | 2 +- unit.cpp | 26 +++++++++++++++++++++++--- 6 files changed, 39 insertions(+), 36 deletions(-) diff --git a/geometry.cpp b/geometry.cpp index 9ece03ed..fc50afd8 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -249,7 +249,7 @@ void add_shared_node_to_bloom(std::string &shared_nodes_bloom, unsigned long lon // Is the vertex at world coordinates wx, wy one of the nodes in the global list of shared nodes? bool is_shared_node(long long wx, long long wy, struct node const *shared_nodes_map, size_t nodepos, std::string const &shared_nodes_bloom) { struct node n; - n.index = encode_vertex((unsigned) wx, (unsigned) wy); + n.index = encode_quadkey((unsigned) wx, (unsigned) wy); size_t word; unsigned long long mask, bits; diff --git a/main.cpp b/main.cpp index e72170f0..288f21e7 100644 --- a/main.cpp +++ b/main.cpp @@ -2122,7 +2122,7 @@ std::pair read_input(std::vector &sources, char *fname, i #endif struct node n; - n.index = encode_vertex((unsigned) x, (unsigned) y); + n.index = encode_quadkey((unsigned) x, (unsigned) y); fwrite_check((char *) &n, sizeof(struct node), 1, readers[0].nodefile, &readers[0].nodepos, "vertices"); } diff --git a/projection.cpp b/projection.cpp index 7a4d645b..af0b4644 100644 --- a/projection.cpp +++ b/projection.cpp @@ -156,19 +156,20 @@ void decode_hilbert(unsigned long long index, unsigned *wx, unsigned *wy) { hilbert_d2xy(1LL << 32, index, wx, wy); } +// Spread the 32 bits of v out into the even bits of a 64-bit value +static inline unsigned long long spread_bits(unsigned int v) { + unsigned long long x = v; + x = (x | (x << 16)) & 0x0000FFFF0000FFFFULL; + x = (x | (x << 8)) & 0x00FF00FF00FF00FFULL; + x = (x | (x << 4)) & 0x0F0F0F0F0F0F0F0FULL; + x = (x | (x << 2)) & 0x3333333333333333ULL; + x = (x | (x << 1)) & 0x5555555555555555ULL; + return x; +} + +// Interleave the bits of wx and wy, with each bit of wx above the same bit of wy unsigned long long encode_quadkey(unsigned int wx, unsigned int wy) { - unsigned long long out = 0; - - int i; - for (i = 0; i < 32; i++) { - unsigned long long v = ((wx >> (32 - (i + 1))) & 1) << 1; - v |= (wy >> (32 - (i + 1))) & 1; - v = v << (64 - 2 * (i + 1)); - - out |= v; - } - - return out; + return (spread_bits(wx) << 1) | spread_bits(wy); } static std::atomic decodex[256]; @@ -217,20 +218,3 @@ void set_projection_or_exit(const char *optarg) { exit(EXIT_ARGS); } } - -// Spread the 32 bits of v out into the even bits of a 64-bit value -static inline unsigned long long spread_bits(unsigned int v) { - unsigned long long x = v; - x = (x | (x << 16)) & 0x0000FFFF0000FFFFULL; - x = (x | (x << 8)) & 0x00FF00FF00FF00FFULL; - x = (x | (x << 4)) & 0x0F0F0F0F0F0F0F0FULL; - x = (x | (x << 2)) & 0x3333333333333333ULL; - x = (x | (x << 1)) & 0x5555555555555555ULL; - return x; -} - -// The same as encode_quadkey(), but faster, for the vertices of the list of shared nodes, -// so that nodes that are near each other are also near each other in the sorted list. -unsigned long long encode_vertex(unsigned int wx, unsigned int wy) { - return (spread_bits(wx) << 1) | spread_bits(wy); -} diff --git a/projection.hpp b/projection.hpp index 96b096ed..6436f132 100644 --- a/projection.hpp +++ b/projection.hpp @@ -26,6 +26,5 @@ void decode_quadkey(unsigned long long index, unsigned *wx, unsigned *wy); unsigned long long encode_hilbert(unsigned int wx, unsigned int wy); void decode_hilbert(unsigned long long index, unsigned *wx, unsigned *wy); -unsigned long long encode_vertex(unsigned int wx, unsigned int wy); #endif diff --git a/serial.cpp b/serial.cpp index cefd1176..cf3de5ba 100644 --- a/serial.cpp +++ b/serial.cpp @@ -475,7 +475,7 @@ static void add_scaled_node(struct reader *r, serialization_state *sst, draw g) long long y = SHIFT_LEFT(g.y); struct node n; - n.index = encode_vertex((unsigned) x, (unsigned) y); + n.index = encode_quadkey((unsigned) x, (unsigned) y); fwrite_check((char *) &n, sizeof(struct node), 1, r->nodefile, &r->nodepos, sst->fname); } diff --git a/unit.cpp b/unit.cpp index c10eddb9..43e01dd7 100644 --- a/unit.cpp +++ b/unit.cpp @@ -129,11 +129,31 @@ TEST_CASE("Bit reversal", "bit reversal") { REQUIRE(bit_reverse(0xF3D912481E6A2C48) == 0x1234567812489BCF); } -TEST_CASE("Vertex encoding matches quadkey encoding", "[projection]") { +// The bit-at-a-time quadkey encoding that encode_quadkey() used to use +static unsigned long long reference_quadkey(unsigned int wx, unsigned int wy) { + unsigned long long out = 0; + + for (int i = 0; i < 32; i++) { + unsigned long long v = ((wx >> (32 - (i + 1))) & 1) << 1; + v |= (wy >> (32 - (i + 1))) & 1; + v = v << (64 - 2 * (i + 1)); + + out |= v; + } + + return out; +} + +TEST_CASE("Quadkey encoding", "[projection]") { unsigned int values[] = {0, 1, 2, 0x7FFFFFFF, 0x80000000, 0xFFFFFFFF, 0x12345678, 0xDEADBEEF}; for (unsigned int x : values) { for (unsigned int y : values) { - REQUIRE(encode_vertex(x, y) == encode_quadkey(x, y)); + REQUIRE(encode_quadkey(x, y) == reference_quadkey(x, y)); + + unsigned wx, wy; + decode_quadkey(encode_quadkey(x, y), &wx, &wy); + REQUIRE(wx == x); + REQUIRE(wy == y); } } @@ -142,7 +162,7 @@ TEST_CASE("Vertex encoding matches quadkey encoding", "[projection]") { seed = seed * 6364136223846793005ULL + 1442695040888963407ULL; unsigned int x = seed >> 32; unsigned int y = seed; - REQUIRE(encode_vertex(x, y) == encode_quadkey(x, y)); + REQUIRE(encode_quadkey(x, y) == reference_quadkey(x, y)); } }