├── .gitignore ├── Makefile ├── morton.h ├── LICENSE ├── README.md ├── test.c ├── morton.c └── bench.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | test 3 | bench 4 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: check 2 | check: test bench 3 | ./test 4 | ./bench 5 | 6 | .PHONY: clean 7 | clean: 8 | rm -f *.o test bench 9 | 10 | test: morton.o test.o 11 | test.o: test.c morton.h Makefile 12 | bench: morton.o bench.o 13 | bench.o: bench.c morton.h Makefile 14 | morton.o: morton.c morton.h Makefile 15 | -------------------------------------------------------------------------------- /morton.h: -------------------------------------------------------------------------------- 1 | #ifndef MORTON_H_ 2 | #define MORTON_H_ 3 | 4 | #if defined(__GNUC__) || (defined(__has_attribute) && __has_attribute(const)) 5 | #define MORTON_PURE __attribute__((__const__)) 6 | #else 7 | #define MORTON_PURE 8 | #endif 9 | 10 | struct Morton { 11 | unsigned lo; 12 | unsigned hi; 13 | }; 14 | 15 | struct Morton unmorton(unsigned long long) MORTON_PURE; 16 | unsigned long long morton(unsigned, unsigned) MORTON_PURE; 17 | 18 | #endif /* MORTON_H_ */ 19 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | ISC License 2 | 3 | Copyright 2024 Justine Alexandra Roberts Tunney 4 | 5 | Permission to use, copy, modify, and/or distribute this software for 6 | any purpose with or without fee is hereby granted, provided that the 7 | above copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 10 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 11 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 12 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 13 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 14 | PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 15 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 16 | PERFORMANCE OF THIS SOFTWARE. 17 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Morton 2 | 3 | This repository implements bit interleaving in C/C++. 4 | 5 | morton(0b1100,0b0011) 6 | │││└──┐││││ 7 | ││└─┐ │││││ 8 | │└┐ │ │││││ 9 | │┌─────┘│││ 10 | │││ │ │ │││ 11 | │││┌────┘││ 12 | │││││ │ ││ 13 | │││││┌───┘│ 14 | │││││││┌──┘ 15 | 0b10100101 16 | 17 | By default a SWAR implementation is used which takes 2 nanoseconds. If 18 | `-mbmi2` is available, then `morton()` and `unmorton()` take less than 19 | one nanosecond. If no compiler optimizations are used then these funcs 20 | take 10 nanoseconds. The cost is constant since this implementation is 21 | fully branchless. 22 | 23 | ## See Also 24 | 25 | - 26 | - 27 | - 28 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | /* -*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;coding:utf-8 -*- 2 | vi: set et ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi 3 | 4 | Copyright 2024 Justine Alexandra Roberts Tunney 5 | 6 | Permission to use, copy, modify, and/or distribute this software for 7 | any purpose with or without fee is hereby granted, provided that the 8 | above copyright notice and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 | PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 16 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 | PERFORMANCE OF THIS SOFTWARE. */ 18 | 19 | #include "morton.h" 20 | 21 | #include 22 | 23 | static inline unsigned rand32(void) { 24 | /* Knuth, D.E., "The Art of Computer Programming," Vol 2, 25 | Seminumerical Algorithms, Third Edition, Addison-Wesley, 1998, 26 | p. 106 (line 26) & p. 108 */ 27 | static unsigned long long lcg = 1; 28 | lcg *= 6364136223846793005; 29 | lcg += 1442695040888963407; 30 | return lcg >> 32; 31 | } 32 | 33 | int main() { 34 | assert(morton(0, 0) == 0); 35 | assert(morton(0, 1) == 1); 36 | assert(morton(1, 0) == 2); 37 | assert(morton(1, 1) == 3); 38 | #if defined(__GNUC__) && !defined(__STRICT_ANSI__) 39 | assert(morton(0b0011, 0b0000) == 0b1010); 40 | assert(morton(0b0000, 0b0011) == 0b0101); 41 | assert(morton(0b1100, 0b0011) == 0b10100101); 42 | #endif 43 | assert(morton(0x347210d1u, 0xc6843fadu) == 0x5a346a180755e653ull); 44 | int N = 2000; 45 | for (int i = 0; i < N; ++i) { 46 | unsigned x = rand32(); 47 | for (int j = 0; j < N; ++j) { 48 | unsigned y = rand32(); 49 | unsigned long long z = morton(x, y); 50 | struct Morton m = unmorton(z); 51 | assert(m.lo == x); 52 | assert(m.hi == y); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /morton.c: -------------------------------------------------------------------------------- 1 | /* -*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;coding:utf-8 -*- 2 | vi: set et ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi 3 | 4 | Copyright 2024 Justine Alexandra Roberts Tunney 5 | 6 | Permission to use, copy, modify, and/or distribute this software for 7 | any purpose with or without fee is hereby granted, provided that the 8 | above copyright notice and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 | PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 16 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 | PERFORMANCE OF THIS SOFTWARE. */ 18 | 19 | #include "morton.h" 20 | 21 | #ifdef __BMI2__ 22 | #include 23 | #endif 24 | 25 | /** 26 | * Interleaves bits. 27 | * 28 | * morton(0b1100,0b0011) 29 | * │││└──┐││││ 30 | * ││└─┐ │││││ 31 | * │└┐ │ │││││ 32 | * │┌─────┘│││ 33 | * │││ │ │ │││ 34 | * │││┌────┘││ 35 | * │││││ │ ││ 36 | * │││││┌───┘│ 37 | * │││││││┌──┘ 38 | * 0b10100101 39 | */ 40 | unsigned long long morton(unsigned hi, unsigned lo) { 41 | #ifdef __BMI2__ 42 | return _pdep_u64(lo, 0x5555555555555555) | 43 | _pdep_u64(hi, 0xAAAAAAAAAAAAAAAA); 44 | #else 45 | unsigned long long xu = lo; 46 | unsigned long long yu = hi; 47 | xu = (xu | xu << 020) & 0x0000FFFF0000FFFF; 48 | xu = (xu | xu << 010) & 0x00FF00FF00FF00FF; 49 | xu = (xu | xu << 004) & 0x0F0F0F0F0F0F0F0F; 50 | xu = (xu | xu << 002) & 0x3333333333333333; 51 | xu = (xu | xu << 001) & 0x5555555555555555; 52 | yu = (yu | yu << 020) & 0x0000FFFF0000FFFF; 53 | yu = (yu | yu << 010) & 0x00FF00FF00FF00FF; 54 | yu = (yu | yu << 004) & 0x0F0F0F0F0F0F0F0F; 55 | yu = (yu | yu << 002) & 0x3333333333333333; 56 | yu = (yu | yu << 001) & 0x5555555555555555; 57 | return xu | yu << 1; 58 | #endif 59 | } 60 | 61 | static inline unsigned unmortoner(unsigned long long x) { 62 | x &= 0x5555555555555555; 63 | x = (x | x >> 001) & 0x3333333333333333; 64 | x = (x | x >> 002) & 0x0F0F0F0F0F0F0F0F; 65 | x = (x | x >> 004) & 0x00FF00FF00FF00FF; 66 | x = (x | x >> 010) & 0x0000FFFF0000FFFF; 67 | x = (x | x >> 020) & 0x00000000FFFFFFFF; 68 | return x; 69 | } 70 | 71 | /** 72 | * Deinterleaves bits. 73 | */ 74 | struct Morton unmorton(unsigned long long z) { 75 | struct Morton m; 76 | #ifdef __BMI2__ 77 | m.hi = _pext_u64(z, 0x5555555555555555); 78 | m.lo = _pext_u64(z, 0xAAAAAAAAAAAAAAAA); 79 | #else 80 | m.hi = unmortoner(z); 81 | m.lo = unmortoner(z >> 1); 82 | #endif 83 | return m; 84 | } 85 | -------------------------------------------------------------------------------- /bench.c: -------------------------------------------------------------------------------- 1 | /* -*- mode:c;indent-tabs-mode:nil;c-basic-offset:4;coding:utf-8 -*- 2 | vi: set et ft=c ts=4 sts=4 sw=4 fenc=utf-8 :vi 3 | 4 | Copyright 2024 Justine Alexandra Roberts Tunney 5 | 6 | Permission to use, copy, modify, and/or distribute this software for 7 | any purpose with or without fee is hereby granted, provided that the 8 | above copyright notice and this permission notice appear in all copies. 9 | 10 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL 11 | WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED 12 | WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE 13 | AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL 14 | DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR 15 | PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER 16 | TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 17 | PERFORMANCE OF THIS SOFTWARE. */ 18 | 19 | #include "morton.h" 20 | 21 | #include 22 | #include 23 | 24 | #ifndef _WIN32 25 | #include 26 | #else 27 | #include 28 | #endif 29 | 30 | static inline int rand32(void) { 31 | static unsigned long long lcg = 1; 32 | lcg *= 6364136223846793005; 33 | lcg += 1442695040888963407; 34 | return lcg >> 32; 35 | } 36 | 37 | #ifdef _WIN32 38 | static long long GetQueryPerformanceFrequency() { 39 | LARGE_INTEGER t; 40 | QueryPerformanceFrequency(&t); 41 | return t.QuadPart; 42 | } 43 | static long long GetQueryPerformanceCounter() { 44 | LARGE_INTEGER t; 45 | QueryPerformanceCounter(&t); 46 | return t.QuadPart; 47 | } 48 | #endif 49 | 50 | static long long micros(void) { 51 | #ifndef _WIN32 52 | struct timespec ts; 53 | clock_gettime(CLOCK_REALTIME, &ts); 54 | return ts.tv_sec * 1000000 + (ts.tv_nsec + 999) / 1000; 55 | #else 56 | static long long timer_freq = GetQueryPerformanceFrequency(); 57 | static long long timer_start = GetQueryPerformanceCounter(); 58 | return ((GetQueryPerformanceCounter() - timer_start) * 1000000) / timer_freq; 59 | #endif 60 | } 61 | 62 | #define N 1000000 63 | #define M 4 64 | 65 | unsigned A[N][M]; 66 | unsigned B[N][M]; 67 | unsigned long long C[N][M]; 68 | 69 | int main() { 70 | int n = 10000; 71 | long long t1, t2; 72 | 73 | for (int i = 0; i < N; ++i) { 74 | for (int j = 0; j < M; ++j) 75 | A[i][j] = rand32(); 76 | for (int j = 0; j < M; ++j) 77 | B[i][j] = rand32(); 78 | for (int j = 0; j < M; ++j) 79 | C[i][j] = rand32(); 80 | } 81 | 82 | // compute memory overhead 83 | t1 = micros(); 84 | for (int i = 0; i < N; ++i) { 85 | for (int j = 0; j < M; ++j) 86 | C[i][j] = A[i][j] ^ B[i][j]; 87 | } 88 | t2 = micros(); 89 | long long overhead = t2 - t1; 90 | 91 | // benchmark morton() 92 | t1 = micros(); 93 | for (int i = 0; i < N; ++i) { 94 | for (int j = 0; j < M; ++j) 95 | C[i][j] = morton(A[i][j], B[i][j]); 96 | } 97 | t2 = micros(); 98 | printf("%10g ns morton()\n", (t2 - t1 - overhead) * 1e3 / N / M); 99 | 100 | // benchmark unmorton() 101 | t1 = micros(); 102 | for (int i = 0; i < N; ++i) { 103 | for (int j = 0; j < M; ++j) { 104 | struct Morton m = unmorton(C[i][j]); 105 | A[i][j] = m.lo; 106 | B[i][j] = m.hi; 107 | } 108 | } 109 | t2 = micros(); 110 | printf("%10g ns unmorton()\n", (t2 - t1 - overhead) * 1e3 / N / M); 111 | } 112 | --------------------------------------------------------------------------------