├── .gitignore ├── ChangeLog.md ├── LICENSE ├── Makefile ├── README.md ├── TODO.md ├── Version.md ├── art ├── art.c ├── art.h ├── art_node.c └── art_node.h ├── benchmark.png ├── benchmark_multi_art.png ├── blink ├── blink_tree.c ├── blink_tree.h ├── latch.h ├── mapping_array.c ├── mapping_array.h ├── node.c └── node.h ├── example ├── Makefile ├── adaptive_radix_tree.c ├── blink_tree.c ├── mass_tree.c └── palm_tree.c ├── generate_data.c ├── hot ├── hot.c ├── hot.h ├── hot_node.c └── hot_node.h ├── mass ├── mass_node.c ├── mass_node.h ├── mass_tree.c └── mass_tree.h ├── palm ├── allocator.c ├── allocator.h ├── bounded_queue.c ├── bounded_queue.h ├── metric.c ├── metric.h ├── node.c ├── node.h ├── palm_tree.c ├── palm_tree.h ├── worker.c └── worker.h ├── run.sh ├── test ├── art_test.c ├── blink_tree_test.c ├── mass_node_test.c ├── mass_tree_test.c ├── one_test.c ├── palm_batch_test.c ├── palm_node_test.c └── palm_tree_test.c ├── third_party └── c_hashmap │ ├── License.md │ ├── README │ ├── hashmap.c │ ├── hashmap.h │ └── main.c └── util ├── rng.c └── rng.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *_test 4 | .tags 5 | generate_data 6 | data 7 | run.sh 8 | -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | ### 4.1.0 2 | 3 | **Multi-ART** 4 | 5 | 1. (BUG)节点扩展后没有获取新 version 6 | 2. (BUG)Node256 初始化 version 设置错误 7 | 3. (BUG)使用自己的内存分配器释放内存时调用了系统函数 8 | 4. (BUG)Node256 下降时没有判断节点是否为空 9 | 5. (BUG)b link tree 判断右移时没有使用旧节点的 fence key 10 | 6. 将 leaf 的 key 和 len 打包在一起 11 | 7. 带有随机数生成器的用于测试所有索引的 benchmark, one benchfuck to rule them all! 12 | 8. (BUG)替换叶子节点时没有考虑到父节点为空的情况 13 | 9. (BUG)节点加锁后解决前缀冲突时没有再次进行前缀检查 14 | 10. (BUG)加锁后发现前缀变化重试时没有解锁 15 | 11. (BUG)Palm Tree Worker 计算任务时出现 beg > end 16 | 12. (BUG)修复节点无法获取正确父节点的 bug 17 | 13. (BUG)修复节点扩展时对叶子节点设置父节点的 bug 18 | 14. (BUG)修复提前设置节点父节点的 bug 19 | 15. (BUG)修复下降到错误层级节点的 bug,通过记录节点开始比较的 key 偏移 20 | 21 | 22 | 23 | ### 4.0.0 24 | 25 | **Adaptive Radix Tree** 26 | 27 | 1. 单线程 adaptive radix tree 28 | 2. (BUG)初始化根节点不为空 29 | 3. (BUG)节点扩展没有拷贝前缀 30 | 4. (BUG)叶子节点不支持256字节长度的 key,改为支持小于16字节 31 | 5. (BUG)节点前缀截断时没有按照实际长度来 32 | 6. (BUG)节点下降时没有获取正确的二级指针 33 | 34 | 35 | 36 | ### 3.2.0 37 | 38 | **Mass Tree 读优化** 39 | 40 | 1. 解决潜在的 `mass_tree_get` bug,当确认获取的 key 信息安全(节点没有发生插入或分裂)后才进行处理 41 | 2. 解决生成测试数据的 bug 42 | 3. 重构节点的 `permutation` 43 | 4. 以更安全的方式设置节点的父节点,简化代码 44 | 5. (BUG)border node 分裂时没有修改其 subtree 的父节点 45 | 46 | 47 | 48 | ### 3.1.0 49 | 50 | **Mass Tree 移除多余的 lower key** 51 | 52 | 1. 优化 keyslice 获取,移除重试时 keyslice 的获取 53 | 2. 去掉 border node 的 lower key,因为 lower key 始终位于 index 0 处,不需要多余的 lower key 54 | 3. 完善测试数据的生成,添加测试脚本 55 | 56 | 57 | 58 | ### 3.0.0 59 | 60 | **Mass Tree** 61 | 62 | 1. 改进版的 Blink Tree 63 | 2. (BUG)border 节点分裂时没有 fence key 截断,对应的,interior 节点下降时只有 keyslice 相等时,才能进行偏移的更新 64 | 3. (BUG)key 偏移更新问题。第二个 bug 是错的,只有当下降到 subtree 时,才需要进行 key 偏移的更新,同时只有当生成 subtree 时才需要进行截断 65 | 4. (BUG)节点下降时根据错误的映射关系获取了错误的节点 66 | 5. (BUG)获取节点信息的宏出现错误,没有更安全地加上括号 67 | 6. (BUG)分裂后将新节点插入父节点时,同时设置父节点,但如果父节点此时也分裂了,可能造成父节点设置错误 68 | 7. (BUG)生成 subtree 后没有解锁节点 69 | 8. (BUG)替换节点时没有修改指示位 70 | 9. (BUG)获取冲突 key 长度时指针使用错误,造成 segment fault 71 | 10. (BUG)生成 subtree 时没有考虑到 keyslice 冲突的问题,通过生成多层 subtree 并且懒惰生成的方式解决 72 | 11. (BUG)替换 subtree 时没有设置 subtree 的父节点 73 | 12. (BUG)生成 subtree 时没有连接所有节点 74 | 13. (BUG)搜索节点时没有正确处理中间状态(unstable state) 75 | 14. (BUG)(13衍生)获取 key 偏移时错误理解小端字节序 76 | 15. (FIX)解决 border node 的 lower key 问题,但是降低了性能,同时增加了内存使用。A necessary evil 77 | 16. (BUG)border node 的 lower key 选取错误,应该选择分裂时新节点的第一个 key,而不是旧节点的最后一个 key 78 | 17. 无论是顺序插入还是随机插入,性能出乎意料得好 79 | 80 | 81 | 82 | ### 2.3.0 83 | 84 | **将 level 0 节点从1/2满提高到2/3,即 B* Tree** 85 | 1. 更紧凑的节点布局,大幅节省节点内存。 86 | 2. 由于这个优化会带来新的 cache miss,并且需要额外的一些 CPU 计算,所以预计并不会对 palm tree 性能提升有帮助,甚至有可能下降? 87 | 3. 如果把当前节点的 key 挪过去了,那当前 worker 后续的 key 可能需要重新定向。(发现这个问题之前就解决了) 88 | 4. 也可能挪过去了会造成后续 key 插入时节点的 key 挪动,不过也可能可以避免当前节点后续 key 的移动,所以并不是一个问题。 89 | 5. 从之前的测试情况来看,4k节点从设计上来说是有问题的,因为瓶颈在 cache miss 上,而且4k节点在实现 b*node 时也带来了很大的问题,需要很多 CPU 计算。所以需要重新设计节点的结构,核心的改变是以 CACHE LINE(64 字节) 为单位,希望获得更好的 cache locality。 90 | 6. 这个优化和 Prefix B Tree 是冲突的 91 | 7. (BUG)单个节点发生多次 move 导致多个 key 需要被替换,会导致在 level 1 替换 key 时发现有的 key 不存在。所以需要进行 key 过滤 92 | 8. (BUG)两个节点同时往新节点移入 key 时没有修改 `next` 域 93 | 9. (BUG)右移时顺序错误 94 | 10. (BUG)统计可移入字节数时只统计了 key 的长度,没有统计其他域 95 | 11. (BUG)由于 key 的左移,导致无法只通过右移来确定实际插入的 level 0 节点 96 | 12. (BUG)顺序插入优化时没有连接新节点 97 | 13. 移除了顺序插入优化的标记 `sopt` 98 | 14. (BUG)当 replace fence key 到达父节点时,父节点已经发生了分裂,导致要被替换的 key 被提升到了父节点上层 99 | 100 | **总结:** 101 | 102 | * 随机插入性能稍有下降(<5%),顺序插入性能不受影响 103 | * 节点占用内存减少约13%,但是目前没有优化彻底,所以没有达到预期效果,具体情况需要等待后续完成 palm tree 信息统计后才可以得知 104 | 105 | 106 | ### 2.2.0 107 | 108 | **对 level 0 进行前缀压缩(Prefix B Tree)** 109 | 110 | 1. 为了简化编程暂不考虑 key 长度小于等于前缀的情况。 111 | 2. 对于写入时 prefix 不一致的问题,需要新分配节点来插入 key,是否会造成很多零碎节点?会有但并不是问题。 112 | 3. 优化效果十分良好,对于100万个10字节 key 会有8%以上的内存节省,随着 key 数量的增长内存节省会更明显。 113 | 114 | 115 | 116 | ### 2.1.0 117 | 118 | **节点内存分配器** 119 | 120 | 1. 每个线程对应一个分配器,尽可能减少内存分配毛刺的出现,更加稳定。 121 | 2. 因为测试线程少,所以目前分配器并不会带来什么实际效果。 122 | 3. 是否可以考虑和 prefetch 相结合? 123 | 4. 是否可以引入新的以线程为单位的节点布局(CSB+ Tree)? 124 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2018, UncP 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | * Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | * Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | * Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CC=gcc 2 | CFLAGS=-std=c99 -D_GNU_SOURCE -Wall -Werror -Wextra -O3 -fno-strict-aliasing 3 | IFLAGS=-I./third_party 4 | LFLAGS=./third_party/c_hashmap/libhashmap.a -lpthread -lm 5 | PFLAGS=-DLazy #-DPrefix -DBStar 6 | DFLAGS= 7 | BFLAGS= 8 | MFLAGS=-DTest 9 | AFLAGS=-DTest 10 | HFLAGS=-DTest 11 | 12 | PALMFLAGS=$(CC) $(CFLAGS) $(PFLAGS) $(IFLAGS) $(DFLAGS) 13 | BLINKFLAGS=$(CC) $(CFLAGS) $(BFLAGS) $(DFLAGS) 14 | MASSFLAGS=$(CC) $(CFLAGS) $(MFLAGS) $(DFLAGS) 15 | ARTFLAGS=$(CC) $(CFLAGS) $(AFLAGS) $(DFLAGS) 16 | HOTFLAGS=$(CC) $(CFLAGS) $(HFLAGS) $(DFLAGS) 17 | ONEFLAGS=$(CC) $(CFLAGS) $(DFLAGS) $(LFLAGS) 18 | 19 | PALM_OBJ=palm/node.o palm/bounded_queue.o palm/worker.o palm/palm_tree.o palm/metric.o palm/allocator.o 20 | BLINK_OBJ=palm/node.o palm/allocator.o blink/node.o blink/blink_tree.o blink/mapping_array.o 21 | MASS_OBJ=mass/mass_node.o mass/mass_tree.o 22 | ART_OBJ=art/art_node.o art/art.o 23 | HOT_OBJ=hot/hot_node.o hot/hot.o 24 | 25 | default: lib 26 | 27 | lib:$(PALM_OBJ) $(BLINK_OBJ) $(MASS_OBJ) $(ART_OBJ) 28 | make third_party 29 | ar rcs libaili.a $(PALM_OBJ) $(BLINK_OBJ) $(MASS_OBJ) $(ART_OBJ) third_party/c_hashmap/hashmap.o 30 | 31 | test: node_test palm_batch_test palm_node_test palm_tree_test 32 | 33 | palm/%.o: palm/%.c 34 | $(PALMFLAGS) -c $^ -o $@ 35 | 36 | palm_node_test: test/palm_node_test.c palm/node.o 37 | $(PALMFLAGS) -o $@ $^ 38 | 39 | palm_batch_test: test/palm_batch_test.c palm/node.o 40 | $(PALMFLAGS) -o $@ $^ 41 | 42 | palm_tree_test: test/palm_tree_test.c palm/node.o palm/worker.o palm/bounded_queue.o palm/palm_tree.o \ 43 | palm/metric.o palm/allocator.o 44 | $(PALMFLAGS) -o $@ $^ $(LFLAGS) 45 | 46 | generate_data: generate_data.c 47 | $(CC) $(CFLAGS) -o $@ $^ -lpthread 48 | 49 | blink/%.o: blink/%.c 50 | $(BLINKFLAGS) -c $^ -o $@ 51 | 52 | blink_tree_test: test/blink_tree_test.c blink/node.o blink/blink_tree.o blink/mapping_array.o palm/node.o \ 53 | palm/allocator.o 54 | $(BLINKFLAGS) -o $@ $^ -lpthread 55 | 56 | mass/%.o: mass/%.c 57 | $(MASSFLAGS) -c $^ -o $@ 58 | 59 | mass_node_test: test/mass_node_test.c mass/mass_node.o 60 | $(MASSFLAGS) -o $@ $^ -lpthread 61 | 62 | mass_tree_test: test/mass_tree_test.c mass/mass_node.o mass/mass_tree.o palm/allocator.o 63 | $(MASSFLAGS) -o $@ $^ -lpthread 64 | 65 | art/%.o: art/%.c 66 | $(ARTFLAGS) -c $^ -o $@ 67 | 68 | art_test: test/art_test.c art/art_node.o art/art.o palm/allocator.o 69 | $(ARTFLAGS) -o $@ $^ -lpthread 70 | 71 | hot/%.o: hot/%.c 72 | $(HOTFLAGS) -c $^ -o $@ 73 | 74 | util/%.o: util/%.c 75 | $(ONEFLAGS) -c $^ -o $@ 76 | 77 | one_test: test/one_test.c util/rng.o $(PALM_OBJ) $(BLINK_OBJ) $(MASS_OBJ) $(ART_OBJ) $(HOT_OBJ) 78 | $(ONEFLAGS) -o $@ $^ $(LFLAGS) 79 | 80 | third_party: third_party/c_hashmap/hashmap.o 81 | cd third_party/c_hashmap && $(CC) $(CFLAGS) -c hashmap.c -o hashmap.o && ar rcs libhashmap.a hashmap.o 82 | 83 | clean: 84 | rm palm/*.o blink/*.o mass/*.o art/*.o util/*.o *_test generate_data libaili.a; cd example && make clean 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aili 2 | [![Author](https://img.shields.io/badge/Author-UncP-brightgreen.svg)](https://github.com/UncP) 3 | [![Version](https://img.shields.io/badge/Version-4.1.0-blue.svg)](https://github.com/UncP/aili) 4 | 5 | ### the fastest in-memory index in the East(maybe the fastest on this planet) 6 | 7 | 8 | A library that provides various concurrent algorithms for in-memory index, aims to achieve extremely FAST speed, but just for EXPERIMENT and FUN. 9 | 10 | #### Algorithms 11 | 12 | * Palm Tree (`palm/`) 13 | * Blink Tree (`blink/`) 14 | * Mass Tree (`mass/`) 15 | * Adaptive Radix Tree (`art/`) 16 | * Height Optimized Trie (`hot/`) (developing) 17 | 18 | 19 | #### Have a Try 20 | 21 | ```bash 22 | # thread_num thread_key_number 23 | ./run.sh palm 4 100 # test palm tree 24 | 25 | ./run.sh blink 4 100 # test blink tree 26 | 27 | ./run.sh mass 4 100 # test mass tree 28 | 29 | ./run.sh art 4 100 # test art tree 30 | ``` 31 | 32 | 33 | #### Benchmark 34 | 35 | ![](./benchmark.png) 36 | 37 | #### Benchmark Multi ART 38 | Multi ART is capable of reaching **100 million** insert per second on a 96-core machine using 64 threads. 39 | 40 | ![](./benchmark_multi_art.png) 41 | 42 | #### Other 43 | 44 | + Checkout `example/` for examples 45 | + Follow my [知乎专栏](https://zhuanlan.zhihu.com/b-tree) for blogs about this repository 46 | + Open an issue if you have any problem 47 | 48 | 49 | 50 | #### References 51 | 52 | - Palm Tree : [Parallel Architecture-Friendly Latch-Free Modifications to B+ Trees on Many-Core Processors](http://www.vldb.org/pvldb/vol4/p795-sewall.pdf) 53 | - Mass Tree : [Cache Craftiness for Fast Multicore Key-Value Storage](https://pdos.csail.mit.edu/papers/masstree:eurosys12.pdf) 54 | - Blink Tree : [Efficient Locking for Concurrent Operations on B-Trees](https://www.csd.uoc.gr/~hy460/pdf/p650-lehman.pdf) 55 | - Prefetch B+ Tree : [Improving Index Performance through Prefetching](http://www.aladdin.cs.cmu.edu/papers/pdfs/y2001/improv_ind.pdf) 56 | - Prefix B Tree : [Prefix B-Trees](http://delivery.acm.org/10.1145/330000/320530/p11-bayer.pdf?ip=111.114.49.2&id=320530&acc=ACTIVE%20SERVICE&key=BF85BBA5741FDC6E%2E4510866D46BF76B7%2E4D4702B0C3E38B35%2E4D4702B0C3E38B35&__acm__=1537792786_42d3c27bf4ea064b8d68b89657e39bf6) 57 | - B* Tree : The Art of Computer Programming, Volume 3, Chapter 6 58 | - Adaptive Radix Tree : [The Adaptive Radix Tree: ARTful Indexing for Main-Memory Databases](https://db.in.tum.de/~leis/papers/ART.pdf) 59 | 60 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | ### TODO 2 | - [x] batch_size 与 node_size 不同 3 | - [x] 根据 batch_size 和 worker 数量来进行 worker 初始化 4 | - [x] 前缀压缩 5 | - [x] palm tree 更细粒度的 metric 6 | - [x] simple prefix B+ Tree 7 | - [x] palm tree 更详细的 metric 信息输出 8 | - [x] palm tree 顺序插入优化 9 | - [x] worker 同步时的 memory order 优化 10 | - [x] 减小同步粒度 11 | - [x] 移除 barrier 12 | - [x] palm tree 层级下降策略 13 | - [x] level 0 顺序插入优化 14 | - [x] palm tree 懒惰下降策略 15 | - [x] Mass Tree 16 | - [x] palm tree 折线下降策略 17 | - [ ] 重构 B+ 树节点 18 | - [x] 手动管理 B+ 树节点内存 19 | - [x] blink Tree 20 | - [x] 重构 bounded mapping queue -> mapping array 21 | - [x] b* node 22 | - [ ] MRBTree 23 | - [x] palm tree 信息统计 24 | - [ ] mass tree linearizability 测试 25 | - [x] mass tree 优化 `node_lock` 以及 `node_get_stable_version` 26 | - [x] mass tree node prefetch 27 | - [ ] mass tree delete 28 | - [x] Adaptive Radix Tree 29 | - [x] Multi-Thread Adaptive Radix Tree 30 | - [ ] Height Optimized Tree (HOT) 31 | -------------------------------------------------------------------------------- /Version.md: -------------------------------------------------------------------------------- 1 | #### Version Information 2 | | Version | Detail | 3 | |:------:|:---------------------------:| 4 | | [1.0.0](https://github.com/UncP/aili/tree/1.0.0) | Palm Tree | 5 | | [1.1.0](https://github.com/UncP/aili/tree/1.1.0) | Palm Tree with point-to-point synchronization | 6 | | [1.2.0](https://github.com/UncP/aili/tree/1.2.0) | Simple Prefix B Tree | 7 | | [1.2.1](https://github.com/UncP/aili/tree/1.2.1) | synchronization optimization & remove barrier | 8 | | [1.2.2](https://github.com/UncP/aili/tree/1.2.2) | adjust Palm Tree descending policy (level descend, not ideal) & sequential insertion optimization | 9 | | [1.2.3](https://github.com/UncP/aili/tree/1.2.3) | adjust Palm Tree descending policy (lazy descend, great for sequential operation) | 10 | | [1.3.0](https://github.com/UncP/aili/tree/1.3.0) | a major refactor | 11 | | [1.3.1](https://github.com/UncP/aili/tree/1.3.1) | Prefetch B Tree | 12 | | [1.3.2](https://github.com/UncP/aili/tree/1.3.2) | adjust Palm Tree descending policy (zigzag descending policy) | 13 | | [2.0.0](https://github.com/UncP/aili/tree/2.0.0) | Blink Tree | 14 | | [2.1.0](https://github.com/UncP/aili/tree/2.1.0) | node allocator | 15 | | [2.2.0](https://github.com/UncP/aili/tree/2.2.0) | Prefix B Tree | 16 | | [2.3.0](https://github.com/UncP/aili/tree/2.3.0) | B* Tree | 17 | | [2.3.1](https://github.com/UncP/aili/tree/2.3.1) | Palm Tree node info collect | 18 | | [3.0.0](https://github.com/UncP/aili/tree/3.0.0) | Mass Tree | 19 | | [3.1.0](https://github.com/UncP/aili/tree/3.1.0) | Mass Tree remove redundant lower key | 20 | | [3.2.0](https://github.com/UncP/aili/tree/3.2.0) | make `mass_tree_get` more robust | 21 | | [4.0.0](https://github.com/UncP/aili/tree/4.0.0) | Adaptive Radix Tree | 22 | | [4.1.0](https://github.com/UncP/aili/tree/4.1.0) | Multi-ART | 23 | 24 | -------------------------------------------------------------------------------- /art/art.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-02-16 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | #include 10 | #ifdef Debug 11 | #include 12 | #include 13 | #endif 14 | 15 | #include "../palm/allocator.h" 16 | #include "art.h" 17 | #include "art_node.h" 18 | 19 | struct adaptive_radix_tree 20 | { 21 | art_node *root; 22 | }; 23 | 24 | adaptive_radix_tree* new_adaptive_radix_tree() 25 | { 26 | #ifdef Allocator 27 | init_allocator(); 28 | #endif 29 | 30 | adaptive_radix_tree *art = malloc(sizeof(adaptive_radix_tree)); 31 | art->root = 0; 32 | 33 | return art; 34 | } 35 | 36 | void free_adaptive_radix_tree(adaptive_radix_tree *art) 37 | { 38 | (void)art; 39 | } 40 | 41 | // return 0 on success, 42 | // return +1 on existed, 43 | // return -1 on retry 44 | static int adaptive_radix_tree_replace_leaf(art_node *parent, art_node **ptr, art_node *an, 45 | const void *key, size_t len, size_t off) 46 | { 47 | art_node *new = art_node_replace_leaf_child(an, key, len, off); 48 | if (likely(new)) { 49 | if (likely(parent)) { 50 | if (unlikely(art_node_lock(parent))) { 51 | // parent is old 52 | free_art_node(new); 53 | return -1; 54 | } else { 55 | art_node *now; 56 | __atomic_load(ptr, &now, __ATOMIC_ACQUIRE); 57 | if (unlikely(now != an)) { 58 | // leaf has been replaced by another thread 59 | art_node_unlock(parent); 60 | free_art_node(new); 61 | return -1; 62 | } 63 | __atomic_store(ptr, &new, __ATOMIC_RELEASE); 64 | art_node_unlock(parent); 65 | return 0; 66 | } 67 | } else { // art with only one leaf 68 | if (likely(__atomic_compare_exchange_n(ptr, &an, new, 0 /* weak */, __ATOMIC_RELEASE, __ATOMIC_ACQUIRE))) { 69 | return 0; 70 | } else { 71 | free_art_node(new); 72 | return -1; 73 | } 74 | } 75 | } else { 76 | return 1; 77 | } 78 | } 79 | 80 | // return 0 on success, 81 | // return +1 on existed, 82 | // return -1 for retry 83 | static int _adaptive_radix_tree_put(art_node *parent, art_node **ptr, const void *key, size_t len, size_t off) 84 | { 85 | art_node *an; 86 | int first = 1; 87 | 88 | begin: 89 | // this is fucking ugly! 90 | if (first) { 91 | first = 0; 92 | } else if (parent) { 93 | // this is not the first time we enter `begin`, so 94 | // we need to make sure that `ptr` is still valid because `parent` might changed 95 | uint64_t pv = art_node_get_version(parent); 96 | if (art_node_version_is_old(pv)) 97 | return -1; // return -1 so that we can retry from root 98 | // `ptr` is still valid, we can proceed 99 | } 100 | 101 | // NOTE: __ATOMIC_RELAXED is not ok 102 | __atomic_load(ptr, &an, __ATOMIC_ACQUIRE); 103 | 104 | if (unlikely(is_leaf(an))) 105 | return adaptive_radix_tree_replace_leaf(parent, ptr, an, key, len, off); 106 | 107 | // verify node prefix 108 | uint64_t v = art_node_get_stable_expand_version(an); 109 | if (unlikely(art_node_version_get_offset(v) != off)) 110 | goto begin; 111 | 112 | if (unlikely(art_node_version_is_old(v))) 113 | goto begin; 114 | 115 | int p = art_node_prefix_compare(an, v, key, len, off); 116 | 117 | uint64_t v1 = art_node_get_version(an); 118 | if (unlikely(art_node_version_is_old(v1) || art_node_version_compare_expand(v, v1))) 119 | goto begin; 120 | v = v1; 121 | 122 | if (p != art_node_version_get_prefix_len(v)) { 123 | if (unlikely(art_node_lock(an))) 124 | goto begin; 125 | // still need to check whether prefix has been changed! 126 | if (unlikely(art_node_version_compare_expand(v, art_node_get_version_unsafe(an)))) { 127 | art_node_unlock(an); 128 | goto begin; 129 | } 130 | debug_assert(art_node_version_is_old(art_node_get_version_unsafe(an)) == 0); 131 | art_node *new = art_node_expand_and_insert(an, key, len, off, p); 132 | parent = art_node_get_locked_parent(an); 133 | art_node_set_parent_unsafe(an, new); 134 | if (likely(parent)) { 135 | debug_assert(off); 136 | art_node_replace_child(parent, ((unsigned char *)key)[off - 1], an, new); 137 | art_node_unlock(parent); 138 | } else { // this is root 139 | __atomic_store(ptr, &new, __ATOMIC_RELEASE); 140 | } 141 | art_node_unlock(an); 142 | return 0; 143 | } 144 | 145 | off += p; 146 | debug_assert(off < len); 147 | 148 | // prefix is matched, we can descend 149 | art_node **next = art_node_find_child(an, v, ((unsigned char *)key)[off]); 150 | 151 | v = art_node_get_version(an); 152 | 153 | if (unlikely(art_node_version_is_old(v))) { 154 | off -= p; 155 | goto begin; 156 | } 157 | 158 | if (next) 159 | return _adaptive_radix_tree_put(an, next, key, len, off + 1); 160 | 161 | if (unlikely(art_node_lock(an))) { 162 | off -= p; 163 | goto begin; 164 | } 165 | 166 | art_node *new = 0; 167 | next = art_node_add_child(an, ((unsigned char *)key)[off], (art_node *)make_leaf(key), &new); 168 | if (unlikely(new)) { 169 | parent = art_node_get_locked_parent(an); 170 | if (likely(parent)) { 171 | debug_assert((int)off > p); 172 | art_node_replace_child(parent, ((unsigned char *)key)[off - p - 1], an, new); 173 | art_node_unlock(parent); 174 | } else { 175 | __atomic_store(ptr, &new, __ATOMIC_RELEASE); 176 | } 177 | } 178 | art_node_unlock(an); 179 | 180 | // another thread might inserted same byte before we acquire lock 181 | if (unlikely(next)) 182 | return _adaptive_radix_tree_put(an, next, key, len, off + 1); 183 | 184 | return 0; 185 | } 186 | 187 | // return 0 on success 188 | // return 1 on duplication 189 | int adaptive_radix_tree_put(adaptive_radix_tree *art, const void *key, size_t len) 190 | { 191 | //print_key(key, len); 192 | 193 | art_node *root = art->root; 194 | if (unlikely(root == 0)) { // empty art 195 | art_node *leaf = (art_node *)make_leaf(key); 196 | if (__atomic_compare_exchange_n(&art->root, &root, leaf, 0 /* weak */, __ATOMIC_RELEASE, __ATOMIC_ACQUIRE)) 197 | return 0; 198 | // else another thread has replaced empty root 199 | } 200 | int ret; 201 | // retry should be rare 202 | while (unlikely((ret = _adaptive_radix_tree_put(0 /* parent */, &art->root, key, len, 0 /* off */)) == -1)) 203 | ; 204 | return ret; 205 | } 206 | 207 | static void* _adaptive_radix_tree_get(art_node *parent, art_node **ptr, const void *key, size_t len, size_t off) 208 | { 209 | art_node *an; 210 | 211 | debug_assert(off <= len); 212 | 213 | int first = 1; // is this the first time we enter `begin` 214 | begin: 215 | // this is fucking ugly! 216 | if (first) { 217 | first = 0; 218 | } else if (parent) { 219 | // this is not the first time we enter `begin`, so 220 | // we need to make sure that `ptr` is still valid because `parent` might changed 221 | uint64_t pv = art_node_get_version(parent); 222 | if (art_node_version_is_old(pv)) 223 | return (void *)1; // return 1 so that we can retry from root 224 | // `ptr` is still valid, we can proceed 225 | } 226 | __atomic_load(ptr, &an, __ATOMIC_ACQUIRE); 227 | 228 | if (unlikely(is_leaf(an))) { 229 | const char *k1 = get_leaf_key(an), *k2 = (const char *)key; 230 | size_t l1 = get_leaf_len(an), l2 = len, i; 231 | for (i = off; i < l1 && i < l2 && k1[i] == k2[i]; ++i) 232 | ; 233 | if (i == l1 && i == l2) 234 | return (void *)k1; // key exists 235 | // art_node_print(parent); 236 | // print_key(k1, l1); 237 | // printf("off:%lu\n", off); 238 | return 0; 239 | } 240 | 241 | uint64_t v = art_node_get_stable_expand_version(an); 242 | if (unlikely(art_node_version_get_offset(v) != off)) 243 | goto begin; 244 | if (unlikely(art_node_version_is_old(v))) 245 | goto begin; 246 | 247 | int p = art_node_prefix_compare(an, v, key, len, off); 248 | 249 | uint64_t v1 = art_node_get_version(an); 250 | if (unlikely(art_node_version_is_old(v1) || art_node_version_compare_expand(v, v1))) 251 | goto begin; 252 | v = v1; 253 | 254 | if (p != art_node_version_get_prefix_len(v)) 255 | return 0; 256 | 257 | off += art_node_version_get_prefix_len(v); 258 | debug_assert(off <= len); 259 | 260 | int advance = off != len; 261 | unsigned char byte = advance ? ((unsigned char *)key)[off] : 0; 262 | 263 | art_node **next = art_node_find_child(an, v, byte); 264 | 265 | v1 = art_node_get_version(an); 266 | 267 | if (unlikely(art_node_version_is_old(v1))) 268 | goto begin; 269 | 270 | if (next) 271 | return _adaptive_radix_tree_get(an, next, key, len, off + advance); 272 | 273 | // art_node_print(an); 274 | // printf("off:%lu\n", off); 275 | return 0; 276 | } 277 | 278 | void* adaptive_radix_tree_get(adaptive_radix_tree *art, const void *key, size_t len) 279 | { 280 | void *ret; 281 | if (unlikely(art->root == 0)) 282 | return 0; 283 | while (unlikely((uint64_t)(ret = _adaptive_radix_tree_get(0, &art->root, key, len, 0)) == 1)) 284 | ; 285 | return ret; 286 | } 287 | 288 | -------------------------------------------------------------------------------- /art/art.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-02-16 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _adapitve_radix_tree_h_ 8 | #define _adaptive_radix_tree_h_ 9 | 10 | #include 11 | 12 | typedef struct adaptive_radix_tree adaptive_radix_tree; 13 | 14 | adaptive_radix_tree* new_adaptive_radix_tree(); 15 | void free_adaptive_radix_tree(adaptive_radix_tree *art); 16 | int adaptive_radix_tree_put(adaptive_radix_tree *art, const void *key, size_t len); 17 | void* adaptive_radix_tree_get(adaptive_radix_tree *art, const void *key, size_t len); 18 | 19 | #endif /* _adaptive_radix_tree_h_ */ 20 | -------------------------------------------------------------------------------- /art/art_node.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-02-06 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #ifdef Debug 14 | #include 15 | #endif 16 | 17 | #ifdef Allocator 18 | #include "../palm/allocator.h" 19 | #endif 20 | 21 | #include "art_node.h" 22 | 23 | #define node4 ((uint64_t)0x000000) 24 | #define node16 ((uint64_t)0x100000) 25 | #define node48 ((uint64_t)0x200000) 26 | #define node256 ((uint64_t)0x300000) 27 | 28 | /** 29 | * node version layout(64 bits) 30 | * off count prefix_len type old lock insert expand vinsert vexpand 31 | * | 8 | 8 | 8 | 8 | 10 | 2 | 1 | 1 | 1 | 1 | 8 | 8 | 32 | * 33 | **/ 34 | 35 | #define OLD_BIT ((uint64_t)1 << 19) 36 | #define LOCK_BIT ((uint64_t)1 << 18) 37 | #define INSERT_BIT ((uint64_t)1 << 17) 38 | #define EXPAND_BIT ((uint64_t)1 << 16) 39 | 40 | #define set_offset(version, offset) (((version) & (~(((uint64_t)0xff) << 54))) | ((uint64_t)(offset) << 54)) 41 | #define get_offset(version) (size_t)(((version) >> 54) & 0xff) 42 | #define get_prefix_len(version) (int)(((version) >> 32) & 0xff) 43 | #define set_prefix_len(version, len) (((version) & (~(((uint64_t)0xff) << 32))) | (((uint64_t)(len)) << 32)) 44 | #define get_count(version) (int)(((version) >> 40) & 0xff) 45 | #define set_count(version, count) (((version) & (~(((uint64_t)0xff) << 40))) | (((uint64_t)(count)) << 40)) 46 | #define incr_count(version) ((version) + ((uint64_t)1 << 40)) 47 | #define get_type(version) (int)((version) & node256) 48 | #define set_type(version, type) ((version) | type) 49 | 50 | #define is_old(version) ((version) & OLD_BIT) 51 | #define is_locked(version) ((version) & LOCK_BIT) 52 | #define is_inserting(version) ((version) & INSERT_BIT) 53 | #define is_expanding(version) ((version) & EXPAND_BIT) 54 | 55 | #define set_old(version) ((version) | OLD_BIT) 56 | #define set_lock(version) ((version) | LOCK_BIT) 57 | #define set_insert(version) ((version) | INSERT_BIT) 58 | #define set_expand(version) ((version) | EXPAND_BIT) 59 | 60 | #define unset_lock(version) ((version) & (~LOCK_BIT)) 61 | #define unset_insert(version) ((version) & (~INSERT_BIT)) 62 | #define unset_expand(version) ((version) & (~EXPAND_BIT)) 63 | 64 | #define get_vinsert(version) ((int)(((version) >> 8) & 0xff)) 65 | #define incr_vinsert(version) (((version) & (~((uint64_t)0xff << 8))) | (((version) + (1 << 8)) & (0xff << 8))) // overflow is handled 66 | 67 | #define get_vexpand(version) ((int)((version) & 0xff)) 68 | #define incr_vexpand(version) (((version) & ~((uint64_t)0xff)) | (((version) + 1) & 0xff)) // overflow is handled 69 | 70 | #define art_node_header \ 71 | uint64_t version; \ 72 | char prefix[8]; \ 73 | art_node *new; \ 74 | art_node *parent; 75 | 76 | struct art_node 77 | { 78 | art_node_header; 79 | }; 80 | 81 | typedef struct art_node4 82 | { 83 | art_node_header; 84 | unsigned char key[4]; 85 | unsigned char unused[4]; 86 | art_node *child[4]; 87 | char meta[0]; 88 | }art_node4; 89 | 90 | typedef struct art_node16 91 | { 92 | art_node_header; 93 | unsigned char key[16]; 94 | art_node *child[16]; 95 | char meta[0]; 96 | }art_node16; 97 | 98 | typedef struct art_node48 99 | { 100 | art_node_header; 101 | unsigned char index[256]; 102 | art_node *child[48]; 103 | char meta[0]; 104 | }art_node48; 105 | 106 | typedef struct art_node256 107 | { 108 | uint64_t version; 109 | char prefix[8]; 110 | art_node *new; 111 | art_node *parent; 112 | art_node *child[256]; 113 | char meta[0]; 114 | }art_node256; 115 | 116 | inline uint64_t art_node_get_version(art_node *an) 117 | { 118 | uint64_t version; 119 | __atomic_load(&an->version, &version, __ATOMIC_ACQUIRE); 120 | return version; 121 | } 122 | 123 | inline void art_node_set_version(art_node *an, uint64_t version) 124 | { 125 | __atomic_store(&an->version, &version, __ATOMIC_RELEASE); 126 | } 127 | 128 | inline uint64_t art_node_get_version_unsafe(art_node *an) 129 | { 130 | return an->version; 131 | } 132 | 133 | inline void art_node_set_version_unsafe(art_node *an, uint64_t version) 134 | { 135 | an->version = version; 136 | } 137 | 138 | static inline void art_node_set_offset(art_node *an, size_t off) 139 | { 140 | debug_assert(off < 256); 141 | an->version = set_offset(an->version, off); 142 | } 143 | 144 | inline size_t art_node_version_get_offset(uint64_t version) 145 | { 146 | return get_offset(version); 147 | } 148 | 149 | static inline art_node* _new_art_node(size_t size) 150 | { 151 | #ifdef Allocator 152 | art_node *an = (art_node *)allocator_alloc(size); 153 | #else 154 | art_node *an = (art_node *)malloc(size); 155 | #endif 156 | an->version = 0; 157 | an->new = 0; 158 | an->parent = 0; 159 | return an; 160 | } 161 | 162 | static inline art_node* new_art_node4() 163 | { 164 | art_node *an = _new_art_node(sizeof(art_node4)); 165 | an->version = set_type(an->version, node4); 166 | return an; 167 | } 168 | 169 | static inline art_node* new_art_node16() 170 | { 171 | art_node *an = _new_art_node(sizeof(art_node16)); 172 | an->version = set_type(an->version, node16); 173 | return an; 174 | } 175 | 176 | static inline art_node* new_art_node48() 177 | { 178 | art_node *an = _new_art_node(sizeof(art_node48)); 179 | an->version = set_type(an->version, node48); 180 | memset(((art_node48 *)an)->index, 0, 256); 181 | return an; 182 | } 183 | 184 | static inline art_node* new_art_node256() 185 | { 186 | art_node *an = _new_art_node(sizeof(art_node256)); 187 | memset(an, 0, sizeof(art_node256)); 188 | an->version = set_type(an->version, node256); 189 | return an; 190 | } 191 | 192 | art_node* new_art_node() 193 | { 194 | return new_art_node4(); 195 | } 196 | 197 | void free_art_node(art_node *an) 198 | { 199 | #ifdef Allocator 200 | (void)an; 201 | #else 202 | free((void *)an); 203 | #endif 204 | } 205 | 206 | art_node** art_node_find_child(art_node *an, uint64_t version, unsigned char byte) 207 | { 208 | debug_assert(is_leaf(an) == 0); 209 | 210 | switch (get_type(version)) { 211 | case node4: { 212 | art_node4 *an4 = (art_node4*)an; 213 | debug_assert(get_count(version) < 5); 214 | for (int i = 0, count = get_count(version); i < count; ++i) 215 | if (an4->key[i] == byte) { 216 | debug_assert(an4->child[i]); 217 | return &(an4->child[i]); 218 | } 219 | } 220 | break; 221 | case node16: { 222 | art_node16 *an16 = (art_node16 *)an; 223 | debug_assert(get_count(version) < 17); 224 | // for (int i = 0, count = get_count(version); i < count; ++i) 225 | // if (an16->key[i] == byte) { 226 | // debug_assert(an16->child[i]); 227 | // return &(an16->child[i]); 228 | // } 229 | __m128i key = _mm_set1_epi8(byte); 230 | __m128i key2 = _mm_loadu_si128((__m128i *)an16->key); 231 | __m128i cmp = _mm_cmpeq_epi8(key, key2); 232 | int mask = (1 << get_count(version)) - 1; 233 | int bitfield = _mm_movemask_epi8(cmp) & mask; 234 | if (bitfield) { 235 | debug_assert(an16->child[__builtin_ctz(bitfield)]); 236 | return &(an16->child[__builtin_ctz(bitfield)]); 237 | } 238 | } 239 | break; 240 | case node48: { 241 | art_node48 *an48 = (art_node48 *)an; 242 | debug_assert(get_count(version) < 49); 243 | int index = an48->index[byte]; 244 | if (index) { 245 | debug_assert(an48->child[index - 1]); 246 | return &(an48->child[index - 1]); 247 | } 248 | } 249 | break; 250 | case node256: { 251 | art_node256 *an256 = (art_node256 *)an; 252 | if (an256->child[byte]) 253 | return &(an256->child[byte]); 254 | } 255 | break; 256 | default: 257 | assert(0); 258 | } 259 | return 0; 260 | } 261 | 262 | static inline void art_node_set_new_node(art_node *old, art_node *new) 263 | { 264 | __atomic_store(&old->new, &new, __ATOMIC_RELAXED); 265 | } 266 | 267 | static inline art_node* art_node_get_new_node(art_node *old) 268 | { 269 | art_node *new; 270 | __atomic_load(&old->new, &new, __ATOMIC_RELAXED); 271 | return new; 272 | } 273 | 274 | // require: node is locked 275 | static art_node* art_node_grow(art_node *an) 276 | { 277 | art_node *new; 278 | uint64_t version = an->version; 279 | 280 | debug_assert(is_locked(version)); 281 | 282 | switch (get_type(version)) { 283 | case node4: { 284 | art_node16 *an16 = (art_node16 *)(new = new_art_node16()); 285 | art_node4 *an4 = (art_node4 *)an; 286 | debug_assert(get_count(version) == 4); 287 | memcpy(an16->prefix, an4->prefix, 8); 288 | an16->version = set_prefix_len(an16->version, get_prefix_len(version)); 289 | for (int i = 0; i < 4; ++i) { 290 | an16->key[i] = an4->key[i]; 291 | an16->child[i] = an4->child[i]; 292 | if (!is_leaf(an4->child[i])) 293 | an4->child[i]->parent = new; 294 | } 295 | an16->version = set_count(an16->version, 4); 296 | } 297 | break; 298 | case node16: { 299 | art_node48 *an48 = (art_node48 *)(new = new_art_node48()); 300 | art_node16 *an16 = (art_node16 *)an; 301 | debug_assert(get_count(version) == 16); 302 | memcpy(an48->prefix, an16->prefix, 8); 303 | an48->version = set_prefix_len(an48->version, get_prefix_len(version)); 304 | for (int i = 0; i < 16; ++i) { 305 | an48->child[i] = an16->child[i]; 306 | if (!is_leaf(an16->child[i])) 307 | an16->child[i]->parent = new; 308 | an48->index[an16->key[i]] = i + 1; 309 | } 310 | an48->version = set_count(an48->version, 16); 311 | } 312 | break; 313 | case node48: { 314 | art_node256 *an256 = (art_node256 *)(new = new_art_node256()); 315 | art_node48 *an48 = (art_node48 *)an; 316 | debug_assert(get_count(version) == 48); 317 | memcpy(an256->prefix, an48->prefix, 8); 318 | an256->version = set_prefix_len(an256->version, get_prefix_len(version)); 319 | for (int i = 0; i < 256; ++i) { 320 | int index = an48->index[i]; 321 | if (index) { 322 | an256->child[i] = an48->child[index - 1]; 323 | if (!is_leaf(an48->child[index - 1])) 324 | an48->child[index - 1]->parent = new; 325 | } 326 | } 327 | } 328 | break; 329 | default: 330 | // node256 is not growable 331 | assert(0); 332 | } 333 | 334 | assert(art_node_lock(new) == 0); 335 | art_node_set_offset(new, get_offset(version)); 336 | art_node_set_new_node(an, new); 337 | art_node_set_version(an, set_old(version)); 338 | return new; 339 | } 340 | 341 | // add a child to art_node, return 0 on success, otherwise return next layer 342 | // require: node is locked 343 | art_node** art_node_add_child(art_node *an, unsigned char byte, art_node *child, art_node **new) 344 | { 345 | debug_assert(is_leaf(an) == 0); 346 | 347 | uint64_t version = an->version; 348 | debug_assert(is_locked(version)); 349 | 350 | art_node **next; 351 | if ((next = art_node_find_child(an, version, byte))) 352 | return next; 353 | 354 | // grow if necessary 355 | if (unlikely(art_node_is_full(an))) { 356 | *new = art_node_grow(an); 357 | an = *new; 358 | version = an->version; 359 | } 360 | 361 | switch (get_type(version)) { 362 | case node4: { 363 | art_node4 *an4 = (art_node4 *)an; 364 | debug_assert(get_count(version) < 4); 365 | for (int i = 0, count = get_count(version); i < count; ++i) 366 | debug_assert(an4->key[i] != byte); 367 | // no need to be ordered 368 | int count = get_count(version); 369 | an4->child[count] = child; 370 | an4->key[count] = byte; 371 | an4->version = incr_count(version); 372 | } 373 | break; 374 | case node16: { 375 | art_node16 *an16 = (art_node16 *)an; 376 | #ifdef Debug 377 | __m128i key = _mm_set1_epi8(byte); 378 | __m128i key2 = _mm_loadu_si128((__m128i *)an16->key); 379 | __m128i cmp = _mm_cmpeq_epi8(key, key2); 380 | int mask = (1 << get_count(version)) - 1; 381 | int bitfield = _mm_movemask_epi8(cmp) & mask; 382 | debug_assert(bitfield == 0); 383 | #endif 384 | // no need to be ordered 385 | int count = get_count(version); 386 | an16->child[count] = child; 387 | an16->key[count] = byte; 388 | an16->version = incr_count(version); 389 | } 390 | break; 391 | case node48: { 392 | art_node48 *an48 = (art_node48 *)an; 393 | debug_assert(an48->index[byte] == 0); 394 | version = incr_count(version); 395 | an48->child[get_count(version) - 1] = child; 396 | an48->index[byte] = get_count(version); 397 | an48->version = version; 398 | } 399 | break; 400 | case node256: { 401 | art_node256 *an256 = (art_node256 *)an; 402 | debug_assert(an256->child[byte] == 0); 403 | an256->child[byte] = child; 404 | } 405 | break; 406 | default: 407 | assert(0); 408 | } 409 | 410 | if (new && *new) 411 | art_node_unlock(*new); 412 | return 0; 413 | } 414 | 415 | // require: node is locked 416 | inline int art_node_is_full(art_node *an) 417 | { 418 | uint64_t version = an->version; 419 | 420 | debug_assert(is_locked(version)); 421 | 422 | switch (get_type(version)) { 423 | case node4 : return get_count(version) == 4; 424 | case node16: return get_count(version) == 16; 425 | case node48: return get_count(version) == 48; 426 | default: return 0; 427 | } 428 | } 429 | 430 | void art_node_set_prefix(art_node *an, const void *key, size_t off, int prefix_len) 431 | { 432 | memcpy(an->prefix, (char *)key + off, prefix_len); 433 | an->version = set_prefix_len(an->version, prefix_len); 434 | } 435 | 436 | // return the first offset that differs 437 | int art_node_prefix_compare(art_node *an, uint64_t version, const void *key, size_t len, size_t off) 438 | { 439 | debug_assert(off <= len); 440 | 441 | int prefix_len = get_prefix_len(version); 442 | const char *prefix = an->prefix, *cur = (const char *)key; 443 | debug_assert(prefix_len >= 0 && prefix_len <= 8); 444 | 445 | int i = 0; 446 | for (; i < prefix_len && off < len; ++i, ++off) { 447 | if (prefix[i] != cur[off]) 448 | return i; 449 | } 450 | 451 | return i; 452 | } 453 | 454 | // require: node is locked 455 | unsigned char art_node_truncate_prefix(art_node *an, int off) 456 | { 457 | uint64_t version = an->version; 458 | 459 | debug_assert(is_locked(version)); 460 | 461 | debug_assert(off < get_prefix_len(version)); 462 | 463 | // mark expand bit before truncate prefix 464 | version = set_expand(version); 465 | art_node_set_version(an, version); 466 | 467 | int prefix_len = get_prefix_len(version); 468 | char *prefix = an->prefix; 469 | unsigned char ret = prefix[off]; 470 | for (int i = 0, j = off + 1; j < prefix_len; ++i, ++j) 471 | prefix[i] = prefix[j]; 472 | 473 | version = set_prefix_len(version, prefix_len - off - 1); 474 | off += get_offset(version) + 1; 475 | version = set_offset(version, off); 476 | art_node_set_version_unsafe(an, version); 477 | 478 | return ret; 479 | } 480 | 481 | inline int art_node_version_get_prefix_len(uint64_t version) 482 | { 483 | return get_prefix_len(version); 484 | } 485 | 486 | uint64_t art_node_get_stable_expand_version(art_node *an) 487 | { 488 | int loop = 4; 489 | uint64_t version = art_node_get_version_unsafe(an); 490 | while (is_expanding(version)) { 491 | for (int i = 0; i < loop; ++i) 492 | __asm__ volatile("pause" ::: "memory"); 493 | if (loop < 128) 494 | loop += loop; 495 | version = art_node_get_version_unsafe(an); 496 | } 497 | return version; 498 | } 499 | 500 | // uint64_t art_node_get_stable_insert_version(art_node *an) 501 | // { 502 | // uint64_t version; 503 | // do { 504 | // version = art_node_get_version(an); 505 | // } while (is_inserting(version)); 506 | // return version; 507 | // } 508 | 509 | inline int art_node_version_compare_expand(uint64_t version1, uint64_t version2) 510 | { 511 | return (is_expanding(version1) != is_expanding(version2)) || (get_vexpand(version1) != get_vexpand(version2)); 512 | } 513 | 514 | // inline int art_node_version_compare_insert(uint64_t version1, uint64_t version2) 515 | // { 516 | // return is_inserting(version1) != is_inserting(version2) || get_vinsert(version1) != get_vinsert(version2); 517 | // } 518 | 519 | // return 0 on success, 1 on failure 520 | int art_node_lock(art_node *an) 521 | { 522 | while (1) { 523 | // must use `acquire` operation to avoid deadlock 524 | uint64_t version = art_node_get_version(an); 525 | if (is_locked(version)) { 526 | // __asm__ __volatile__ ("pause"); 527 | continue; 528 | } 529 | if (unlikely(is_old(version))) 530 | return 1; 531 | if (__atomic_compare_exchange_n(&an->version, &version, set_lock(version), 532 | 1 /* weak */, __ATOMIC_RELEASE, __ATOMIC_RELAXED)) 533 | break; 534 | } 535 | return 0; 536 | } 537 | 538 | static inline art_node* art_node_get_parent(art_node *an) 539 | { 540 | art_node *parent; 541 | __atomic_load(&an->parent, &parent, __ATOMIC_ACQUIRE); 542 | return parent; 543 | } 544 | 545 | inline void art_node_set_parent_unsafe(art_node *an, art_node *parent) 546 | { 547 | an->parent = parent; 548 | } 549 | 550 | art_node* art_node_get_locked_parent(art_node *an) 551 | { 552 | art_node *parent; 553 | while (1) { 554 | if ((parent = art_node_get_parent(an)) == 0) 555 | break; 556 | if (unlikely(art_node_lock(parent))) 557 | continue; 558 | if (art_node_get_parent(an) == parent) 559 | break; 560 | art_node_unlock(parent); 561 | } 562 | return parent; 563 | } 564 | 565 | // require: node is locked 566 | void art_node_unlock(art_node *an) 567 | { 568 | uint64_t version = an->version; 569 | 570 | debug_assert(is_locked(version)); 571 | 572 | //if (is_inserting(version)) { 573 | // incr_vinsert(version); 574 | // version = unset_insert(version); 575 | //} 576 | if (is_expanding(version)) { 577 | version = incr_vexpand(version); 578 | version = unset_expand(version); 579 | } 580 | 581 | art_node_set_version(an, unset_lock(version)); 582 | } 583 | 584 | int art_node_version_is_old(uint64_t version) 585 | { 586 | return is_old(version); 587 | } 588 | 589 | art_node* art_node_replace_leaf_child(art_node *an, const void *key, size_t len, size_t off) 590 | { 591 | debug_assert(is_leaf(an)); 592 | 593 | const char *k1 = get_leaf_key(an), *k2 = (const char *)key; 594 | size_t l1 = get_leaf_len(an), l2 = len, i; 595 | for (i = off; i < l1 && i < l2 && k1[i] == k2[i]; ++i) 596 | ; 597 | if (unlikely(i == l1 && i == l2)) 598 | return 0; // key exists 599 | 600 | art_node *new = new_art_node(); 601 | art_node_set_offset(new, off); 602 | assert(art_node_lock(new) == 0); 603 | // TODO: i - off might be bigger than 8 604 | assert(i - off <= 8); 605 | art_node_set_prefix(new, k1, off, i - off); 606 | off = i; 607 | unsigned char byte; 608 | byte = off == l1 ? 0 : k1[off]; 609 | assert(art_node_add_child(new, byte, an, 0) == 0); 610 | byte = off == l2 ? 0 : k2[off]; 611 | assert(art_node_add_child(new, byte, (art_node *)make_leaf(k2), 0) == 0); 612 | art_node_unlock(new); 613 | 614 | return new; 615 | } 616 | 617 | // require: node is locked 618 | art_node* art_node_expand_and_insert(art_node *an, const void *key, size_t len, size_t off, int common) 619 | { 620 | debug_assert(is_locked(an->version)); 621 | 622 | art_node* new = new_art_node(); 623 | art_node_set_offset(new, off); 624 | assert(art_node_lock(new) == 0); 625 | art_node_set_prefix(new, key, off, common); 626 | unsigned char byte; 627 | byte = (off + common < len) ? ((unsigned char *)key)[off + common] : 0; 628 | assert(art_node_add_child(new, byte, (art_node *)make_leaf(key), 0) == 0); 629 | byte = art_node_truncate_prefix(an, common); 630 | assert(art_node_add_child(new, byte, an, 0) == 0); 631 | art_node_unlock(new); 632 | 633 | return new; 634 | } 635 | 636 | // require: parent is locked 637 | void art_node_replace_child(art_node *parent, unsigned char byte, art_node *old, art_node *new) 638 | { 639 | (void)old; 640 | uint64_t version = parent->version; 641 | debug_assert(is_locked(version)); 642 | 643 | art_node **child = art_node_find_child(parent, version, byte); 644 | 645 | debug_assert(child && *child == old); 646 | 647 | __atomic_store(child, &new, __ATOMIC_RELEASE); 648 | new->parent = parent; 649 | } 650 | 651 | #ifdef Debug 652 | void art_node_print(art_node *an) 653 | { 654 | uint64_t version = art_node_get_version(an); 655 | 656 | if (an->new) { 657 | printf("has new:\n"); 658 | art_node_print(an->new); 659 | } 660 | 661 | printf("%p\n", an); 662 | printf("is_locked: %u\n", !!is_locked(version)); 663 | printf("is_old: %u\n", !!is_old(version)); 664 | printf("is_expand: %u vexpand: %u\n", !!is_expanding(version), get_vexpand(version)); 665 | printf("prefix_len: %d\n", get_prefix_len(version)); 666 | for (int i = 0; i < get_prefix_len(version); ++i) { 667 | printf("%d ", (unsigned char)an->prefix[i]); 668 | } 669 | printf("\n"); 670 | switch (get_type(version)) { 671 | case node4: { 672 | printf("type 4\n"); 673 | art_node4 *an4 = (art_node4 *)an; 674 | for (int i = 0; i < get_count(version); ++i) { 675 | if (!is_leaf(an4->child[i])) 676 | printf("%d %p\n", an4->key[i], an4->child[i]); 677 | else { 678 | printf("%d ", an4->key[i]); 679 | print_key(get_leaf_key(an4->child[i]), 8); 680 | } 681 | } 682 | } 683 | break; 684 | case node16: { 685 | printf("type 16\n"); 686 | art_node16 *an16 = (art_node16 *)an; 687 | for (int i = 0; i < get_count(version); ++i) { 688 | if (!is_leaf(an16->child[i])) 689 | printf("%d %p\n", an16->key[i], an16->child[i]); 690 | else { 691 | printf("%d ", an16->key[i]); 692 | print_key(get_leaf_key(an16->child[i]), 8); 693 | } 694 | } 695 | } 696 | break; 697 | case node48: { 698 | printf("type 48\n"); 699 | art_node48 *an48 = (art_node48 *)an; 700 | for (int i = 0; i < 256; ++i) 701 | if (an48->index[i]) { 702 | if (!is_leaf(an48->child[an48->index[i] - 1])) 703 | printf("%d %p\n", i, an48->child[an48->index[i] - 1]); 704 | else { 705 | printf("%d ", i); 706 | print_key(get_leaf_key(an48->child[an48->index[i] - 1]), 8); 707 | } 708 | } 709 | } 710 | break; 711 | case node256: { 712 | printf("type 256\n"); 713 | art_node256 *an256 = (art_node256 *)an; 714 | for (int i = 0; i < 256; ++i) 715 | if (an256->child[i]) { 716 | if (!is_leaf(an256->child[i])) 717 | printf("%d %p\n", i, an256->child[i]); 718 | else { 719 | printf("%d ", i); 720 | print_key(get_leaf_key(an256->child[i]), 8); 721 | } 722 | } 723 | } 724 | break; 725 | default: 726 | assert(0); 727 | } 728 | printf("\n"); 729 | } 730 | 731 | void print_key(const void *key, size_t len) 732 | { 733 | unsigned char *n = (unsigned char *)key; 734 | for (int i = 0; i < (int)len; ++i) { 735 | printf("%d ", n[i]); 736 | } 737 | printf("\n"); 738 | } 739 | 740 | #endif // Debug 741 | -------------------------------------------------------------------------------- /art/art_node.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-02-05 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _art_node_h_ 8 | #define _art_node_h_ 9 | 10 | #include 11 | 12 | #ifdef Debug 13 | #include 14 | #define debug_assert(v) assert(v) 15 | #else 16 | #define debug_assert(v) 17 | #endif // Debug 18 | 19 | #define fuck printf("fuck\n"); 20 | 21 | #define likely(x) (__builtin_expect(!!(x), 1)) 22 | #define unlikely(x) (__builtin_expect(!!(x), 0)) 23 | 24 | typedef struct art_node art_node; 25 | 26 | #define is_leaf(ptr) ((uintptr_t)(ptr) & 1) 27 | #define make_leaf(ptr) ((uintptr_t)((const char *)(ptr) - 1) | 1) 28 | #define get_leaf_key(ptr) (((const char *)((uintptr_t)(ptr) & (~(uintptr_t)1))) + 1) 29 | #define get_leaf_len(ptr) ((size_t)*(char *)((uintptr_t)(ptr) & (~(uintptr_t)1))) 30 | 31 | art_node* new_art_node(); 32 | void free_art_node(art_node *an); 33 | art_node** art_node_add_child(art_node *an, unsigned char byte, art_node *child, art_node **new); 34 | art_node** art_node_find_child(art_node *an, uint64_t version, unsigned char byte); 35 | int art_node_is_full(art_node *an); 36 | void art_node_set_prefix(art_node *an, const void *key, size_t off, int prefix_len); 37 | const char* art_node_get_prefix(art_node *an); 38 | int art_node_prefix_compare(art_node *an, uint64_t version, const void *key, size_t len, size_t off); 39 | unsigned char art_node_truncate_prefix(art_node *an, int off); 40 | uint64_t art_node_get_version(art_node *an); 41 | uint64_t art_node_get_version_unsafe(art_node *an); 42 | uint64_t art_node_get_stable_expand_version(art_node *an); 43 | // uint64_t art_node_get_stable_insert_version(art_node *an); 44 | int art_node_version_get_prefix_len(uint64_t version); 45 | int art_node_version_compare_expand(uint64_t version1, uint64_t version2); 46 | // int art_node_version_compare_insert(uint64_t version1, uint64_t version2); 47 | int art_node_lock(art_node *an); 48 | art_node* art_node_get_locked_parent(art_node *an); 49 | void art_node_set_parent_unsafe(art_node *an, art_node *parent); 50 | void art_node_unlock(art_node *an); 51 | int art_node_version_is_old(uint64_t version); 52 | art_node* art_node_replace_leaf_child(art_node *an, const void *key, size_t len, size_t off); 53 | void art_node_replace_child(art_node *parent, unsigned char byte, art_node *old, art_node *new); 54 | art_node* art_node_expand_and_insert(art_node *an, const void *key, size_t len, size_t off, int common); 55 | size_t art_node_version_get_offset(uint64_t version); 56 | 57 | #ifdef Debug 58 | void art_node_print(art_node *an); 59 | void print_key(const void *key, size_t len); 60 | #endif 61 | 62 | #endif /* _art_node_h_ */ 63 | -------------------------------------------------------------------------------- /benchmark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UncP/aili/a2f3225ed0ba7ec6f14ae8ade761525d64f60659/benchmark.png -------------------------------------------------------------------------------- /benchmark_multi_art.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UncP/aili/a2f3225ed0ba7ec6f14ae8ade761525d64f60659/benchmark_multi_art.png -------------------------------------------------------------------------------- /blink/blink_tree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-11-20 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | #include 10 | // TODO: remove this 11 | #include 12 | 13 | #include "../palm/allocator.h" 14 | #include "blink_tree.h" 15 | 16 | static void* run(void *arg) 17 | { 18 | blink_tree *bt = (blink_tree *)arg; 19 | mapping_array *q = bt->array; 20 | 21 | int idx; 22 | void *tmp; 23 | while (1) { 24 | char *buf = (char *)mapping_array_get_busy(q, &idx); 25 | 26 | if (unlikely(buf == 0)) 27 | break; 28 | 29 | int is_write = (int)(buf[0]); 30 | uint32_t len = *((uint32_t *)(buf + 1)); 31 | const void *key = (void *)(buf + 5); 32 | const void *val = (void *)(*((uint64_t *)(buf + len + 5))); 33 | if (is_write) { 34 | blink_tree_write(bt, key, len, val); 35 | } else { 36 | #ifdef Test 37 | assert(blink_tree_read(bt, key, len, &tmp)); 38 | assert((uint64_t)tmp == 3190); 39 | #else 40 | blink_tree_read(bt, key, len, &tmp); 41 | #endif 42 | } 43 | 44 | mapping_array_put_busy(q, idx); 45 | } 46 | 47 | return (void *)0; 48 | } 49 | 50 | blink_tree* new_blink_tree(int thread_num) 51 | { 52 | #ifdef Allocator 53 | init_allocator(); 54 | #endif 55 | 56 | blink_tree *bt = (blink_tree *)malloc(sizeof(blink_tree)); 57 | 58 | blink_node *root = new_blink_node(Root, 0); 59 | 60 | uint32_t offset = (char *)(&(root->pn)) - (char *)(&(root->lock)); 61 | set_node_offset(offset); 62 | 63 | blink_node_insert_infinity_key(root); 64 | 65 | bt->root = root; 66 | 67 | bt->array = 0; 68 | if (thread_num <= 0) { 69 | // array is disabled 70 | return bt; 71 | } 72 | 73 | if (thread_num > 4) 74 | thread_num = 4; 75 | 76 | bt->array = new_mapping_array(1 /* w or r */ + sizeof(uint32_t) + max_key_size + sizeof(void *)); 77 | 78 | bt->thread_num = thread_num; 79 | bt->ids = (pthread_t *)malloc(bt->thread_num * sizeof(pthread_t)); 80 | 81 | for (int i = 0; i < bt->thread_num; ++i) 82 | assert(pthread_create(&bt->ids[i], 0, run, (void *)bt) == 0); 83 | 84 | return bt; 85 | } 86 | 87 | void free_blink_tree(blink_tree *bt) 88 | { 89 | if (bt->array) { 90 | free_mapping_array(bt->array); 91 | 92 | for (int i = 0; i < bt->thread_num; ++i) 93 | assert(pthread_join(bt->ids[i], 0) == 0); 94 | free((void *)bt->ids); 95 | } 96 | 97 | // TODO: free all nodes 98 | 99 | free((void *)bt); 100 | } 101 | 102 | void blink_tree_flush(blink_tree *bt) 103 | { 104 | if (bt->array) 105 | mapping_array_wait_empty(bt->array); 106 | } 107 | 108 | void blink_tree_schedule(blink_tree *bt, int is_write, const void *key, uint32_t len, const void *val) 109 | { 110 | assert(bt->array); 111 | 112 | int idx; 113 | char *buf = (char *)mapping_array_get_free(bt->array, &idx); 114 | 115 | buf[0] = (char)is_write; 116 | *((uint32_t *)(buf + 1)) = len; 117 | memcpy(buf + 5, key, len); 118 | if (val) 119 | *((uint64_t *)(buf + 5 + len)) = *((uint64_t *)&val); 120 | else 121 | *((uint64_t *)(buf + 5 + len)) = 0; 122 | 123 | mapping_array_put_free(bt->array, idx); 124 | } 125 | 126 | struct stack { 127 | blink_node *path[max_descend_depth]; 128 | uint32_t depth; 129 | }; 130 | 131 | static void blink_tree_root_split(blink_tree *bt, blink_node *left, const void *key, uint32_t len, blink_node *right) 132 | { 133 | assert(blink_node_is_root(left)); 134 | 135 | int level = blink_node_get_level(left); 136 | blink_node *new_root = new_blink_node(blink_node_get_type(left), level + 1); 137 | 138 | blink_node_insert_infinity_key(new_root); 139 | 140 | blink_node_set_first(new_root, left); 141 | assert(blink_node_insert(new_root, key, len, (const void *)right) == 1); 142 | 143 | int type = level ? Branch : Leaf; 144 | blink_node_set_type(left, type); 145 | blink_node_set_type(right, type); 146 | 147 | // it's ok to use `relaxed` operation, but it doesn't matter 148 | __atomic_store(&bt->root, &new_root, __ATOMIC_RELEASE); 149 | } 150 | 151 | static blink_node* 152 | blink_tree_descend_to_leaf(blink_tree *bt, const void *key, uint32_t len, struct stack *stack, int is_write) 153 | { 154 | blink_node *curr; 155 | stack->depth = 0; 156 | 157 | // acquire the latest root, it's ok to be stale if it changes right after 158 | // actually it's also ok to use `relaxed` operation 159 | __atomic_load(&bt->root, &curr, __ATOMIC_ACQUIRE); 160 | 161 | // we can read `level` without lock this node since a node's level never changes 162 | int level = blink_node_get_level(curr); 163 | 164 | while (level) { 165 | assert(curr); 166 | blink_node_rlock(curr); 167 | blink_node *child = blink_node_descend(curr, key, len); 168 | blink_node_unlock(curr); 169 | if (likely(blink_node_get_level(child) != level)) { 170 | stack->path[stack->depth++] = curr; 171 | --level; 172 | } 173 | curr = child; 174 | } 175 | 176 | assert(curr && blink_node_get_level(curr) == 0); 177 | if (is_write) 178 | blink_node_wlock(curr); 179 | else 180 | blink_node_rlock(curr); 181 | 182 | return curr; 183 | } 184 | 185 | // Reference: Efficient Locking for Concurrent Operations on B-Trees 186 | int blink_tree_write(blink_tree *bt, const void *key, uint32_t len, const void *val) 187 | { 188 | struct stack stack; 189 | blink_node *curr = blink_tree_descend_to_leaf(bt, key, len, &stack, 1 /* is_write */); 190 | 191 | char fkey[max_key_size]; 192 | uint32_t flen; 193 | void *k = (void *)key; 194 | uint32_t l = len; 195 | void *v = (void *)val; 196 | 197 | for (;;) { 198 | switch (blink_node_insert(curr, k, l, v)) { 199 | case 0: { // key already exists 200 | assert(blink_node_get_level(curr) == 0); 201 | blink_node_unlock(curr); 202 | return 0; 203 | } 204 | case 1: 205 | // key insert succeed 206 | blink_node_unlock(curr); 207 | return 1; 208 | case -1: { // node needs to split 209 | // a normal split 210 | blink_node *new = new_blink_node(blink_node_get_type(curr), blink_node_get_level(curr)); 211 | 212 | blink_node_split(curr, new, fkey, &flen); 213 | if (blink_node_need_move_right(curr, k, l)) 214 | assert(blink_node_insert(new, k, l, v) == 1); 215 | else 216 | assert(blink_node_insert(curr, k, l, v) == 1); 217 | 218 | memcpy(k, fkey, flen); l = flen; v = (void *)new; 219 | 220 | // promote to parent 221 | if (stack.depth) { 222 | blink_node *parent = stack.path[--stack.depth]; 223 | // we can unlock `curr` first, but to be safe just lock `parent` first 224 | blink_node_wlock(parent); 225 | blink_node_unlock(curr); 226 | curr = parent; 227 | } else { 228 | blink_tree_root_split(bt, curr, k, len, new); 229 | blink_node_unlock(curr); 230 | return 1; 231 | } 232 | break; 233 | } 234 | case -3: { 235 | // need to move to right 236 | blink_node *next = blink_node_get_next(curr); 237 | blink_node_wlock(next); 238 | blink_node_unlock(curr); 239 | curr = next; 240 | break; 241 | } 242 | default: assert(0); 243 | } 244 | } 245 | } 246 | 247 | // Reference: Efficient Locking for Concurrent Operations on B-Trees 248 | int blink_tree_read(blink_tree *bt, const void *key, uint32_t len, void **val) 249 | { 250 | struct stack stack; 251 | blink_node *curr = blink_tree_descend_to_leaf(bt, key, len, &stack, 0 /* is_write */); 252 | 253 | void *ret; 254 | for (;;) { 255 | switch ((int64_t)(ret = blink_node_search(curr, key, len))) { 256 | case 0: { // key not exists 257 | blink_node_unlock(curr); 258 | *val = 0; 259 | return 0; 260 | } 261 | // move to right leaf 262 | case -1: { 263 | blink_node *next = blink_node_get_next(curr); 264 | blink_node_rlock(next); 265 | blink_node_unlock(curr); 266 | curr = next; 267 | break; 268 | } 269 | default: 270 | blink_node_unlock(curr); 271 | *val = ret; 272 | return 1; 273 | } 274 | } 275 | } 276 | -------------------------------------------------------------------------------- /blink/blink_tree.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-11-20 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _blink_tree_h_ 8 | #define _blink_tree_h_ 9 | 10 | #include 11 | 12 | #include "node.h" 13 | #include "mapping_array.h" 14 | 15 | typedef struct blink_tree 16 | { 17 | blink_node *root; 18 | 19 | mapping_array *array; 20 | 21 | int thread_num; 22 | pthread_t *ids; 23 | 24 | }blink_tree; 25 | 26 | blink_tree* new_blink_tree(int thread_num); 27 | void free_blink_tree(blink_tree *bt); 28 | int blink_tree_write(blink_tree *bt, const void *key, uint32_t len, const void *val); 29 | int blink_tree_read(blink_tree *bt, const void *key, uint32_t len, void **val); 30 | void blink_tree_schedule(blink_tree *bt, int is_write, const void *key, uint32_t len, const void *val); 31 | void blink_tree_flush(blink_tree *bt); 32 | 33 | #endif /* _blink_tree_h_ */ -------------------------------------------------------------------------------- /blink/latch.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-11-20 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _latch_h_ 8 | #define _latch_h_ 9 | 10 | #include 11 | 12 | // currently latch is just a posix rw lock 13 | typedef struct latch 14 | { 15 | pthread_rwlock_t val[1]; 16 | }latch; 17 | 18 | #define latch_init(latch) pthread_rwlock_init((latch)->val, 0); 19 | #define latch_rlock(latch) pthread_rwlock_rdlock((latch)->val); 20 | #define latch_wlock(latch) pthread_rwlock_wrlock((latch)->val); 21 | #define latch_unlock(latch) pthread_rwlock_unlock((latch)->val); 22 | 23 | #endif /* _latch_h_ */ 24 | -------------------------------------------------------------------------------- /blink/mapping_array.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-11-21 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | // TODO: remove this 10 | #include 11 | 12 | #include "mapping_array.h" 13 | 14 | // find first set bit, ported from linux kernal, `word` must not be 0UL 15 | static inline unsigned long ffs_(unsigned long word) 16 | { 17 | __asm__ ("rep; bsf %1,%0" 18 | : "=r" (word) 19 | : "rm" (word)); 20 | return word; 21 | } 22 | 23 | mapping_array* new_mapping_array(size_t element_bytes) 24 | { 25 | mapping_array *q = (mapping_array *)malloc(sizeof(mapping_array)); 26 | 27 | q->clear = 0; 28 | q->free = (uint64_t)-1; 29 | q->busy = 0; 30 | 31 | size_t aligned_element_bytes = (element_bytes + 63) & (~((size_t)63)); 32 | void *ptr = valloc(max_array_size * aligned_element_bytes); 33 | for (size_t i = 0; i < max_array_size; ++i) 34 | q->elements[i] = (void *)((char *)ptr + i * aligned_element_bytes); 35 | 36 | pthread_mutex_init(q->mutex, 0); 37 | pthread_cond_init(q->free_cond, 0); 38 | pthread_cond_init(q->busy_cond, 0); 39 | 40 | return q; 41 | } 42 | 43 | void free_mapping_array(mapping_array *q) 44 | { 45 | mapping_array_clear(q); 46 | 47 | pthread_mutex_destroy(q->mutex); 48 | pthread_cond_destroy(q->free_cond); 49 | pthread_cond_destroy(q->busy_cond); 50 | 51 | free((void *)q); 52 | } 53 | 54 | void mapping_array_clear(mapping_array *q) 55 | { 56 | pthread_mutex_lock(q->mutex); 57 | 58 | if (q->clear) { 59 | pthread_mutex_unlock(q->mutex); 60 | return ; 61 | } 62 | 63 | while (q->free != (uint64_t)-1 || q->busy != 0) 64 | pthread_cond_wait(q->free_cond, q->mutex); 65 | 66 | q->clear = 1; 67 | 68 | pthread_mutex_unlock(q->mutex); 69 | pthread_cond_broadcast(q->busy_cond); 70 | } 71 | 72 | void mapping_array_wait_empty(mapping_array *q) 73 | { 74 | pthread_mutex_lock(q->mutex); 75 | 76 | assert(q->clear == 0); 77 | 78 | while (q->free != (uint64_t)-1 || q->busy != 0) 79 | pthread_cond_wait(q->free_cond, q->mutex); 80 | 81 | pthread_mutex_unlock(q->mutex); 82 | } 83 | 84 | void* mapping_array_get_free(mapping_array *q, int *idx) 85 | { 86 | pthread_mutex_lock(q->mutex); 87 | 88 | assert(q->clear == 0); 89 | 90 | while (q->free == 0) 91 | pthread_cond_wait(q->free_cond, q->mutex); 92 | 93 | *idx = ffs_(q->free); 94 | 95 | return q->elements[*idx]; 96 | } 97 | 98 | void mapping_array_put_free(mapping_array *q, int idx) 99 | { 100 | assert(idx >= 0 && idx <= 63); 101 | uint64_t mask = ((uint64_t)1) << idx; 102 | q->busy |= mask; 103 | q->free &= ~mask; 104 | 105 | pthread_mutex_unlock(q->mutex); 106 | pthread_cond_signal(q->busy_cond); 107 | } 108 | 109 | void* mapping_array_get_busy(mapping_array *q, int *idx) 110 | { 111 | pthread_mutex_lock(q->mutex); 112 | 113 | while (q->busy == 0 && q->clear == 0) 114 | pthread_cond_wait(q->busy_cond, q->mutex); 115 | 116 | if (q->clear) { 117 | pthread_mutex_unlock(q->mutex); 118 | *idx = -1; 119 | return (void *)0; 120 | } 121 | 122 | *idx = ffs_(q->busy); 123 | 124 | q->busy &= ~(((uint64_t)1) << *idx); 125 | 126 | pthread_mutex_unlock(q->mutex); 127 | return q->elements[*idx]; 128 | } 129 | 130 | void mapping_array_put_busy(mapping_array *q, int idx) 131 | { 132 | assert(idx >= 0 && idx <= 63); 133 | 134 | pthread_mutex_lock(q->mutex); 135 | 136 | assert(q->clear == 0); 137 | 138 | q->free |= ((uint64_t)1) << idx; 139 | 140 | pthread_mutex_unlock(q->mutex); 141 | pthread_cond_signal(q->free_cond); 142 | } 143 | -------------------------------------------------------------------------------- /blink/mapping_array.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-11-21 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _mapping_array_h_ 8 | #define _mapping_array_h_ 9 | 10 | #include 11 | #include 12 | 13 | // do not change it 14 | #define max_array_size 64 15 | 16 | // not an FIFO queue, starvation might occur 17 | typedef struct mapping_array 18 | { 19 | void *elements[max_array_size]; 20 | int clear; 21 | uint64_t free; // bitmap 22 | uint64_t busy; // bitmap 23 | 24 | pthread_mutex_t mutex[1]; 25 | pthread_cond_t free_cond[1]; 26 | pthread_cond_t busy_cond[1]; 27 | }mapping_array; 28 | 29 | mapping_array* new_mapping_array(); 30 | void free_mapping_array(mapping_array *q); 31 | void mapping_array_clear(mapping_array *q); 32 | void mapping_array_wait_empty(mapping_array *q); 33 | void* mapping_array_get_free(mapping_array *q, int *idx); 34 | void mapping_array_put_free(mapping_array *q, int idx); 35 | void* mapping_array_get_busy(mapping_array *q, int *idx); 36 | void mapping_array_put_busy(mapping_array *q, int idx); 37 | 38 | #endif /* _mapping_array_h_ */ 39 | -------------------------------------------------------------------------------- /blink/node.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-11-20 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include "../palm/allocator.h" 13 | #include "node.h" 14 | 15 | blink_node *new_blink_node(uint8_t type, uint8_t level) 16 | { 17 | #ifdef Allocator 18 | blink_node *bn = (blink_node *)allocator_alloc(get_node_size()); 19 | #else 20 | blink_node *bn = (blink_node *)malloc(get_node_size()); 21 | #endif 22 | 23 | latch_init(bn->lock); 24 | node_init(bn->pn, type | Blink, level); 25 | 26 | return bn; 27 | } 28 | 29 | void free_blink_node(blink_node *bn) 30 | { 31 | #ifdef Allocator 32 | allocator_free((void *)bn); 33 | #else 34 | free((void *)bn); 35 | #endif 36 | } 37 | 38 | void free_blink_tree_node(blink_node *bn) 39 | { 40 | (void)bn; 41 | // TODO 42 | } 43 | 44 | inline void blink_node_rlock(blink_node *bn) 45 | { 46 | latch_rlock(bn->lock); 47 | } 48 | 49 | inline void blink_node_wlock(blink_node *bn) 50 | { 51 | latch_wlock(bn->lock); 52 | } 53 | 54 | inline void blink_node_unlock(blink_node *bn) 55 | { 56 | latch_unlock(bn->lock); 57 | } 58 | 59 | blink_node* blink_node_descend(blink_node *bn, const void *key, uint32_t len) 60 | { 61 | return (blink_node *)node_descend(bn->pn, key, len); 62 | } 63 | 64 | int blink_node_insert(blink_node *bn, const void *key, uint32_t len, const void *val) 65 | { 66 | return node_insert(bn->pn, key, len, val); 67 | } 68 | 69 | void* blink_node_search(blink_node *bn, const void *key, uint32_t len) 70 | { 71 | return node_search(bn->pn, key, len); 72 | } 73 | 74 | void blink_node_split(blink_node *old, blink_node *new, char *pkey, uint32_t *plen) 75 | { 76 | node_split(old->pn, new->pn, pkey, plen); 77 | node_insert_fence(old->pn, new->pn, (void *)new, pkey, plen); 78 | } 79 | 80 | int blink_node_need_move_right(blink_node *bn, const void *key, uint32_t len) 81 | { 82 | return node_need_move_right(bn->pn, key, len); 83 | } 84 | 85 | void blink_node_insert_infinity_key(blink_node *bn) 86 | { 87 | char key[max_key_size]; 88 | memset(key, 0xff, max_key_size); 89 | assert(blink_node_insert(bn, key, max_key_size, 0) == 1); 90 | } 91 | 92 | #ifdef Test 93 | 94 | void blink_node_print(blink_node *bn, int detail) 95 | { 96 | node_print(bn->pn, detail); 97 | } 98 | 99 | #endif /* Test */ 100 | -------------------------------------------------------------------------------- /blink/node.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-11-20 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _blink_node_h_ 8 | #define _blink_node_h_ 9 | 10 | #include "latch.h" 11 | #include "../palm/node.h" 12 | 13 | typedef node palm_node; 14 | 15 | // blink node is basically a wrapper for palm node, but with a latch and a fence key 16 | typedef struct blink_node { 17 | latch lock[1]; 18 | char padding[64 - (sizeof(latch) % 64)]; // only for padding 19 | palm_node pn[1]; 20 | }blink_node; 21 | 22 | #define blink_node_is_root(bn) ((int)((bn)->pn->type | Root)) 23 | #define blink_node_get_level(bn) ((int)((bn)->pn->level)) 24 | #define blink_node_get_type(bn) ((bn)->pn->type) 25 | #define blink_node_set_type(bn, type) ((bn)->pn->type = ((type) | Blink)) 26 | #define blink_node_get_next(bn) ((blink_node *)((bn)->pn->next)) 27 | #define blink_node_set_first(bn, fir) ((bn)->pn->first = ((palm_node *)fir)) 28 | 29 | blink_node* new_blink_node(uint8_t type, uint8_t level); 30 | void free_blink_node(blink_node *bn); 31 | void free_blink_tree_node(blink_node *bn); 32 | void blink_node_rlock(blink_node *bn); 33 | void blink_node_wlock(blink_node *bn); 34 | void blink_node_unlock(blink_node *bn); 35 | blink_node* blink_node_descend(blink_node *bn, const void *key, uint32_t len); 36 | int blink_node_insert(blink_node *bn, const void *key, uint32_t len, const void *val); 37 | void blink_node_insert_infinity_key(blink_node *bn); 38 | void* blink_node_search(blink_node *bn, const void *key, uint32_t len); 39 | void blink_node_split(blink_node *old, blink_node *new, char *pkey, uint32_t *plen); 40 | int blink_node_need_move_right(blink_node *bn, const void *key, uint32_t len); 41 | 42 | #ifdef Test 43 | 44 | void blink_node_print(blink_node *bn, int detail); 45 | 46 | #endif /* Test */ 47 | 48 | #endif /* _blink_node_h_ */ 49 | -------------------------------------------------------------------------------- /example/Makefile: -------------------------------------------------------------------------------- 1 | all:palm_tree.c blink_tree.c mass_tree.c adaptive_radix_tree.c 2 | cd .. && make lib "DFLAGS+=-DAllocator" 3 | gcc -std=c99 -o palm_tree palm_tree.c ../libaili.a -lpthread -lm 4 | gcc -std=c99 -o blink_tree blink_tree.c ../libaili.a -lpthread -lm -D_GNU_SOURCE 5 | gcc -std=c99 -o mass_tree mass_tree.c ../libaili.a -lpthread -lm 6 | gcc -std=c99 -o adaptive_radix_tree adaptive_radix_tree.c ../libaili.a -lpthread -lm 7 | 8 | clean: 9 | rm palm_tree 10 | rm blink_tree 11 | rm mass_tree 12 | rm adaptive_radix_tree 13 | -------------------------------------------------------------------------------- /example/adaptive_radix_tree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-10-18 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | 10 | #include "../art/art.h" 11 | 12 | /* a simple example as how to use adaptive radix tree */ 13 | 14 | int main() 15 | { 16 | char hello[6]; 17 | hello[0] = 5; 18 | hello[1] = 'h'; 19 | hello[2] = 'e'; 20 | hello[3] = 'l'; 21 | hello[4] = 'l'; 22 | hello[5] = 'o'; 23 | 24 | adaptive_radix_tree *art = new_adaptive_radix_tree(); 25 | 26 | // adaptive radix tree is just a toy, value is not stored 27 | adaptive_radix_tree_put(art, (const void *)&hello[1], 5); 28 | assert(adaptive_radix_tree_get(art, (const void *)&hello[1], 5) == (void *)&hello[1]); 29 | printf("%s\n", hello); 30 | 31 | free_adaptive_radix_tree(art); 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /example/blink_tree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-11-22 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | 10 | #include "../blink/blink_tree.h" 11 | 12 | /* a simple example as how to use blink tree */ 13 | 14 | int main() 15 | { 16 | static const char *hello = "hello"; 17 | static const char *world = "world"; 18 | 19 | blink_tree *bt = new_blink_tree(2 /* thread_number */); 20 | 21 | // synchronous operation 22 | blink_tree_write(bt, (const void *)hello, 5, (const void *)world); 23 | void *value; 24 | blink_tree_read(bt, (const void *)hello, 5, &value); 25 | assert(value == world); 26 | printf("%s\n", value); 27 | 28 | // asynchronous operation 29 | blink_tree_schedule(bt, 1 /* is_write */, (const void *)world, 5, (const void *)hello); 30 | blink_tree_flush(bt); // wait the job to be done 31 | blink_tree_read(bt, (const void *)world, 5, &value); 32 | assert(value == hello); 33 | printf("%s\n", value); 34 | 35 | free_blink_tree(bt); 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /example/mass_tree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-10-18 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | 10 | #include "../mass/mass_tree.h" 11 | 12 | /* a simple example as how to use mass tree */ 13 | 14 | int main() 15 | { 16 | static const char *hello = "hello"; 17 | 18 | mass_tree *mt = new_mass_tree(); 19 | 20 | // since mass tree is just a toy, value is not stored in mass tree, use `null` as value, 21 | // extra infomartion about key is stored for debug purpose 22 | mass_tree_put(mt, (const void *)hello, 5, (void *)0); 23 | assert(mass_tree_get(mt, (const void *)hello, 5) == (void *)hello); 24 | printf("%s\n", hello); 25 | 26 | free_mass_tree(mt); 27 | return 0; 28 | } 29 | -------------------------------------------------------------------------------- /example/palm_tree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-10-04 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | 10 | #include "../palm/palm_tree.h" 11 | 12 | /* a simple example as how to use palm tree */ 13 | 14 | int main() 15 | { 16 | static const char *hello = "hello"; 17 | static const char *world = "world"; 18 | 19 | palm_tree *pt = new_palm_tree(2 /* thread_number */, 4 /* queue_size */); 20 | 21 | batch *b1 = new_batch(); 22 | batch_add_write(b1, (const void *)hello, 5, (const void *)world); 23 | palm_tree_execute(pt, b1); 24 | 25 | batch *b2 = new_batch(); 26 | batch_add_read(b2, (const void *)hello, 5); // index 0 27 | palm_tree_execute(pt, b2); 28 | 29 | // wait until all the batch be executed 30 | palm_tree_flush(pt); 31 | 32 | const char *value = (const char *)batch_get_value_at(b2, 0); 33 | assert(value == world); 34 | printf("%s\n", value); 35 | 36 | free_batch(b1); 37 | free_batch(b2); 38 | free_palm_tree(pt); 39 | return 0; 40 | } 41 | -------------------------------------------------------------------------------- /generate_data.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-09-05 4 | * license: BSD-3 5 | **/ 6 | 7 | #define _XOPEN_SOURCE 500 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | static const char *dir = "./data"; 20 | 21 | static void generate_test_data(int file_name, int key_num, int key_len, int random, int initial, int step) 22 | { 23 | uint64_t total_len = 0; 24 | 25 | char file[64]; 26 | memset(file, 0, 64); 27 | memcpy(file, dir, strlen(dir)); 28 | sprintf(file + strlen(dir), "/%d", file_name); 29 | int fd = open(file, O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP); 30 | assert(fd != -1); 31 | 32 | const char *string = "abcdefghijklmnopqrstuvwxyz0123456789"; 33 | const int str_len = strlen(string); 34 | 35 | char buf[8192]; 36 | int ptr = 0; 37 | char key[key_len + 1]; 38 | for (int i = 0; i < key_len; ++i) 39 | key[i] = '0'; 40 | key[key_len - 1] = '0' + initial; 41 | for (int i = 0; i < key_num; ++i) { 42 | if (random) { 43 | for (int k = 0; k < key_len; ++k) 44 | key[k] = string[rand() % str_len]; 45 | } else { 46 | int cur = key_len - 1, s = step; 47 | while (key[cur] + step > '9') { 48 | key[cur] = key[cur] + step - 10; 49 | s = 1; 50 | assert(--cur >= 0); 51 | } 52 | key[cur] += s; 53 | } 54 | key[key_len] = '\n'; 55 | if (ptr + key_len + 1 >= 8192) { 56 | assert(pwrite(fd, buf, ptr, total_len) == ptr); 57 | total_len += ptr; 58 | ptr = 0; 59 | } 60 | memcpy(buf + ptr, key, key_len + 1); 61 | ptr += key_len + 1; 62 | if (i && (i % 1000000) == 0) 63 | printf("total %d, generated %d\n", key_num, i); 64 | } 65 | 66 | assert(pwrite(fd, buf, ptr, total_len) == ptr); 67 | 68 | close(fd); 69 | } 70 | 71 | int main(int argc, char **argv) 72 | { 73 | if (argc < 4) { 74 | printf("file_number key_number key_length\n"); 75 | exit(1); 76 | } 77 | srand(time(NULL)); 78 | 79 | int file_number = atoi(argv[1]); 80 | int key_num = atoi(argv[2]); 81 | int key_len = atoi(argv[3]); 82 | if (file_number <= 0) file_number = 1; 83 | if (key_num <= 0) key_num = 1000000; 84 | if (key_num > 100000000) key_num = 100000000; 85 | if (key_len <= 0) key_len = 8; 86 | if (key_len > 255) key_len = 255; 87 | 88 | if (access(dir, F_OK)) 89 | assert(mkdir(dir, S_IRUSR | S_IWUSR | S_IXUSR | S_IRGRP | S_IROTH) >= 0); 90 | 91 | int file = 1; 92 | printf("generate random test data\n"); 93 | for (int i = 0; i < file_number; ++i, ++file) 94 | generate_test_data(file, key_num, key_len, 1 /* random */, 0, 0); 95 | 96 | printf("generate sequential test data\n"); 97 | for (int i = 0; i < file_number; ++i, ++file) 98 | generate_test_data(file, key_num, key_len, 0 /* sequential */, i, file_number); 99 | 100 | printf("key_num: %d key_len: %d\n", key_num, key_len); 101 | return 0; 102 | } 103 | -------------------------------------------------------------------------------- /hot/hot.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-11-02 4 | * license: BSD-3 5 | **/ 6 | -------------------------------------------------------------------------------- /hot/hot.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-11-02 4 | * license: BSD-3 5 | **/ 6 | -------------------------------------------------------------------------------- /hot/hot_node.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-11-02 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | 9 | #include "hot_node.h" 10 | 11 | #define node_header \ 12 | uint64_t type:3; \ 13 | uint64_t height:3; \ 14 | uint64_t n_keys:6; \ 15 | uint64_t locked:1; \ 16 | uint64_t unused:51; 17 | 18 | typedef struct hot_node_s_8 19 | { 20 | node_header; 21 | uint8_t keys[32]; 22 | void *childs[32]; 23 | }hot_node_s_8; 24 | -------------------------------------------------------------------------------- /hot/hot_node.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-11-02 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _hot_node_h_ 8 | #define _hot_node_h_ 9 | 10 | typedef struct hot_node hot_node; 11 | 12 | 13 | #endif /* _hot_node_h_ */ 14 | -------------------------------------------------------------------------------- /mass/mass_node.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-10-05 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _mass_node_h_ 8 | #define _mass_node_h_ 9 | 10 | #include 11 | 12 | #define likely(x) (__builtin_expect(!!(x), 1)) 13 | #define unlikely(x) (__builtin_expect(!!(x), 0)) 14 | 15 | // mass_node type 16 | #define Interior 0 17 | #define Border 1 18 | 19 | /** 20 | * layout of a mass_node's version (32 bit): 21 | * lock insert split delete root border not-used vsplit vinsert 22 | * | 1 | 1 | 1 | 1 | 1 | 1 | 2 | 8 | 16 | 23 | * 24 | **/ 25 | 26 | #define LOCK_BIT ((uint32_t)1 << 31) 27 | #define INSERT_BIT ((uint32_t)1 << 30) 28 | #define SPLIT_BIT ((uint32_t)1 << 29) 29 | #define DELETE_BIT ((uint32_t)1 << 28) 30 | #define ROOT_BIT ((uint32_t)1 << 27) 31 | #define BORDER_BIT ((uint32_t)1 << 26) 32 | 33 | // `vsplit` is a 8 bit field 34 | #define get_vsplit(n) ((uint32_t)(((n) >> 16) & 0xff)) 35 | #define incr_vsplit(n) (((n) & 0xff00ffff) | (((n) + 0x10000) & 0xff0000)) // overflow is handled 36 | 37 | // `vinsert` is a 16 bit field 38 | #define get_vinsert(n) ((n) & 0xffff) 39 | #define incr_vinsert(n) (((n) & 0xffff0000) | (((n) + 1) & 0xffff)) // overflow is handled 40 | 41 | #define set_lock(n) ((n) | LOCK_BIT) 42 | #define set_insert(n) ((n) | INSERT_BIT) 43 | #define set_split(n) ((n) | SPLIT_BIT) 44 | #define set_delete(n) ((n) | DELETE_BIT) 45 | #define set_root(n) ((n) | ROOT_BIT) 46 | #define set_border(n) ((n) | BORDER_BIT) 47 | 48 | #define unset_lock(n) ((n) & (~LOCK_BIT)) 49 | #define unset_insert(n) ((n) & (~INSERT_BIT)) 50 | #define unset_split(n) ((n) & (~SPLIT_BIT)) 51 | #define unset_root(n) ((n) & (~ROOT_BIT)) 52 | 53 | #define is_locked(n) ((n) & LOCK_BIT) 54 | #define is_inserting(n) ((n) & INSERT_BIT) 55 | #define is_spliting(n) ((n) & SPLIT_BIT) 56 | #define is_deleted(n) ((n) & DELETE_BIT) 57 | #define is_root(n) ((n) & ROOT_BIT) 58 | #define is_border(n) ((n) & BORDER_BIT) 59 | #define is_interior(n) (!is_border(n)) 60 | 61 | typedef struct mass_node mass_node; 62 | 63 | mass_node* new_mass_node(int type); 64 | void free_mass_node(mass_node *n); 65 | void mass_node_prefetch(mass_node *n); 66 | void border_mass_node_prefetch_write(mass_node *n); 67 | void border_mass_node_prefetch_read(mass_node *n); 68 | void mass_node_lock_unsafe(mass_node *n); 69 | void mass_node_unlock_unsafe(mass_node *n); 70 | void mass_node_lock(mass_node *n); 71 | void mass_node_unlock(mass_node *n); 72 | void mass_node_set_root_unsafe(mass_node *n); 73 | void mass_node_unset_root_unsafe(mass_node *n); 74 | uint32_t mass_node_get_version(mass_node *n); 75 | uint32_t mass_node_get_version_unsafe(mass_node *n); 76 | uint32_t mass_node_get_stable_version(mass_node *n); 77 | void mass_node_set_version(mass_node *n, uint32_t version); 78 | mass_node* mass_node_get_next(mass_node *n); 79 | mass_node* mass_node_get_parent(mass_node *n); 80 | mass_node* mass_node_get_locked_parent(mass_node *n); 81 | void mass_node_set_first_child(mass_node *n, mass_node *c); 82 | int mass_node_is_full(mass_node *n); 83 | int mass_node_include_key(mass_node *n, uint64_t off); 84 | int mass_node_get_conflict_key_index(mass_node *n, const void *key, uint32_t len, uint32_t off, void **ckey, uint32_t *clen); 85 | void mass_node_replace_at_index(mass_node *n, int index, mass_node *n1); 86 | void mass_node_swap_child(mass_node *n, mass_node *c, mass_node *c1); 87 | mass_node* mass_node_descend(mass_node *n, uint64_t cur); 88 | void* border_mass_node_insert(mass_node *n, const void *key, uint32_t len, uint32_t off, const void *val, int is_link); 89 | void interior_mass_node_insert(mass_node *n, uint64_t key, mass_node *child); 90 | mass_node* mass_node_split(mass_node *n, uint64_t *fence); 91 | void* mass_node_search(mass_node *n, uint64_t cur, void **value); 92 | 93 | int mass_compare_key(uint64_t k1, uint64_t k2); 94 | uint64_t get_next_keyslice(const void *key, uint32_t len, uint32_t off); 95 | uint64_t get_next_keyslice_and_advance(const void *key, uint32_t len, uint32_t *off); 96 | 97 | #ifndef htobe64 98 | #ifdef __APPLE__ 99 | #include 100 | #define htobe64(x) OSSwapHostToBigInt64(x) 101 | #endif 102 | #ifdef __linux__ 103 | #define _BSD_SOURCE 104 | #include 105 | #endif 106 | #endif 107 | 108 | #ifdef Test 109 | 110 | void free_mass_node_raw(mass_node *n); 111 | void mass_node_print(mass_node *n); 112 | void mass_node_validate(mass_node *n); 113 | 114 | #endif /* Test */ 115 | 116 | #endif /* _mass_mass_node_h_ */ 117 | -------------------------------------------------------------------------------- /mass/mass_tree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-10-05 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | // TODO: remove this 12 | #include 13 | 14 | #include "../palm/allocator.h" 15 | #include "mass_tree.h" 16 | 17 | mass_tree* new_mass_tree() 18 | { 19 | #ifdef Allocator 20 | init_allocator(); 21 | #endif 22 | 23 | mass_tree *mt = (mass_tree *)malloc(sizeof(mass_tree)); 24 | 25 | mass_node *r = new_mass_node(Border); 26 | mass_node_set_root_unsafe(r); 27 | 28 | mt->root = r; 29 | 30 | return mt; 31 | } 32 | 33 | void free_mass_tree(mass_tree *mt) 34 | { 35 | free_mass_node(mt->root); 36 | } 37 | 38 | #ifdef Test 39 | 40 | void mass_tree_validate(mass_tree *mt) 41 | { 42 | mass_node_validate(mt->root); 43 | } 44 | 45 | #endif // Test 46 | 47 | // require: `n` and `n1` are locked 48 | static mass_node* mass_tree_grow(mass_node *n, uint64_t fence, mass_node *n1) 49 | { 50 | mass_node *r = new_mass_node(Interior); 51 | 52 | // NOTE: not necessary, lock it to make `mass_node_insert` happy 53 | mass_node_lock_unsafe(r); 54 | 55 | mass_node_set_root_unsafe(r); 56 | 57 | mass_node_set_first_child(r, n); 58 | 59 | interior_mass_node_insert(r, fence, n1); 60 | 61 | mass_node_unset_root_unsafe(n1); 62 | mass_node_unset_root_unsafe(n); 63 | 64 | mass_node_unlock_unsafe(r); 65 | 66 | return r; 67 | } 68 | 69 | // find the border mass_node and record stable version 70 | static mass_node* find_border_mass_node(mass_node *r, uint64_t cur, uint32_t *version) 71 | { 72 | uint32_t v; 73 | mass_node *n; 74 | 75 | retry: 76 | n = r; 77 | assert(n); 78 | v = mass_node_get_stable_version(n); 79 | // it's possible that a root has split 80 | if (!is_root(v)) { 81 | r = mass_node_get_parent(n); 82 | goto retry; 83 | } 84 | 85 | descend: 86 | if (is_border(v)) { 87 | *version = v; 88 | return n; 89 | } 90 | 91 | mass_node_prefetch(n); 92 | 93 | mass_node *n1 = mass_node_descend(n, cur); 94 | assert(n1); 95 | // this is a crucial step, must not move the line below to case 1 96 | uint32_t v1 = mass_node_get_stable_version(n1); 97 | 98 | uint32_t diff = mass_node_get_version(n) ^ v; 99 | if (diff == LOCK_BIT || diff == 0) { 100 | // case 1: neither insert nor split happens between last stable version and current version, 101 | // descend to child mass_node 102 | n = n1; 103 | v = v1; 104 | goto descend; 105 | } 106 | 107 | uint32_t v2 = mass_node_get_stable_version(n); 108 | // case 2: this mass_node had a split, retry from root, pessimistic 109 | if (get_vsplit(v2) != get_vsplit(v)) 110 | goto retry; 111 | 112 | // case 3: this mass_node inserted a key, retry this mass_node 113 | v = v2; 114 | goto descend; 115 | } 116 | 117 | // require: `n` is locked 118 | // create a subtree lazily and then insert kv into it, at last replace kv with this subtree 119 | static void create_new_layer(mass_node *n, const void *key, uint32_t len, uint32_t off, const void *val) 120 | { 121 | void *ckey; 122 | uint32_t clen; 123 | int idx = mass_node_get_conflict_key_index(n, key, len, off, &ckey, &clen); 124 | 125 | // advance key offset that causes this conflict 126 | off += sizeof(uint64_t); 127 | 128 | // these 2 key can still be have mutiple common prefix keyslice, we need to loop and create 129 | // subtree until they don't 130 | mass_node *head = 0, *parent = 0; 131 | uint64_t lks; 132 | uint32_t noff = off + sizeof(uint64_t); 133 | while (noff <= clen && noff <= len) { 134 | uint64_t ks1 = get_next_keyslice(ckey, clen, off); 135 | uint64_t ks2 = get_next_keyslice(key, len, off); 136 | // no need to use `mass_compare_key` 137 | if (ks1 != ks2) break; 138 | 139 | mass_node *bn = new_mass_node(Border); 140 | mass_node_set_root_unsafe(bn); 141 | if (head == 0) head = bn; 142 | if (parent) { 143 | mass_node_lock_unsafe(parent); 144 | assert((uint64_t)border_mass_node_insert(parent, &lks, sizeof(uint64_t), 0 /* off */, bn, 1 /* is_link */) == 1); 145 | mass_node_unlock_unsafe(parent); 146 | } 147 | lks = ks1; 148 | parent = bn; 149 | off += sizeof(uint64_t); 150 | noff = off + sizeof(uint64_t); 151 | } 152 | 153 | // insert these 2 keys without conflict into border mass_node 154 | mass_node *bn = new_mass_node(Border); 155 | mass_node_set_root_unsafe(bn); 156 | mass_node_lock_unsafe(bn); 157 | assert((uint64_t)border_mass_node_insert(bn, ckey, clen, off, 0, 0 /* is_link */) == 1); 158 | assert((uint64_t)border_mass_node_insert(bn, key, len, off, val, 0 /* is_link */) == 1); 159 | mass_node_unlock_unsafe(bn); 160 | 161 | if (parent) { 162 | mass_node_lock_unsafe(parent); 163 | assert((uint64_t)border_mass_node_insert(parent, &lks, sizeof(uint64_t), 0 /* off */, bn, 1 /* is_link */) == 1); 164 | mass_node_unlock_unsafe(parent); 165 | } 166 | 167 | // now replace previous key with new subtree link 168 | if (head == 0) 169 | mass_node_replace_at_index(n, idx, bn); 170 | else 171 | mass_node_replace_at_index(n, idx, head); 172 | } 173 | 174 | // require: `n` and `n1` is locked 175 | static void mass_tree_promote_split_mass_node(mass_tree *mt, mass_node *n, uint64_t fence, mass_node *n1) 176 | { 177 | mass_node *p; 178 | ascend: 179 | p = mass_node_get_locked_parent(n); 180 | if (unlikely(p == 0)) { 181 | mass_node *new_root = mass_tree_grow(n, fence, n1); 182 | mt->root = new_root; 183 | mass_node_unlock(n); 184 | mass_node_unlock(n1); 185 | return ; 186 | } 187 | 188 | uint32_t v; 189 | v = mass_node_get_version(p); 190 | if (unlikely(is_border(v))) { // `n` is a sub tree 191 | mass_node *sub_root = mass_tree_grow(n, fence, n1); 192 | mass_node_swap_child(p, n, sub_root); 193 | mass_node_unlock(n); 194 | mass_node_unlock(n1); 195 | mass_node_unlock(p); 196 | } else if (likely(mass_node_is_full(p) == 0)) { 197 | interior_mass_node_insert(p, fence, n1); 198 | mass_node_unlock(n); 199 | mass_node_unlock(n1); 200 | mass_node_unlock(p); 201 | } else { // mass_node is full 202 | v = set_split(v); 203 | mass_node_set_version(p, v); 204 | mass_node_unlock(n); 205 | uint64_t fence1 = 0; 206 | mass_node *p1 = mass_node_split(p, &fence1); 207 | assert(fence1); 208 | if (mass_compare_key(fence, fence1) < 0) 209 | interior_mass_node_insert(p, fence, n1); 210 | else 211 | interior_mass_node_insert(p1, fence, n1); 212 | mass_node_unlock(n1); 213 | n = p; 214 | fence = fence1; 215 | n1 = p1; 216 | goto ascend; 217 | } 218 | } 219 | 220 | int mass_tree_put(mass_tree *mt, const void *key, uint32_t len, const void *val) 221 | { 222 | uint32_t off = 0, v; 223 | uint64_t cur; 224 | // it's ok to use stale root 225 | mass_node *r = mt->root, *n; 226 | 227 | again: 228 | cur = get_next_keyslice(key, len, off); 229 | n = find_border_mass_node(r, cur, &v); 230 | 231 | forward: 232 | if (unlikely(is_deleted(v))) { 233 | // NOTE: remove this if we ever implement `mass_tree_delete` 234 | assert(0); 235 | goto again; 236 | } 237 | 238 | // before we write this mass_node, a lock must be obtained 239 | mass_node_lock(n); 240 | 241 | // it's ok to use `unsafe` operation since mass_node is locked 242 | uint32_t diff = mass_node_get_version_unsafe(n) ^ v; 243 | if (diff != LOCK_BIT) { // mass_node has changed between we acquire this mass_node and lock this mass_node 244 | // unlock first 245 | mass_node_unlock(n); 246 | 247 | v = mass_node_get_stable_version(n); 248 | mass_node *next = mass_node_get_next(n); 249 | // there might be inserts or inserts happened, traverse through the link 250 | while (!is_deleted(v) && next && mass_node_include_key(next, cur)) { 251 | n = next; 252 | v = mass_node_get_stable_version(n); 253 | next = mass_node_get_next(n); 254 | } 255 | goto forward; 256 | } 257 | 258 | border_mass_node_prefetch_write(n); 259 | 260 | void *ret = border_mass_node_insert(n, key, len, off, val, 0 /* is_link */); 261 | switch ((uint64_t)ret) { 262 | case 0: // key existed 263 | mass_node_unlock(n); 264 | return 0; 265 | case 1: // key inserted 266 | mass_node_unlock(n); 267 | return 1; 268 | case -1: { // need to create a deeper layer 269 | create_new_layer(n, key, len, off, val); 270 | mass_node_unlock(n); 271 | return 1; 272 | } 273 | case -2: { // mass_node is full, need to split and promote 274 | uint64_t fence = 0; 275 | mass_node *n1 = mass_node_split(n, &fence); 276 | assert(fence); 277 | uint64_t cur = get_next_keyslice(key, len, off); 278 | // equal is not possible 279 | if (mass_compare_key(cur, fence) < 0) 280 | assert((uint64_t)border_mass_node_insert(n, key, len, off, val, 0 /* is_link */) == 1); 281 | else 282 | assert((uint64_t)border_mass_node_insert(n1, key, len, off, val, 0 /* is_link */) == 1); 283 | 284 | mass_tree_promote_split_mass_node(mt, n, fence, n1); 285 | return 1; 286 | } 287 | default: // need to go to a deeper layer 288 | mass_node_unlock(n); 289 | r = (mass_node *)ret; 290 | // if we need to advance to next layer, then key offset will not exceed key length 291 | off += sizeof(uint64_t); 292 | goto again; 293 | } 294 | } 295 | 296 | void* mass_tree_get(mass_tree *mt, const void *key, uint32_t len) 297 | { 298 | uint32_t off = 0, v; 299 | uint64_t cur; 300 | // it's ok to use stale root 301 | mass_node *r = mt->root, *n; 302 | 303 | again: 304 | cur = get_next_keyslice_and_advance(key, len, &off); 305 | n = find_border_mass_node(r, cur, &v); 306 | 307 | forward: 308 | if (is_deleted(v)) { 309 | // NOTE: remove this if we ever implement `mass_tree_delete` 310 | assert(0); 311 | goto again; 312 | } 313 | 314 | border_mass_node_prefetch_read(n); 315 | 316 | void *suffix; 317 | void *lv = mass_node_search(n, cur, &suffix); 318 | 319 | uint32_t diff = mass_node_get_version(n) ^ v; 320 | if (diff != LOCK_BIT && diff != 0) { 321 | v = mass_node_get_stable_version(n); 322 | mass_node *next = mass_node_get_next(n); 323 | // there might be inserts or inserts happened, traverse through the link 324 | while (!is_deleted(v) && next && mass_node_include_key(next, cur)) { 325 | n = next; 326 | v = mass_node_get_stable_version(n); 327 | next = mass_node_get_next(n); 328 | } 329 | goto forward; 330 | } 331 | 332 | // case 1: unstable state, need to retry 333 | if (unlikely((uint64_t)lv == 1)) goto forward; 334 | 335 | if (suffix) { 336 | uint32_t clen = (uint32_t)((uint64_t)lv); 337 | uint32_t coff = (uint32_t)((uint64_t)lv >> 32); 338 | assert(coff == off); 339 | // case 2: key exists 340 | if (clen == len && !memcmp((char *)key + off, (char *)suffix + off, len - off)) 341 | return suffix; 342 | } else if (lv) { 343 | // case 3: goto a deeper layer 344 | r = (mass_node *)lv; 345 | // offset is already set in `get_next_keyslice_and_advance` 346 | goto again; 347 | } 348 | 349 | // case 4: key does not exist 350 | return 0; 351 | } 352 | -------------------------------------------------------------------------------- /mass/mass_tree.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-10-05 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _mass_tree_h_ 8 | #define _mass_tree_h_ 9 | 10 | #include "mass_node.h" 11 | 12 | typedef struct mass_tree 13 | { 14 | mass_node *root; 15 | }mass_tree; 16 | 17 | mass_tree* new_mass_tree(); 18 | void free_mass_tree(mass_tree *mt); 19 | int mass_tree_put(mass_tree *mt, const void *key, uint32_t len, const void *val); 20 | void* mass_tree_get(mass_tree *mt, const void *key, uint32_t len); 21 | 22 | #ifdef Test 23 | 24 | void mass_tree_validate(mass_tree *mt); 25 | 26 | #endif // Test 27 | 28 | #endif /* _mass_tree_h_ */ 29 | -------------------------------------------------------------------------------- /palm/allocator.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-11-23 4 | * license: BSD-3 5 | **/ 6 | 7 | #define _BSD_SOURCE 8 | #define _POSIX_C_SOURCE 200112L 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "allocator.h" 16 | 17 | #define likely(x) (__builtin_expect(!!(x), 1)) 18 | #define unlikely(x) (__builtin_expect(!!(x), 0)) 19 | 20 | static pthread_key_t key; 21 | static int initialized = 0; 22 | static pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 23 | 24 | static inline void* block_alloc(block *b, size_t size, int *success) 25 | { 26 | if (likely((b->now + size) <= b->tot)) { 27 | *success = 1; 28 | void *ptr = (char *)b->buffer + b->now; 29 | b->now += size; 30 | return ptr; 31 | } else { 32 | *success = 0; 33 | return 0; 34 | } 35 | } 36 | 37 | static inline block* new_block(block *meta) 38 | { 39 | size_t s = (sizeof(block) + 63) & (~((size_t)63)); 40 | int success; 41 | block *b = block_alloc(meta, s, &success); 42 | if (likely(success)) { 43 | #ifdef __linux__ 44 | b->buffer = mmap(0, block_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 45 | #else 46 | b->buffer = mmap(0, block_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); 47 | #endif 48 | assert(b->buffer != MAP_FAILED); 49 | b->now = 0; 50 | b->tot = block_size; 51 | b->next = 0; 52 | return b; 53 | } 54 | return 0; 55 | } 56 | 57 | static inline block* new_meta_block() 58 | { 59 | char *buf = (char *)mmap(0, meta_block_size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0); 60 | assert(buf != MAP_FAILED); 61 | block *m = (block *)buf; 62 | m->buffer = (void *)buf; 63 | m->now = (sizeof(block) + 63) & (~((size_t)63)); 64 | m->tot = meta_block_size; 65 | m->next = 0; 66 | return m; 67 | } 68 | 69 | static void free_block(block *b) 70 | { 71 | munmap(b->buffer, b->tot); 72 | } 73 | 74 | void destroy_allocator(void *arg) 75 | { 76 | // for test reason don't dealloc memory when this thread exit, 77 | // memory will be munmaped when process exit 78 | return ; 79 | 80 | allocator *a = (allocator *)arg; 81 | 82 | block *curr = a->curr; 83 | while (curr) { 84 | block *next = curr->next; 85 | free_block(curr); 86 | curr = next; 87 | } 88 | 89 | block *small_curr = a->small_curr; 90 | while (small_curr) { 91 | block *next = small_curr->next; 92 | free_block(small_curr); 93 | small_curr = next; 94 | } 95 | 96 | curr = a->meta_curr; 97 | while (curr) { 98 | block *next = curr->next; 99 | free_block(curr); 100 | curr = next; 101 | } 102 | 103 | free((void *)a); 104 | } 105 | 106 | void init_allocator() 107 | { 108 | pthread_mutex_lock(&mutex); 109 | if (initialized == 0) { 110 | assert(pthread_key_create(&key, destroy_allocator) == 0); 111 | initialized = 1; 112 | } 113 | pthread_mutex_unlock(&mutex); 114 | } 115 | 116 | static void init_thread_allocator() 117 | { 118 | allocator *a; 119 | assert(posix_memalign((void **)&a, 64, sizeof(allocator)) == 0); 120 | block *meta = new_meta_block(); 121 | a->meta_curr = meta; 122 | 123 | block *curr = new_block(a->meta_curr); 124 | assert(curr); 125 | a->curr = curr; 126 | 127 | block *small_curr = new_block(a->meta_curr); 128 | assert(small_curr); 129 | a->small_curr = small_curr; 130 | 131 | assert(pthread_setspecific(key, (void *)a) == 0); 132 | } 133 | 134 | static inline allocator* get_thread_allocator() 135 | { 136 | allocator *a; 137 | 138 | #ifdef __linux__ 139 | a = (allocator *)pthread_getspecific(key); 140 | if (unlikely(a == 0)) { 141 | init_thread_allocator(); 142 | a = (allocator *)pthread_getspecific(key); 143 | assert(a); 144 | } 145 | #else 146 | a = pthread_getspecific(key); 147 | if (unlikely(a == 0)) { 148 | init_thread_allocator(); 149 | assert((a = pthread_getspecific(key))); 150 | } 151 | #endif 152 | 153 | return a; 154 | } 155 | 156 | void* allocator_alloc(size_t size) 157 | { 158 | allocator *a = get_thread_allocator(); 159 | 160 | // cache line alignment 161 | size = (size + 63) & (~((size_t)63)); 162 | 163 | int success; 164 | void *ptr = block_alloc(a->curr, size, &success); 165 | if (unlikely(success == 0)) { 166 | block *new = new_block(a->meta_curr); 167 | if (unlikely(new == 0)) { 168 | block *meta = new_meta_block(); 169 | meta->next = a->meta_curr; 170 | a->meta_curr = meta; 171 | new = new_block(a->meta_curr); 172 | assert(new); 173 | } 174 | new->next = a->curr; 175 | a->curr = new; 176 | ptr = block_alloc(a->curr, size, &success); 177 | } 178 | assert(success); 179 | return ptr; 180 | } 181 | 182 | void* allocator_alloc_small(size_t size) 183 | { 184 | allocator *a = get_thread_allocator(); 185 | 186 | // 8 bytes alignment 187 | size = (size + 8) & (~((size_t)8)); 188 | 189 | int success; 190 | void *ptr = block_alloc(a->small_curr, size, &success); 191 | if (unlikely(success == 0)) { 192 | block *new = new_block(a->meta_curr); 193 | if (unlikely(new == 0)) { 194 | block *meta = new_meta_block(); 195 | meta->next = a->meta_curr; 196 | a->meta_curr = meta; 197 | new = new_block(a->meta_curr); 198 | assert(new); 199 | } 200 | new->next = a->small_curr; 201 | a->small_curr = new; 202 | ptr = block_alloc(a->small_curr, size, &success); 203 | } 204 | assert(success); 205 | return ptr; 206 | } 207 | 208 | void allocator_free(void *ptr) 209 | { 210 | // do nothing 211 | (void)ptr; 212 | } 213 | -------------------------------------------------------------------------------- /palm/allocator.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-11-23 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _allocator_h_ 8 | #define _allocator_h_ 9 | 10 | // this allocator is used to allocate small size memory(typically <=4kb) in a fast way, 11 | // an allocator is obtained by one thread only so no thread synchronization is needed, 12 | // the memory allocated is always vaild until the allocator itself is destroyed 13 | 14 | #define meta_block_size ((size_t)4 << 10) // 4kb 15 | // TODO: linux huge page advise? 16 | #define block_size ((size_t)4 << 20) // 4mb 17 | 18 | typedef struct block 19 | { 20 | void *buffer; 21 | size_t now; 22 | size_t tot; 23 | 24 | struct block *next; 25 | }block; 26 | 27 | typedef struct allocator 28 | { 29 | block *meta_curr; 30 | block *curr; 31 | block *small_curr; 32 | }allocator; 33 | 34 | void init_allocator(); 35 | void* allocator_alloc(size_t size); 36 | void* allocator_alloc_small(size_t size); 37 | void allocator_free(void *ptr); 38 | 39 | #endif /* _allocator_h_ */ -------------------------------------------------------------------------------- /palm/bounded_queue.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-09-13 4 | * license: BSD-3 5 | **/ 6 | 7 | #define _POSIX_C_SOURCE 200112L 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | #include "bounded_queue.h" 14 | 15 | bounded_queue* new_bounded_queue(int total) 16 | { 17 | if (total <= 0) total = 1; 18 | // we don't use a big queue to avoid too much batch memory 19 | if (total >= 8) total = 8; 20 | 21 | void *q_buf; 22 | assert(posix_memalign(&q_buf, 64, sizeof(bounded_queue)) == 0); 23 | bounded_queue *q = (bounded_queue *)q_buf; 24 | 25 | q->total = total; 26 | q->head = 0; 27 | q->tail = 0; 28 | q->size = 0; 29 | q->clear = 0; 30 | 31 | void *array; 32 | assert(posix_memalign(&array, 64, sizeof(void *) * q->total) == 0); 33 | memset(array, 0, sizeof(void *) * q->total); 34 | q->array = (void **)array; 35 | 36 | assert(pthread_mutex_init(&q->mutex, 0) == 0); 37 | assert(pthread_cond_init(&q->cond, 0) == 0); 38 | 39 | return q; 40 | } 41 | 42 | void free_bounded_queue(bounded_queue *q) 43 | { 44 | pthread_mutex_destroy(&q->mutex); 45 | pthread_cond_destroy(&q->cond); 46 | 47 | free((void *)q->array); 48 | 49 | free((void *)q); 50 | } 51 | 52 | void bounded_queue_wait_empty(bounded_queue *q) 53 | { 54 | pthread_mutex_lock(&q->mutex); 55 | 56 | // wait until all the queue elements have been processed 57 | while (q->size) 58 | pthread_cond_wait(&q->cond, &q->mutex); 59 | 60 | pthread_mutex_unlock(&q->mutex); 61 | } 62 | 63 | void bounded_queue_clear(bounded_queue *q) 64 | { 65 | pthread_mutex_lock(&q->mutex); 66 | 67 | // wait until all the queue elements have been processed 68 | while (q->size) 69 | pthread_cond_wait(&q->cond, &q->mutex); 70 | 71 | q->clear = 1; 72 | pthread_cond_broadcast(&q->cond); 73 | 74 | pthread_mutex_unlock(&q->mutex); 75 | } 76 | 77 | void bounded_queue_enqueue(bounded_queue *q, void *element) 78 | { 79 | assert(element); 80 | 81 | pthread_mutex_lock(&q->mutex); 82 | 83 | while (q->array[q->tail] && !q->clear) 84 | pthread_cond_wait(&q->cond, &q->mutex); 85 | 86 | if (!q->clear) { 87 | q->array[q->tail++] = element; 88 | ++q->size; 89 | if (q->tail == q->total) 90 | q->tail = 0; 91 | // wake up all the workers 92 | pthread_cond_broadcast(&q->cond); 93 | } 94 | 95 | pthread_mutex_unlock(&q->mutex); 96 | } 97 | 98 | // return the element at `idx` but don't proceed `q->head` 99 | void* bounded_queue_get_at(bounded_queue *q, int *idx) 100 | { 101 | pthread_mutex_lock(&q->mutex); 102 | 103 | while (!q->array[*idx] && !q->clear) 104 | pthread_cond_wait(&q->cond, &q->mutex); 105 | 106 | void *r; 107 | if (!q->clear) { 108 | r = q->array[*idx]; 109 | if (++(*idx) == q->total) 110 | *idx = 0; 111 | } else { 112 | r = 0; 113 | } 114 | 115 | pthread_mutex_unlock(&q->mutex); 116 | return r; 117 | } 118 | 119 | void bounded_queue_dequeue(bounded_queue *q) 120 | { 121 | pthread_mutex_lock(&q->mutex); 122 | 123 | assert(q->array[q->head]); 124 | 125 | q->array[q->head] = 0; 126 | --q->size; 127 | 128 | if (++q->head == q->total) 129 | q->head = 0; 130 | 131 | pthread_cond_signal(&q->cond); 132 | 133 | pthread_mutex_unlock(&q->mutex); 134 | } 135 | -------------------------------------------------------------------------------- /palm/bounded_queue.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-09-13 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _bounded_queue_h_ 8 | #define _bounded_queue_h_ 9 | 10 | #include 11 | 12 | typedef struct bounded_queue 13 | { 14 | int total; 15 | int head; // point to the slot which to enqueue 16 | int tail; // point to the slot which to dequeue 17 | int size; 18 | int clear; 19 | 20 | void **array; 21 | 22 | pthread_mutex_t mutex; 23 | 24 | pthread_cond_t cond; 25 | }bounded_queue; 26 | 27 | bounded_queue* new_bounded_queue(int total); 28 | void free_bounded_queue(bounded_queue *q); 29 | void bounded_queue_wait_empty(bounded_queue *q); 30 | void bounded_queue_clear(bounded_queue *q); 31 | void bounded_queue_enqueue(bounded_queue *q, void *element); 32 | void* bounded_queue_get_at(bounded_queue *q, int *idx); 33 | void bounded_queue_dequeue(bounded_queue *q); 34 | 35 | #endif /* _bounded_queue_h_ */ -------------------------------------------------------------------------------- /palm/metric.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-09-17 4 | * license: BSD-3 5 | **/ 6 | 7 | #define _POSIX_C_SOURCE 200112L 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "metric.h" 15 | 16 | struct clock* new_clock() 17 | { 18 | struct clock *c = (struct clock *)malloc(sizeof(struct clock)); 19 | c->cpu = 0; 20 | c->tot = 0; 21 | return c; 22 | } 23 | 24 | struct clock clock_get() 25 | { 26 | struct clock c; 27 | struct timespec ts; 28 | 29 | clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); 30 | c.cpu = (((unsigned long long)ts.tv_sec) * 1e9 + ts.tv_nsec) / 1000; 31 | 32 | clock_gettime(CLOCK_REALTIME, &ts); 33 | c.tot = (((unsigned long long)ts.tv_sec) * 1e9 + ts.tv_nsec) / 1000; 34 | return c; 35 | } 36 | 37 | static struct clock clock_get_duration(struct clock *old, struct clock* new) 38 | { 39 | struct clock c; 40 | c.cpu = new->cpu - old->cpu; 41 | c.tot = new->tot - old->tot; 42 | return c; 43 | } 44 | 45 | static void clock_update(struct clock *c, struct clock *d) 46 | { 47 | c->cpu += d->cpu; 48 | c->tot += d->tot; 49 | } 50 | 51 | static void clock_reset(struct clock *c) 52 | { 53 | c->cpu = 0; 54 | c->tot = 0; 55 | } 56 | 57 | #define max_metric_entry 32 58 | 59 | typedef struct metric 60 | { 61 | char *name[max_metric_entry]; 62 | int len; 63 | map_t hashmap; 64 | }metric; 65 | 66 | static metric *metrics; 67 | static int metric_num; 68 | 69 | void init_metric(int num) 70 | { 71 | if (num <= 0) num = 1; 72 | 73 | metric_num = num; 74 | metrics = (metric *)malloc(sizeof(metric) * metric_num); 75 | 76 | for (int i = 0; i < metric_num; ++i) { 77 | metrics[i].len = 0; 78 | metrics[i].hashmap = hashmap_new(); 79 | } 80 | } 81 | 82 | static metric* _get_metric(int id) 83 | { 84 | return &metrics[id]; 85 | } 86 | 87 | void register_metric(int id, const char *name, struct clock *c) 88 | { 89 | metric *m = _get_metric(id); 90 | if (m->len == max_metric_entry) return ; 91 | m->name[m->len++] = (char *)name; 92 | hashmap_put(m->hashmap, (char *)name, (void *)c); 93 | } 94 | 95 | void update_metric(int id, const char *name, struct clock *before) 96 | { 97 | struct clock now = clock_get(); 98 | struct clock duration = clock_get_duration(before, &now); 99 | metric *m = _get_metric(id); 100 | struct clock *old; 101 | hashmap_get(m->hashmap, (char *)name, (void **)&old); 102 | clock_update(old, &duration); 103 | *before = now; 104 | } 105 | 106 | void show_metric() 107 | { 108 | metric *m = _get_metric(0); 109 | struct clock clocks[m->len]; 110 | for (int i = 0; i < m->len; ++i) 111 | clock_reset(&clocks[i]); 112 | 113 | for (int i = 0; i < m->len; ++i) { 114 | for (int j = 0; j < metric_num; ++j) { 115 | metric *m = _get_metric(j); 116 | struct clock *c; 117 | hashmap_get(m->hashmap, (char *)m->name[i], (void **)&c); 118 | clock_update(&clocks[i], c); 119 | clock_reset(c); 120 | } 121 | } 122 | 123 | struct clock all; 124 | clock_reset(&all); 125 | for (int i = 0; i < m->len; ++i) { 126 | clocks[i].cpu /= metric_num; 127 | clocks[i].tot /= metric_num; 128 | clock_update(&all, &clocks[i]); 129 | } 130 | 131 | printf("cpu: %llu us total: %llu us\n", all.cpu, all.tot); 132 | for (int i = 0; i < m->len; ++i) { 133 | printf("%-24s: cpu: %5.2f %% tot: %5.2f %%\n", m->name[i], 134 | (float)clocks[i].cpu / all.cpu * 100, 135 | (float)clocks[i].tot / all.tot * 100); 136 | } 137 | } 138 | 139 | void free_metric() 140 | { 141 | for (int i = 0; i < metric_num; ++i) { 142 | metric *m = _get_metric(i); 143 | for (int j = 0; j < m->len; ++j) { 144 | void *r; 145 | hashmap_get(m->hashmap, (char *)m->name[j], &r); 146 | free(r); 147 | } 148 | hashmap_free(m->hashmap); 149 | } 150 | free((void *)metrics); 151 | } 152 | -------------------------------------------------------------------------------- /palm/metric.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-09-17 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _metric_h_ 8 | #define _metric_h_ 9 | 10 | struct clock 11 | { 12 | unsigned long long cpu; 13 | unsigned long long tot; 14 | }; 15 | 16 | struct clock* new_clock(); 17 | struct clock clock_get(); 18 | 19 | void init_metric(int num); 20 | void register_metric(int id, const char *name, struct clock *c); 21 | void update_metric(int id, const char *name, struct clock *before); 22 | void show_metric(); 23 | void free_metric(); 24 | 25 | #endif /* _metric_h_ */ 26 | -------------------------------------------------------------------------------- /palm/node.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-08-19 4 | * license: BSD-3 5 | **/ 6 | 7 | /** 8 | * B+ tree node is k-v storage unit & internal index unit 9 | * 10 | * layout of a node in bytes: 11 | * type level sopt prefix id keys offset next node first child 12 | * | 1 | 1 | 1 | 1 | 4 | 4 | 4 | 8 | 8 | 13 | * | prefix data | kv paris | 14 | * | kv pairs | 15 | * | kv pairs | 16 | * | kv pairs | index | 17 | * 18 | * 19 | * layout of kv pair: 20 | * key len ptr 21 | * | 1 | key | 8 | 22 | * 23 | * if node is a leaf node, ptr represents the pointer to the value or value itself 24 | * if node is a internal node, ptr represents the pointer to the child nodes 25 | * 26 | **/ 27 | 28 | #ifndef _node_h_ 29 | #define _node_h_ 30 | 31 | #include 32 | 33 | // node type 34 | #define Root (1 << 0) 35 | #define Branch (1 << 1) 36 | #define Leaf (1 << 2) 37 | #define Blink (1 << 3) // blink tree 38 | #define Batch (1 << 4) 39 | 40 | // op type 41 | #define Read 0 42 | #define Write 1 43 | 44 | // do not fucking change it 45 | typedef uint64_t val_t; 46 | #define value_bytes sizeof(val_t) 47 | 48 | #define set_val(ptr, val) ((*(val_t *)(ptr)) = (val)) 49 | 50 | // you can change uint8_t to uint16_t so that bigger keys are supported, 51 | // but key length byte will take more space, also you need to update 52 | // `node_min_size` below to at least 128kb 53 | typedef uint8_t len_t; 54 | #define key_byte sizeof(len_t) 55 | 56 | #define max_key_size ((uint32_t)((len_t)~((uint64_t)0))) 57 | 58 | // you can change uint16_t to uint32_t so that bigger size nodes are supported, 59 | // but index will take more space 60 | typedef uint16_t index_t; 61 | #define index_byte sizeof(index_t) 62 | 63 | #define node_min_size (((uint32_t)1) << 12) // 4kb 64 | #define node_max_size (((uint32_t)1) << 16) // 64kb 65 | // if you set `index_t` to uint32_t, 66 | // the node_max_size can be up to 4gb 67 | 68 | typedef struct __attribute__ ((packed)) node 69 | { 70 | uint32_t type:8; // Root or Branch or Leaf 71 | uint32_t level:8; // level this node in 72 | uint32_t sopt:8; // for sequential insertion optimization, only for level 0 73 | uint32_t pre:8; // prefix length, only used in level 0 74 | uint32_t id; // id of this node, mainly for debug 75 | uint32_t keys; // number of keys 76 | uint32_t off; // current data offset 77 | struct node *next; // pointer to the right child 78 | struct node *first; // pointer to the first child if level > 0, otherwise NULL 79 | char data[0]; // to palce the prefix & the index & all the k-v pairs 80 | }node; 81 | 82 | void set_node_size(uint32_t size); 83 | uint32_t get_node_size(); 84 | void set_batch_size(uint32_t size); 85 | uint32_t get_batch_size(); 86 | int compare_key(const void *key1, uint32_t len1, const void *key2, uint32_t len2); 87 | 88 | node* new_node(uint8_t type, uint8_t level); 89 | void free_node(node *n); 90 | void free_btree_node(node *n); 91 | node* node_descend(node *n, const void *key, uint32_t len); 92 | int node_insert(node *n, const void *key, uint32_t len, const void *val); 93 | void* node_search(node *n, const void *key, uint32_t len); 94 | void node_split(node *old, node *new, char *pkey, uint32_t *plen); 95 | int node_not_include_key(node *n, const void *key, uint32_t len); 96 | int node_adjust_few(node *left, node *right, char *okey, uint32_t *olen, char *key, uint32_t *len); 97 | void node_adjust_many(node *new, node *left, node *right, char *okey, uint32_t *olen, char *key, uint32_t *len, 98 | char *nkey, uint32_t *nlen); 99 | int node_replace_key(node *n, const void *okey, uint32_t olen, const void *val, const void *key, uint32_t len); 100 | void node_prefetch(node *n); 101 | int node_is_after_key(node *n, const void *key, uint32_t len); 102 | int node_need_move_right(node *n, const void *key, uint32_t len); 103 | 104 | void set_node_offset(uint32_t offset); 105 | void node_init(node *n, uint8_t type, uint8_t level); 106 | void node_insert_fence(node *old, node *new, void *next, char *pkey, uint32_t *plen); 107 | 108 | /** 109 | * batch is a wrapper for node with some difference, key may be duplicated 110 | * 111 | * layout of kv pair in batch: 112 | * op key len ptr 113 | * | 1 | 1 | key | 8 | 114 | **/ 115 | // TODO: different size for node and batch, batch size can be much larger than node size 116 | typedef node batch; 117 | 118 | batch* new_batch(); 119 | void free_batch(batch *b); 120 | void batch_clear(batch *b); 121 | int batch_add_write(batch *b, const void *key, uint32_t len, const void *val); 122 | int batch_add_read(batch *b, const void *key, uint32_t len); 123 | void batch_read_at(batch *b, uint32_t idx, uint32_t *op, void **key, uint32_t *len, void **val); 124 | void* batch_get_value_at(batch *b, uint32_t idx); 125 | 126 | #define max_descend_depth 7 // should be enough levels for a b+ tree 127 | 128 | // the root to leaf descending path of one kv 129 | typedef struct path { 130 | uint32_t id; // id of the kv in the batch 131 | uint32_t depth; // node number in this path 132 | node *nodes[max_descend_depth]; // nodes[0] is root 133 | }path; 134 | 135 | void path_clear(path *p); 136 | void path_copy(const path *src, path *dst); 137 | void path_set_kv_id(path *p, uint32_t id); 138 | uint32_t path_get_kv_id(path *p); 139 | void path_push_node(path *p, node *n); 140 | node* path_get_node_at_level(path *p, uint32_t level); 141 | node* path_get_node_at_index(path *p, uint32_t idx); 142 | uint32_t path_get_level(path *p); 143 | 144 | #define fence_insert 0 145 | #define fence_replace 1 146 | 147 | typedef struct fence 148 | { 149 | path *pth; // path that this fence belongs to 150 | node *ptr; // new node pointer 151 | uint32_t type; // fence type 152 | uint32_t len; // key length 153 | char key[16]; // key data, TODO: make it dynamic or change to `max_key_size` 154 | uint32_t olen; // old key length 155 | char okey[16];// old key data to replace, TODO: make it dynamic or change to `max_key_size` 156 | }fence; 157 | 158 | #define likely(x) (__builtin_expect(!!(x), 1)) 159 | #define unlikely(x) (__builtin_expect(!!(x), 0)) 160 | 161 | #ifdef Test 162 | 163 | uint32_t node_get_total_id(); 164 | void node_print(node *n, int detail); 165 | void batch_print(batch *b, int detail); 166 | void node_validate(node *n); 167 | void batch_validate(batch *n); 168 | void btree_node_validate(node *n); 169 | int node_try_compression(node *n, const void *key, uint32_t len); 170 | float node_get_coverage(node *n); 171 | 172 | #endif /* Test */ 173 | 174 | #endif /* _node_h_ */ 175 | -------------------------------------------------------------------------------- /palm/palm_tree.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-08-22 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | // TODO: remove this 12 | #include 13 | 14 | #include "palm_tree.h" 15 | #include "metric.h" 16 | #include "allocator.h" 17 | 18 | static const char *stage_descend = "descend to leaf"; 19 | static const char *stage_sync = "worker sync"; 20 | static const char *stage_redis = "redistribute work"; 21 | static const char *stage_leaves = "modify leaves"; 22 | static const char *stage_branches = "modify braches"; 23 | static const char *stage_root = "modify root"; 24 | 25 | static void do_palm_tree_execute(palm_tree *pt, batch *b, worker *w); 26 | 27 | typedef struct thread_arg 28 | { 29 | palm_tree *pt; 30 | worker *wrk; 31 | bounded_queue *que; 32 | }thread_arg; 33 | 34 | static thread_arg* new_thread_arg(palm_tree *pt, worker *w, bounded_queue *q) 35 | { 36 | thread_arg *j = (thread_arg *)malloc(sizeof(thread_arg)); 37 | j->pt = pt; 38 | j->wrk = w; 39 | j->que = q; 40 | 41 | return j; 42 | } 43 | 44 | static void free_thread_arg(thread_arg *j) 45 | { 46 | free((void *)j); 47 | } 48 | 49 | static void* run(void *arg) 50 | { 51 | thread_arg *j = (thread_arg *)arg; 52 | palm_tree *pt = j->pt; 53 | worker *w= j->wrk; 54 | bounded_queue *q = j->que; 55 | int q_idx = 0; 56 | 57 | while (1) { 58 | // TODO: optimization? 59 | batch *bth = bounded_queue_get_at(q, &q_idx); // q_idx will be updated in the queue 60 | 61 | if (likely(bth)) 62 | do_palm_tree_execute(pt, bth, w); 63 | else 64 | break; 65 | 66 | // let worker 0 do the dequeue 67 | if (w->id == 0) 68 | bounded_queue_dequeue(q); 69 | } 70 | 71 | free_thread_arg(j); 72 | return 0; 73 | } 74 | 75 | palm_tree* new_palm_tree(int worker_num, int queue_size) 76 | { 77 | #ifdef Allocator 78 | init_allocator(); 79 | #endif 80 | 81 | if (worker_num <= 0) worker_num = 1; 82 | 83 | init_metric(worker_num); 84 | 85 | for (int i = 0; i < worker_num; ++i) { 86 | register_metric(i, stage_descend, (void *)new_clock()); 87 | register_metric(i, stage_sync, (void *)new_clock()); 88 | register_metric(i, stage_redis, (void *)new_clock()); 89 | register_metric(i, stage_leaves, (void *)new_clock()); 90 | register_metric(i, stage_branches, (void *)new_clock()); 91 | register_metric(i, stage_root, (void *)new_clock()); 92 | } 93 | 94 | palm_tree *pt = (palm_tree *)malloc(sizeof(palm_tree)); 95 | pt->root = new_node(Root, 0); 96 | 97 | pt->worker_num = worker_num; 98 | pt->queue = new_bounded_queue(queue_size); 99 | pt->ids = (pthread_t *)malloc(sizeof(pthread_t) * pt->worker_num); 100 | pt->workers = (worker **)malloc(sizeof(worker *) * pt->worker_num); 101 | 102 | for (int i = 0; i < pt->worker_num; ++i) { 103 | pt->workers[i] = new_worker(i, pt->worker_num); 104 | if (i > 0) 105 | worker_link(pt->workers[i - 1], pt->workers[i]); 106 | } 107 | 108 | for (int i = 0; i < pt->worker_num; ++i) { 109 | thread_arg *arg = new_thread_arg(pt, pt->workers[i], pt->queue); 110 | assert(pthread_create(&pt->ids[i], 0, run, (void *)arg) == 0); 111 | } 112 | 113 | return pt; 114 | } 115 | 116 | void free_palm_tree(palm_tree *pt) 117 | { 118 | bounded_queue_clear(pt->queue); 119 | 120 | // collect all the child threads 121 | for (int i = 0; i < pt->worker_num; ++i) 122 | assert(pthread_join(pt->ids[i], 0) == 0); 123 | 124 | free_bounded_queue(pt->queue); 125 | 126 | for (int i = 0; i < pt->worker_num; ++i) 127 | free_worker(pt->workers[i]); 128 | 129 | free((void *)pt->workers); 130 | free((void *)pt->ids); 131 | 132 | // free the entire palm tree recursively 133 | free_btree_node(pt->root); 134 | 135 | free((void *)pt); 136 | 137 | free_metric(); 138 | } 139 | 140 | // finish all the task batch in the queue 141 | void palm_tree_flush(palm_tree *pt) 142 | { 143 | bounded_queue_wait_empty(pt->queue); 144 | } 145 | 146 | // put task batch in the queue 147 | void palm_tree_execute(palm_tree *pt, batch *b) 148 | { 149 | bounded_queue_enqueue(pt->queue, b); 150 | } 151 | 152 | #ifdef Test 153 | 154 | void palm_tree_validate(palm_tree *pt) 155 | { 156 | node *ptr = pt->root; 157 | uint32_t total_count = 0; 158 | float total_coverage = 0; 159 | float average_prefix = 0; 160 | while (ptr) { 161 | node *next = ptr->first; 162 | node *cur = ptr; 163 | uint32_t count = 0; 164 | float coverage = 0; 165 | uint32_t less50 = 0; 166 | uint32_t less60 = 0; 167 | uint32_t less70 = 0; 168 | uint32_t less80 = 0; 169 | while (cur) { 170 | btree_node_validate(cur); 171 | if (cur->level == 0) average_prefix += cur->pre; 172 | float c = node_get_coverage(cur); 173 | if (c < 0.5) ++less50; 174 | if (c < 0.6) ++less60; 175 | if (c < 0.7) ++less70; 176 | if (c < 0.8) ++less80; 177 | coverage += c; 178 | ++count; 179 | cur = cur->next; 180 | } 181 | printf("level %u: count: %-4u coverage: %.2f%% <50%%: %-4u <60%%: %-4u <70%%: %-4u <80%%: %-4u\n", 182 | ptr->level, count, (coverage * 100 / count), less50, less60, less70, less80); 183 | total_count += count; 184 | total_coverage += coverage; 185 | average_prefix /= count; 186 | ptr = next; 187 | } 188 | printf("average prefix length: %.2f\n", average_prefix); 189 | printf("total node count: %u\naverage coverage: %.2f%%\n", 190 | total_count, total_coverage * 100 / total_count); 191 | } 192 | 193 | #endif /* Test */ 194 | 195 | // only processed by worker 0 196 | static void handle_root_split(palm_tree *pt, worker *w) 197 | { 198 | uint32_t number; 199 | fence *fences; 200 | worker_get_fences(w, pt->root->level, &fences, &number); 201 | 202 | if (likely(number == 0)) return ; 203 | 204 | node *new_root = new_node(Root, pt->root->level + 1); 205 | // adjust old root type 206 | pt->root->type = pt->root->level == 0 ? Leaf : Branch; 207 | // set old root as new root's first child 208 | new_root->first = pt->root; 209 | 210 | for (uint32_t i = 0; i < number; ++i) { 211 | assert(node_insert(new_root, fences[i].key, fences[i].len, fences[i].ptr) == 1); 212 | } 213 | 214 | // replace old root 215 | pt->root = new_root; 216 | } 217 | 218 | #ifdef Lazy 219 | // descend to leaf node for key at `kidx`, using path at `pidx` 220 | static void descend_to_leaf_single(node *r, batch *b, worker *w, uint32_t kidx, uint32_t pidx) 221 | { 222 | uint32_t op; 223 | void *key; 224 | uint32_t len; 225 | void *val; 226 | // get kv info 227 | batch_read_at(b, kidx, &op, &key, &len, &val); 228 | 229 | path* p = worker_get_path_at(w, pidx); 230 | 231 | // loop until we reach level 0, push all the node to `p` along the way 232 | uint32_t level = r->level; 233 | node *cur = r; 234 | while (level--) { 235 | node *pre = cur; 236 | cur = node_descend(cur, key, len); 237 | // TODO: remove this 238 | assert(pre && pre->level); 239 | path_push_node(p, pre); 240 | } 241 | 242 | // TODO: remove this 243 | assert(cur && !cur->level); 244 | path_push_node(p, cur); 245 | } 246 | 247 | // this function is used for lazy descending, for key range [key_a, key_b], 248 | // if path a and path b falls into the same leaf node, all the keys between them 249 | // must fall into the same leaf node since they are sorted, 250 | // so we can avoid descending for each key, this is especially useful 251 | // when the palm tree is small or the key is close to each other 252 | // TODO: use loop to replace recursion 253 | static void descend_for_range(node *r, batch *b, worker *w, uint32_t kbeg, uint32_t kend, uint32_t pidx) 254 | { 255 | if ((kbeg + 1) >= kend) return ; 256 | 257 | path *lp = worker_get_path_at(w, pidx); 258 | path *rp = worker_get_path_at(w, pidx + kend - kbeg); 259 | if (path_get_node_at_level(lp, 0) != path_get_node_at_level(rp, 0)) { 260 | uint32_t kmid = (kbeg + kend) / 2; 261 | descend_to_leaf_single(r, b, w, kmid, pidx + kmid - kbeg); 262 | descend_for_range(r, b, w, kbeg, kmid, pidx); 263 | descend_for_range(r, b, w, kmid, kend, pidx + kmid - kbeg); 264 | } else { 265 | // all the keys in [kbeg, kend] fall into the same leaf node, 266 | // they must all have the exact same path, so copy path for keys in (kbeg, kend) 267 | uint32_t between = kend - kbeg - 1; 268 | for (uint32_t i = 1; i <= between; ++i) 269 | path_copy(lp, worker_get_path_at(w, pidx + i)); 270 | } 271 | } 272 | #endif /* Lazy */ 273 | 274 | // we descend to leaf node for each key in [beg, end), and store each key's descending path. 275 | // there are 3 descending policy to choose: 276 | // 1. lazy descend: like dfs, but with some amazing optimization, great for sequential insertion 277 | // 2. level descend: like bfs, good for cache locality 278 | // 3. zigzag descend: invented by myself, also good for cache locality 279 | static void descend_to_leaf(palm_tree *pt, batch *b, uint32_t beg, uint32_t end, worker *w) 280 | { 281 | if (beg == end) return ; 282 | 283 | #ifdef Lazy // lazy descend 284 | for (uint32_t i = beg; i < end; ++i) { 285 | path* p = worker_get_new_path(w); 286 | path_set_kv_id(p, i); 287 | } 288 | 289 | uint32_t pidx = 0; 290 | descend_to_leaf_single(pt->root, b, w, beg, pidx); 291 | if (--end > beg) { 292 | descend_to_leaf_single(pt->root, b, w, end, pidx + end - beg); 293 | descend_for_range(pt->root, b, w, beg, end, pidx); 294 | } 295 | #elif Level // level descend 296 | for (uint32_t i = beg; i < end; ++i) { 297 | path* p = worker_get_new_path(w); 298 | path_set_kv_id(p, i); 299 | path_push_node(p, pt->root); 300 | } 301 | 302 | for (uint32_t level = pt->root->level, idx = 0; level; --level, ++idx) { 303 | for (uint32_t i = beg, j = 0; i < end; ++i, ++j) { 304 | uint32_t op; 305 | void *key; 306 | uint32_t len; 307 | void *val; 308 | // get kv info 309 | batch_read_at(b, i, &op, &key, &len, &val); 310 | path *p = worker_get_path_at(w, j); 311 | node *cur = path_get_node_at_index(p, idx); 312 | cur = node_descend(cur, key, len); 313 | node_prefetch(cur); 314 | path_push_node(p, cur); 315 | } 316 | } 317 | #else // zigzag descend 318 | for (uint32_t i = beg; i < end; ++i) { 319 | path* p = worker_get_new_path(w); 320 | path_set_kv_id(p, i); 321 | path_push_node(p, pt->root); 322 | } 323 | 324 | // make sure that we process each key from left to right in level 0 for better cache locality 325 | // 1 means left to right, -1 means right to left 326 | int direction = ((pt->root->level % 2) == 0) ? 1 : -1; 327 | for (uint32_t level = pt->root->level, idx = 0; level; --level, ++idx, direction *= -1) { 328 | int i, e, j; 329 | if (direction == 1) 330 | i = beg, e = end, j = 0; 331 | else 332 | i = end - 1, e = (int)beg - 1, j = end - beg - 1; 333 | for (; i != e; i += direction, j += direction) { 334 | uint32_t op; 335 | void *key; 336 | uint32_t len; 337 | void *val; 338 | // get kv info 339 | batch_read_at(b, (uint32_t)i, &op, &key, &len, &val); 340 | path *p = worker_get_path_at(w, (uint32_t)j); 341 | node *cur = path_get_node_at_index(p, idx); 342 | cur = node_descend(cur, key, len); 343 | node_prefetch(cur); 344 | path_push_node(p, cur); 345 | } 346 | } 347 | #endif 348 | } 349 | 350 | // Reference: Parallel Architecture-Friendly Latch-Free Modifications to B+ Trees on Many-Core Processors 351 | // this is the entrance for all the write/read operations 352 | static void do_palm_tree_execute(palm_tree *pt, batch *b, worker *w) 353 | { 354 | worker_reset(w); 355 | 356 | // get root level here to prevent dead lock bug when promoting node modifications 357 | uint32_t root_level = pt->root->level; 358 | struct clock c = clock_get(); 359 | 360 | /* --- Stage 1 --- */ 361 | 362 | // calculate [beg, end) in a batch that current thread needs to process 363 | // it's possible that a worker has no key to process 364 | uint32_t part = (uint32_t)ceilf((float)b->keys / w->total); 365 | uint32_t beg = w->id * part > b->keys ? b->keys : w->id * part; 366 | uint32_t end = beg + part > b->keys ? b->keys : beg + part; 367 | 368 | // descend to leaf for each key that belongs to this worker in this batch 369 | descend_to_leaf(pt, b, beg, end, w); update_metric(w->id, stage_descend, &c); 370 | 371 | worker_sync(w, 0 /* level */, root_level); update_metric(w->id, stage_sync, &c); 372 | 373 | /* --- Stage 2 --- */ 374 | 375 | // try to find overlap nodes in previoud worker and next worker, 376 | // if there is a previous worker owns the same leaf node in current worker, 377 | // it will be processed by previous worker 378 | worker_redistribute_work(w, 0 /* level */); update_metric(w->id, stage_redis, &c); 379 | 380 | // now we process all the paths that belong to this worker 381 | worker_execute_on_leaf_nodes(w, b); update_metric(w->id, stage_leaves, &c); 382 | 383 | worker_sync(w, 1 /* level */, root_level); update_metric(w->id, stage_sync, &c); 384 | 385 | /* --- Stage 3 --- */ 386 | 387 | // fix the split level by level 388 | uint32_t level = 1; 389 | while (level <= root_level) { 390 | worker_redistribute_work(w, level); update_metric(w->id, stage_redis, &c); 391 | 392 | worker_execute_on_branch_nodes(w, level); update_metric(w->id, stage_branches, &c); 393 | 394 | ++level; 395 | 396 | worker_sync(w, level, root_level); update_metric(w->id, stage_sync, &c); 397 | 398 | // this is a very fucking smart and elegant optimization, we use `level` as an external 399 | // switch value, although `level` is on each thread's stack, it is globally equal for 400 | // workers in the same synchronization group at each stage, so it can be used to avoid 401 | // concurrency problems and save a lot of small but frequent memory allocation for 402 | // split information at the same time 403 | worker_switch_fence(w, level); 404 | } 405 | 406 | /* --- Stage 4 --- */ 407 | 408 | if (w->id == 0) { 409 | handle_root_split(pt, w); update_metric(w->id, stage_root, &c); 410 | } 411 | 412 | // do a global synchronization, not really needed, but just make things consistent 413 | worker_sync(w, level + 1, root_level); update_metric(w->id, stage_sync, &c); 414 | } 415 | -------------------------------------------------------------------------------- /palm/palm_tree.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-08-22 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _palm_tree_h_ 8 | #define _palm_tree_h_ 9 | 10 | #include 11 | 12 | #include "node.h" 13 | #include "worker.h" 14 | #include "bounded_queue.h" 15 | 16 | typedef struct palm_tree 17 | { 18 | node *root; 19 | 20 | int worker_num; 21 | int running; 22 | pthread_t *ids; 23 | 24 | bounded_queue *queue; 25 | 26 | worker **workers; 27 | 28 | }palm_tree; 29 | 30 | palm_tree* new_palm_tree(int worker_num, int queue_size); 31 | void free_palm_tree(palm_tree *pt); 32 | void palm_tree_flush(palm_tree *pt); 33 | void palm_tree_execute(palm_tree *pt, batch *b); 34 | 35 | #ifdef Test 36 | 37 | void palm_tree_validate(palm_tree *pt); 38 | 39 | #endif /* Test */ 40 | 41 | #endif /* _palm_tree_h_ */ 42 | -------------------------------------------------------------------------------- /palm/worker.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-08-24 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _worker_h_ 8 | #define _worker_h_ 9 | 10 | #include "node.h" 11 | 12 | #define channel_size max_descend_depth + 1 // +2 is better but we want `channel_size` to be 8 13 | 14 | /** 15 | * every thread has a worker, worker does write/read operations to b+ tree, 16 | * worker is chained together to form a double-linked list, 17 | * every worker only execute operation on paths that are not in conflict with other nodes, 18 | * if there is a conflict, worker with smaller id wins. 19 | * when descended to leaf nodes, a worker will communicate with prev worker and next worker 20 | * to determine which keys belongs to it. 21 | * 22 | * for example: 23 | * worker 1 descends to 3 leaf nodes, n1, n2, n3 24 | * worker 2 descends to 3 leaf nodes, n3, n4, n5 25 | * worker 3 descends to 3 leaf nodes, n5, n6, n7 26 | * 27 | * in this case, worker 1 process n1 n2, 28 | * worker 2 process n3 n4, 29 | * worker 3 process n5 n6 n7, 30 | **/ 31 | typedef struct worker 32 | { 33 | uint32_t id; // my id 34 | uint32_t total; // total workers 35 | 36 | uint32_t max_path; // maximum path number 37 | uint32_t cur_path; // current path number 38 | uint32_t beg_path; // begin path index this worker needs to process 39 | uint32_t tot_path; // total paths that this worker needs to process 40 | path *paths; // paths for all the keys this worker has 41 | 42 | uint32_t max_fence; // maximum number of new node this worker generates 43 | uint32_t cur_fence[2]; // current number of new node this worker generates 44 | uint32_t beg_fence; // begin fence index this worker needs to process 45 | uint32_t tot_fence; // total fences that this worker needs to process 46 | fence *fences[2]; // to place the fence key info, there are 2 groups for switch 47 | // each of them are sorted according to the key 48 | // this is a very cool optimization 49 | 50 | struct worker *prev; // previous worker with smaller id 51 | struct worker *next; // next worker with bigger id 52 | 53 | /* point to point synchronization */ 54 | node *last[channel_size]; // `last` & `first` both take up a cache line 55 | node *first[channel_size]; 56 | node *their_last; 57 | node *my_first; 58 | node *my_last; 59 | node *their_first; 60 | }worker; 61 | 62 | worker* new_worker(uint32_t id, uint32_t total); 63 | void free_worker(worker* w); 64 | void worker_link(worker *a, worker *b); 65 | path* worker_get_new_path(worker *w); 66 | path* worker_get_path_at(worker *w, uint32_t idx); 67 | void worker_update_fence(worker *w, uint32_t level, fence *f, uint32_t i); 68 | void worker_switch_fence(worker *w, uint32_t level); 69 | void worker_get_fences(worker *w, uint32_t level, fence **fences, uint32_t *number); 70 | void worker_redistribute_work(worker *w, uint32_t level); 71 | void worker_reset(worker *w); 72 | void worker_sync(worker *w, uint32_t level, uint32_t root_level); 73 | void worker_execute_on_leaf_nodes(worker *w, batch *b); 74 | void worker_execute_on_branch_nodes(worker *w, uint32_t level); 75 | 76 | #ifdef Test 77 | 78 | void worker_print_path_info(worker *w); 79 | void worker_print_fence_info(worker *w, uint32_t level); 80 | 81 | #endif 82 | 83 | #endif /* _worker_h_ */ -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #! /bin/sh 2 | 3 | make third_party 4 | # tree_name thread_num thread_key_num 5 | make one_test "DFLAGS+=-DTest -DDebug -DAllocator" && ./one_test $1 $2 $3 6 | 7 | # f_name n_size b_size t_num q_num k_num 8 | # make "DFLAGS+=-DTest -DAllocator -DPrefix" palm_tree_test && ./palm_tree_test 1 4096 4096 $2 8 $3 9 | # make "DFLAGS+=-DTest -DAllocator -DPrefix" palm_tree_test && ./palm_tree_test 1 65536 65536 $2 8 $3 10 | # make "DFLAGS+=-DTest -DAllocator -DPrefix -DBStar" palm_tree_test && ./palm_tree_test 1 4096 4096 2 8 1000000 11 | 12 | # f_name n_size t_num k_num 13 | # make "DFLAGS+=-DTest -DAllocator" blink_tree_test && ./blink_tree_test 1 4096 $2 $3 14 | # f_name t_num k_num 15 | # make mass_tree_test "DFLAGS+=-DAllocator" && ./mass_tree_test 1 $2 $3 16 | # f_name t_num k_num 17 | # make art_test "DFLAGS+=-DDebug" && ./art_test 1 $2 $3 18 | -------------------------------------------------------------------------------- /test/art_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-02-24 4 | * license: BSD-3 5 | **/ 6 | 7 | #define _XOPEN_SOURCE 500 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "../art/art.h" 19 | #ifdef Allocator 20 | #include "../palm/allocator.h" 21 | #endif // Allocator 22 | 23 | static long long mstime() 24 | { 25 | struct timeval tv; 26 | long long ust; 27 | 28 | gettimeofday(&tv, NULL); 29 | ust = ((long long)tv.tv_sec)*1000000; 30 | ust += tv.tv_usec; 31 | return ust / 1000; 32 | } 33 | 34 | struct thread_arg 35 | { 36 | adaptive_radix_tree *art; 37 | int file; 38 | int total_keys; 39 | int write; 40 | }; 41 | 42 | static void* run(void *arg) 43 | { 44 | struct thread_arg *ta = (struct thread_arg *)arg; 45 | adaptive_radix_tree *art = ta->art; 46 | int file = ta->file; 47 | int total_keys = ta->total_keys; 48 | int write = ta->write; 49 | 50 | char file_name[32]; 51 | memset(file_name, 0, 32); 52 | memcpy(file_name, "./data/", 7); 53 | char file_buf[32]; 54 | int file_len = snprintf(file_buf, 32, "%d", file); 55 | memcpy(file_name + 7, file_buf, file_len); 56 | 57 | int fd = open(file_name, O_RDONLY); 58 | assert(fd > 0); 59 | int block = 4 * 4096, curr = 0, ptr = 0, count = 0; 60 | char buf[block]; 61 | int flag = 1; 62 | long long before = mstime(); 63 | for (; (ptr = pread(fd, buf, block, curr)) > 0 && flag; curr += ptr) { 64 | while (--ptr && buf[ptr] != '\n' && buf[ptr] != '\0') buf[ptr] = '\0'; 65 | if (ptr) buf[ptr++] = '\0'; 66 | else break; 67 | for (int i = 0; i < ptr; ++i) { 68 | char *key = buf + i, *tmp = key; 69 | size_t len = 0; 70 | while (tmp[len] != '\0' && tmp[len] != '\n') 71 | ++len; 72 | tmp[len] = '\0'; 73 | i += len; 74 | 75 | if (count && (count % 1000000) == 0) 76 | printf("%d\n", count); 77 | 78 | if (count++ == total_keys) { 79 | flag = 0; 80 | break; 81 | } 82 | 83 | if (write) { 84 | void *slice; 85 | #ifdef Allocator 86 | slice = allocator_alloc_small(len); 87 | #else 88 | slice = malloc(len); 89 | #endif // Allocator 90 | memcpy(slice, key, len); 91 | assert(adaptive_radix_tree_put(art, slice, len) == 0); 92 | } else { 93 | void *value = adaptive_radix_tree_get(art, key, len); 94 | if (value == 0) { 95 | char buf[len + 1]; 96 | memcpy(buf, key, len); 97 | buf[len] = 0; 98 | printf("%s\n", buf); 99 | } 100 | assert(value); 101 | assert(memcmp(value, key, len) == 0); 102 | } 103 | } 104 | } 105 | 106 | long long after = mstime(); 107 | printf("\033[31mtotal: %d\033[0m\n\033[32mtime: %.4f s\033[0m\n", total_keys, (float)(after - before) / 1000); 108 | 109 | close(fd); 110 | 111 | return (void *)ta; 112 | } 113 | 114 | void test_adaptive_radix_tree(int file, int thread_number, int total_keys) 115 | { 116 | adaptive_radix_tree *art = new_adaptive_radix_tree(); 117 | 118 | int thread_keys = total_keys / thread_number; 119 | pthread_t ids[thread_number]; 120 | for (int i = 0; i < thread_number; ++i) { 121 | struct thread_arg *ta = malloc(sizeof(struct thread_arg)); 122 | ta->art = art; 123 | ta->file = i + file; 124 | ta->total_keys = thread_keys; 125 | ta->write = 1; 126 | assert(pthread_create(&ids[i], 0, run, (void *)ta) == 0); 127 | } 128 | 129 | for (int i = 0; i < thread_number; ++i) { 130 | struct thread_arg *ta; 131 | assert(pthread_join(ids[i], (void **)&ta) == 0); 132 | free(ta); 133 | } 134 | 135 | for (int i = 0; i < thread_number; ++i) { 136 | struct thread_arg *ta = malloc(sizeof(struct thread_arg)); 137 | ta->art = art; 138 | ta->file = i + file; 139 | ta->total_keys = thread_keys; 140 | ta->write = 0; 141 | assert(pthread_create(&ids[i], 0, run, (void *)ta) == 0); 142 | } 143 | 144 | for (int i = 0; i < thread_number; ++i) { 145 | struct thread_arg *ta; 146 | assert(pthread_join(ids[i], (void **)&ta) == 0); 147 | free(ta); 148 | } 149 | 150 | free_adaptive_radix_tree(art); 151 | } 152 | 153 | void test_adaptive_radix_tree_structure() 154 | { 155 | { 156 | adaptive_radix_tree *art = new_adaptive_radix_tree(); 157 | 158 | char *key1 = malloc(16); 159 | key1[0] = 5; 160 | key1++; 161 | memcpy((void *)key1, "hello\0", 6); 162 | char *key2 = malloc(16); 163 | key2[0] = 6; 164 | key2++; 165 | memcpy((void *)key2, "hello0\0", 7); 166 | char *key3 = malloc(16); 167 | key3[0] = 7; 168 | key3++; 169 | memcpy((void *)key3, "hello00", 8); 170 | 171 | adaptive_radix_tree_put(art, key1, 5, 0); 172 | adaptive_radix_tree_put(art, key2, 6, 0); 173 | adaptive_radix_tree_put(art, key3, 7, 0); 174 | 175 | assert(adaptive_radix_tree_get(art, key1, 5) == key1); 176 | assert(adaptive_radix_tree_get(art, key2, 6) == key2); 177 | assert(adaptive_radix_tree_get(art, key3, 7) == key3); 178 | 179 | free_adaptive_radix_tree(art); 180 | } 181 | { 182 | adaptive_radix_tree *art = new_adaptive_radix_tree(); 183 | 184 | char *key1 = malloc(16); 185 | key1[0] = 7; 186 | key1++; 187 | memcpy((void *)key1, "hello00\0", 8); 188 | char *key2 = malloc(16); 189 | key2[0] = 6; 190 | key2++; 191 | memcpy((void *)key2, "hello0\0", 7); 192 | char *key3 = malloc(16); 193 | key3[0] = 5; 194 | key3++; 195 | memcpy((void *)key3, "hello\0", 6); 196 | 197 | adaptive_radix_tree_put(art, key1, 7, 0); 198 | adaptive_radix_tree_put(art, key2, 6, 0); 199 | adaptive_radix_tree_put(art, key3, 5, 0); 200 | 201 | assert(adaptive_radix_tree_get(art, key1, 7) == key1); 202 | assert(adaptive_radix_tree_get(art, key2, 6) == key2); 203 | assert(adaptive_radix_tree_get(art, key3, 5) == key3); 204 | 205 | free_adaptive_radix_tree(art); 206 | } 207 | } 208 | 209 | int main(int argc, char **argv) 210 | { 211 | if (argc < 4) { 212 | printf("file_name thread_number key_number\n"); 213 | exit(1); 214 | } 215 | 216 | int file = atoi(argv[1]); 217 | (void)file; 218 | int thread_number = atoi(argv[2]); 219 | int total_keys = atoi(argv[3]); 220 | if (total_keys <= 0) total_keys = 1; 221 | if (thread_number <= 0) thread_number = 1; 222 | 223 | //test_adaptive_radix_tree(file, thread_number, total_keys); 224 | test_adaptive_radix_tree_structure(); 225 | 226 | return 0; 227 | } 228 | -------------------------------------------------------------------------------- /test/blink_tree_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-11-20 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "../blink/blink_tree.h" 16 | 17 | static char *file_str; 18 | static int thread_number; 19 | static int total_keys; 20 | 21 | static long long mstime() 22 | { 23 | struct timeval tv; 24 | long long ust; 25 | 26 | gettimeofday(&tv, NULL); 27 | ust = ((long long)tv.tv_sec)*1000000; 28 | ust += tv.tv_usec; 29 | return ust / 1000; 30 | } 31 | 32 | void test_blink_tree() 33 | { 34 | blink_tree *bt = new_blink_tree(thread_number); 35 | 36 | char file_name[32]; 37 | memset(file_name, 0, 32); 38 | memcpy(file_name, "./data/", 7); 39 | memcpy(file_name + 7, file_str, strlen(file_str)); 40 | 41 | int fd = open(file_name, O_RDONLY); 42 | assert(fd > 0); 43 | int block = 4 * 4096, curr = 0, ptr = 0, count = 0; 44 | char buf[block]; 45 | int flag = 1; 46 | long long before = mstime(); 47 | for (; (ptr = pread(fd, buf, block, curr)) > 0 && flag; curr += ptr) { 48 | while (--ptr && buf[ptr] != '\n' && buf[ptr] != '\0') buf[ptr] = '\0'; 49 | if (ptr) buf[ptr++] = '\0'; 50 | else break; 51 | for (int i = 0; i < ptr; ++i) { 52 | char *key = buf + i, *tmp = key; 53 | uint32_t len = 0; 54 | while (tmp[len] != '\0' && tmp[len] != '\n') 55 | ++len; 56 | tmp[len] = '\0'; 57 | i += len; 58 | 59 | if (count && (count % 1000000) == 0) 60 | printf("%d\n", count); 61 | 62 | if (count++ == total_keys) { 63 | flag = 0; 64 | break; 65 | } 66 | 67 | // blink_tree_write(bt, key, len, (const void *)3190); 68 | blink_tree_schedule(bt, 1 /* is_write */, key, len, (const void *)3190); 69 | } 70 | } 71 | 72 | blink_tree_flush(bt); 73 | 74 | long long after = mstime(); 75 | printf("\033[31mtotal: %d\033[0m\n\033[32mput time: %.4f s\033[0m\n", total_keys, (float)(after - before) / 1000); 76 | 77 | curr = 0; 78 | flag = 1; 79 | count = 0; 80 | before = mstime(); 81 | for (; (ptr = pread(fd, buf, block, curr)) > 0 && flag; curr += ptr) { 82 | while (--ptr && buf[ptr] != '\n' && buf[ptr] != '\0') buf[ptr] = '\0'; 83 | if (ptr) buf[ptr++] = '\0'; 84 | else break; 85 | for (int i = 0; i < ptr; ++i) { 86 | char *key = buf + i, *tmp = key; 87 | uint32_t len = 0; 88 | while (tmp[len] != '\0' && tmp[len] != '\n') 89 | ++len; 90 | tmp[len] = '\0'; 91 | i += len; 92 | 93 | if (count && (count % 1000000) == 0) 94 | printf("%d\n", count); 95 | 96 | if (count++ == total_keys) { 97 | flag = 0; 98 | break; 99 | } 100 | 101 | // void *value; 102 | // assert(blink_tree_read(bt, key, len, &value)); 103 | // assert((uint64_t)value == 3190); 104 | blink_tree_schedule(bt, 0 /* is_write */, key, len, 0); 105 | } 106 | } 107 | 108 | blink_tree_flush(bt); 109 | 110 | after = mstime(); 111 | printf("\033[34mget time: %.4f s\033[0m\n", (float)(after - before) / 1000); 112 | 113 | close(fd); 114 | 115 | free_blink_tree(bt); 116 | } 117 | 118 | int main(int argc, char **argv) 119 | { 120 | if (argc < 5) { 121 | printf("file_name node_size thread_number key_number\n"); 122 | exit(1); 123 | } 124 | 125 | file_str = argv[1]; 126 | int node_size = atoi(argv[2]); 127 | thread_number = atoi(argv[3]); 128 | total_keys = atoi(argv[4]); 129 | if (total_keys <= 0) total_keys = 1; 130 | if (thread_number <= 0) thread_number = 1; 131 | 132 | set_node_size(node_size); 133 | 134 | test_blink_tree(); 135 | 136 | return 0; 137 | } 138 | -------------------------------------------------------------------------------- /test/mass_node_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-10-07 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | 14 | #include "../mass/node.h" 15 | 16 | #define magic_number 3190 17 | 18 | static long long mstime() 19 | { 20 | struct timeval tv; 21 | long long ust; 22 | 23 | gettimeofday(&tv, NULL); 24 | ust = ((long long)tv.tv_sec)*1000000; 25 | ust += tv.tv_usec; 26 | return ust / 1000; 27 | } 28 | 29 | void test_node_marco() 30 | { 31 | printf("test node marco\n"); 32 | 33 | node* n1 = new_node(Border); 34 | assert(is_border(n1->version)); 35 | 36 | assert(!is_locked(n1->version)); 37 | n1->version = set_lock(n1->version); 38 | assert(is_locked(n1->version)); 39 | n1->version = unset_lock(n1->version); 40 | assert(!is_locked(n1->version)); 41 | 42 | assert(!is_inserting(n1->version)); 43 | n1->version = set_insert(n1->version); 44 | assert(is_inserting(n1->version)); 45 | n1->version = unset_insert(n1->version); 46 | assert(!is_inserting(n1->version)); 47 | 48 | assert(!is_spliting(n1->version)); 49 | n1->version = set_split(n1->version); 50 | assert(is_spliting(n1->version)); 51 | n1->version = unset_split(n1->version); 52 | assert(!is_spliting(n1->version)); 53 | 54 | assert(!is_root(n1->version)); 55 | n1->version = set_root(n1->version); 56 | assert(is_root(n1->version)); 57 | n1->version = unset_root(n1->version); 58 | assert(!is_root(n1->version)); 59 | 60 | assert(!is_deleted(n1->version)); 61 | n1->version = set_delete(n1->version); 62 | assert(is_deleted(n1->version)); 63 | 64 | node *n2 = new_node(Interior); 65 | assert(is_interior(n2->version)); 66 | 67 | assert(get_vinsert(n2->version) == 0); 68 | for (int i = 0; i < 255; ++i) { 69 | n2->version = incr_vinsert(n2->version); 70 | assert((int)get_vinsert(n2->version) == (i+1)); 71 | } 72 | n2->version = incr_vinsert(n2->version); 73 | assert(n2->version == 0); 74 | 75 | assert(get_vsplit(n2->version) == 0); 76 | for (int i = 0; i < 65535; ++i) { 77 | n2->version = incr_vsplit(n2->version); 78 | assert((int)get_vsplit(n2->version) == (i+1)); 79 | } 80 | n2->version = incr_vsplit(n2->version); 81 | assert(n2->version == 0); 82 | 83 | free_node(n1); 84 | free_node(n2); 85 | } 86 | 87 | void test_node_utility_functions() 88 | { 89 | printf("test node utility functions\n"); 90 | 91 | node *n = new_node(Border); 92 | 93 | node_set_root(n); 94 | node_unset_root(n); 95 | 96 | node_set_version(n, (uint32_t)magic_number); 97 | assert(node_get_version(n) == magic_number); 98 | 99 | node_set_parent(n, (node *)magic_number); 100 | assert((int)node_get_parent(n) == magic_number); 101 | 102 | assert(node_get_next(n) == 0); 103 | 104 | free_node(n); 105 | } 106 | 107 | struct node_arg 108 | { 109 | node *n; 110 | int milliseconds; 111 | int *beg; 112 | int *end; 113 | int idx; 114 | int time; 115 | pthread_mutex_t *mutex; 116 | }; 117 | 118 | static void* _run(void *arg) 119 | { 120 | struct node_arg *na = (struct node_arg *)arg; 121 | 122 | long long now = mstime(); 123 | while ((mstime() - now) < na->milliseconds) { 124 | node_lock(na->n); 125 | // pthread_mutex_lock(na->mutex); 126 | *na->beg += na->idx; 127 | *na->end += na->idx; 128 | ++na->time; 129 | // pthread_mutex_unlock(na->mutex); 130 | node_unlock(na->n); 131 | } 132 | return 0; 133 | } 134 | 135 | void test_node_lock() 136 | { 137 | printf("test node lock\n"); 138 | 139 | node *n = new_node(Border); 140 | 141 | int threads = 4, beg = 0, end = 0, milliseconds = 3000; 142 | pthread_t ids[threads]; 143 | struct node_arg *args[threads]; 144 | pthread_mutex_t mutex; 145 | pthread_mutex_init(&mutex, 0); 146 | for (int i = 0; i < threads; ++i) { 147 | args[i] = (struct node_arg *)malloc(sizeof(struct node_arg)); 148 | args[i]->n = n; 149 | args[i]->milliseconds = milliseconds; 150 | args[i]->beg = &beg; 151 | args[i]->end = &end; 152 | args[i]->idx = i + 1; 153 | args[i]->time = 0; 154 | args[i]->mutex = &mutex; 155 | assert(pthread_create(&ids[i], NULL, _run, (void *)args[i]) == 0); 156 | } 157 | 158 | usleep((milliseconds + 5) * 1000); 159 | 160 | for (int i = 0; i < threads; ++i) 161 | assert(pthread_join(ids[i], 0) == 0); 162 | 163 | assert(pthread_mutex_destroy(&mutex) == 0); 164 | 165 | assert(beg == end); 166 | 167 | printf("%d\n", beg); 168 | for (int i = 0; i < threads; ++i) { 169 | beg -= args[i]->idx * args[i]->time; 170 | printf("%d\n", beg); 171 | } 172 | 173 | assert(beg == 0); 174 | 175 | for (int i = 0; i < threads; ++i) 176 | free(args[i]); 177 | 178 | free_node(n); 179 | } 180 | 181 | void test_border_node_insert_and_search() 182 | { 183 | printf("test border node insert and search\n"); 184 | 185 | node *n = new_node(Border); 186 | node_lock(n); 187 | 188 | srand(time(0)); 189 | // each key should be unique 190 | uint64_t key[15]; 191 | for (int i = 0; i < 15; ++i) { 192 | key[i] = ((uint64_t)rand() << 32) + i; 193 | uint32_t tmp = 0; 194 | assert((int)node_insert(n, &key[i], sizeof(uint64_t), &tmp, &key[i], (i % 2) ? 1 : 0 /* link */) == 1); 195 | } 196 | 197 | // each key is already inserted 198 | for (int i = 0; i < 15; ++i) { 199 | uint32_t tmp = 0; 200 | if ((i % 2)) 201 | assert((uint64_t *)node_insert(n, &key[i], sizeof(uint64_t), &tmp, &key[i], 0 /* link */) == &key[i]); 202 | else 203 | assert(node_insert(n, &key[i], sizeof(uint64_t), &tmp, &key[i], 0 /* link */) == 0); 204 | } 205 | 206 | assert(node_is_full(n)); 207 | node_unlock(n); 208 | 209 | node_print(n, 1); 210 | 211 | for (int i = 0; i < 15; ++i) { 212 | void *suffix; 213 | uint32_t tmp = 0; 214 | if ((i % 2)) { 215 | assert((uint64_t *)node_search(n, &key[i], sizeof(uint64_t), &tmp, &suffix) == &key[i]); 216 | } else { 217 | assert(node_search(n, &key[i], sizeof(uint64_t), &tmp, &suffix) == 0); 218 | assert((uint64_t *)suffix == &key[i]); 219 | } 220 | } 221 | 222 | free_node_raw(n); 223 | } 224 | 225 | void test_interior_node_insert_and_search() 226 | { 227 | printf("test interior node insert and search\n"); 228 | 229 | node *n = new_node(Interior); 230 | node_lock(n); 231 | 232 | srand(time(0)); 233 | uint64_t key[15]; 234 | 235 | node_set_first_child(n, (void *)-1); 236 | for (int i = 0; i < 15; ++i) { 237 | key[i] = i * 2 + 1; 238 | uint32_t tmp = 0; 239 | assert((int)node_insert(n, &key[i], sizeof(uint64_t), &tmp, &key[i], 1 /* link */) == 1); 240 | } 241 | 242 | node_unlock(n); 243 | 244 | node_print(n, 1); 245 | 246 | for (int i = 0; i < 15; ++i) { 247 | uint64_t k = i * 2; 248 | uint32_t tmp = 0; 249 | if (i == 0) 250 | assert((int)node_locate_child(n, &k, sizeof(uint64_t), &tmp) == -1); 251 | else 252 | assert((uint64_t *)node_locate_child(n, &k, sizeof(uint64_t), &tmp) == &key[i - 1]); 253 | } 254 | uint64_t k = 15 * 2; 255 | uint32_t tmp = 0; 256 | assert((uint64_t *)node_locate_child(n, &k, sizeof(uint64_t), &tmp) == &key[14]); 257 | 258 | free_node_raw(n); 259 | } 260 | 261 | void test_border_node_split() 262 | { 263 | printf("test border node split\n"); 264 | 265 | node *n = new_node(Border); 266 | node_lock(n); 267 | 268 | srand(time(0)); 269 | // each key should be unique 270 | uint64_t key[15]; 271 | for (int i = 0; i < 15; ++i) { 272 | key[i] = i * 2 + 1; 273 | uint32_t tmp = 0; 274 | assert((int)node_insert(n, &key[i], sizeof(uint64_t), &tmp, &key[i], (i % 2) ? 1 : 0 /* link */) == 1); 275 | } 276 | 277 | node_print(n, 1); 278 | 279 | uint64_t fence; 280 | node *n1 = node_split(n, &fence); 281 | 282 | node_unlock(n); 283 | node_unlock(n1); 284 | 285 | assert(fence == key[7]); 286 | 287 | for (int i = 0; i < 7; ++i) { 288 | uint32_t tmp = 0; 289 | void *suffix; 290 | if ((i % 2)) { 291 | assert((uint64_t *)node_search(n, &key[i], sizeof(uint64_t), &tmp, &suffix) == &key[i]); 292 | } else { 293 | assert(node_search(n, &key[i], sizeof(uint64_t), &tmp, &suffix) == 0); 294 | assert((uint64_t *)suffix == &key[i]); 295 | } 296 | tmp = 0; 297 | assert(node_search(n1, &key[i], sizeof(uint64_t), &tmp, &suffix) == 0); 298 | assert(!suffix); 299 | } 300 | 301 | for (int i = 7; i < 15; ++i) { 302 | uint32_t tmp = 0; 303 | void *suffix; 304 | if ((i % 2)) { 305 | assert((uint64_t *)node_search(n1, &key[i], sizeof(uint64_t), &tmp, &suffix) == &key[i]); 306 | } else { 307 | assert(node_search(n1, &key[i], sizeof(uint64_t), &tmp, &suffix) == 0); 308 | assert((uint64_t *)suffix == &key[i]); 309 | } 310 | tmp = 0; 311 | assert(node_search(n, &key[i], sizeof(uint64_t), &tmp, &suffix) == 0); 312 | assert(!suffix); 313 | } 314 | 315 | assert(node_get_next(n) == n1); 316 | 317 | node_print(n, 1); 318 | node_print(n1, 1); 319 | 320 | free_node_raw(n); 321 | free_node_raw(n1); 322 | } 323 | 324 | void test_interior_node_split() 325 | { 326 | printf("test interior node split\n"); 327 | 328 | node *n = new_node(Interior); 329 | node_lock(n); 330 | 331 | srand(time(0)); 332 | uint64_t key[15]; 333 | node *child[16]; 334 | child[0] = new_node(Border); 335 | node_set_parent(child[0], n); 336 | 337 | node_set_first_child(n, child[0]); 338 | for (int i = 0; i < 15; ++i) { 339 | key[i] = i * 2 + 1; 340 | child[i+1] = new_node(Border); 341 | node_set_parent(child[i+1], n); 342 | uint32_t tmp = 0; 343 | assert((int)node_insert(n, &key[i], sizeof(uint64_t), &tmp, child[i+1], 1 /* link */) == 1); 344 | } 345 | 346 | node_print(n, 1); 347 | 348 | uint64_t fence; 349 | node *n1 = node_split(n, &fence); 350 | 351 | node_unlock(n); 352 | node_unlock(n1); 353 | 354 | assert(fence == key[7]); 355 | 356 | for (int i = 0; i < 7; ++i) { 357 | uint64_t k = i * 2; 358 | uint32_t tmp = 0; 359 | assert(node_locate_child(n, &k, sizeof(uint64_t), &tmp) == child[i]); 360 | assert(node_get_parent(child[i]) == n); 361 | } 362 | uint64_t k = 7 * 2; 363 | uint32_t tmp = 0; 364 | assert(node_locate_child(n, &k, sizeof(uint64_t), &tmp) == child[7]); 365 | assert(node_get_parent(child[7]) == n); 366 | 367 | for (int i = 8; i < 15; ++i) { 368 | uint64_t k = i * 2; 369 | uint32_t tmp = 0; 370 | assert(node_locate_child(n1, &k, sizeof(uint64_t), &tmp) == child[i]); 371 | assert(node_get_parent(child[i]) == n1); 372 | } 373 | k = 15 * 2; 374 | tmp = 0; 375 | assert(node_locate_child(n1, &k, sizeof(uint64_t), &tmp) == child[15]); 376 | assert(node_get_parent(child[15]) == n1); 377 | 378 | node_print(n, 1); 379 | node_print(n1, 1); 380 | 381 | for (int i = 0; i < 16; ++i) 382 | free_node_raw(child[i]); 383 | free_node_raw(n); 384 | free_node_raw(n1); 385 | } 386 | 387 | int main() 388 | { 389 | test_node_marco(); 390 | test_node_utility_functions(); 391 | test_node_lock(); 392 | test_border_node_insert_and_search(); 393 | test_interior_node_insert_and_search(); 394 | test_border_node_split(); 395 | test_interior_node_split(); 396 | return 0; 397 | } 398 | -------------------------------------------------------------------------------- /test/mass_tree_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-12-04 4 | * license: BSD-3 5 | **/ 6 | 7 | #define _XOPEN_SOURCE 500 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #include "../mass/mass_tree.h" 19 | #ifdef Allocator 20 | #include "../palm/allocator.h" 21 | #endif // Allocator 22 | 23 | static long long mstime() 24 | { 25 | struct timeval tv; 26 | long long ust; 27 | 28 | gettimeofday(&tv, NULL); 29 | ust = ((long long)tv.tv_sec)*1000000; 30 | ust += tv.tv_usec; 31 | return ust / 1000; 32 | } 33 | 34 | struct thread_arg 35 | { 36 | mass_tree *mt; 37 | int file; 38 | int total_keys; 39 | int write; 40 | }; 41 | 42 | static void* run(void *arg) 43 | { 44 | struct thread_arg *ta = (struct thread_arg *)arg; 45 | mass_tree *mt = ta->mt; 46 | int file = ta->file; 47 | int total_keys = ta->total_keys; 48 | int write = ta->write; 49 | 50 | char file_name[32]; 51 | memset(file_name, 0, 32); 52 | memcpy(file_name, "./data/", 7); 53 | char file_buf[32]; 54 | int file_len = snprintf(file_buf, 32, "%d", file); 55 | memcpy(file_name + 7, file_buf, file_len); 56 | 57 | int fd = open(file_name, O_RDONLY); 58 | assert(fd > 0); 59 | int block = 4 * 4096, curr = 0, ptr = 0, count = 0; 60 | char buf[block]; 61 | int flag = 1; 62 | long long before = mstime(); 63 | for (; (ptr = pread(fd, buf, block, curr)) > 0 && flag; curr += ptr) { 64 | while (--ptr && buf[ptr] != '\n' && buf[ptr] != '\0') buf[ptr] = '\0'; 65 | if (ptr) buf[ptr++] = '\0'; 66 | else break; 67 | for (int i = 0; i < ptr; ++i) { 68 | char *key = buf + i, *tmp = key; 69 | uint32_t len = 0; 70 | while (tmp[len] != '\0' && tmp[len] != '\n') 71 | ++len; 72 | tmp[len] = '\0'; 73 | i += len; 74 | 75 | if (count && (count % 1000000) == 0) 76 | printf("%d\n", count); 77 | 78 | if (count++ == total_keys) { 79 | flag = 0; 80 | break; 81 | } 82 | 83 | if (write) { 84 | void *slice; 85 | #ifdef Allocator 86 | slice = allocator_alloc_small(len); 87 | #else 88 | slice = malloc(len); 89 | #endif // Allocator 90 | memcpy(slice, key, len); 91 | mass_tree_put(mt, slice, len, (const void *)3190); 92 | } else { 93 | void *value = mass_tree_get(mt, key, len); 94 | if (value == 0) { 95 | char buf[len + 1]; 96 | memcpy(buf, key, len); 97 | buf[len] = 0; 98 | printf("%s\n", buf); 99 | } 100 | assert(value); 101 | assert(memcmp(value, key, len) == 0); 102 | } 103 | } 104 | } 105 | 106 | long long after = mstime(); 107 | printf("\033[31mtotal: %d\033[0m\n\033[32mtime: %.4f s\033[0m\n", total_keys, (float)(after - before) / 1000); 108 | 109 | close(fd); 110 | 111 | return (void *)ta; 112 | } 113 | 114 | void test_mass_tree(int file, int thread_number, int total_keys) 115 | { 116 | mass_tree *mt = new_mass_tree(thread_number); 117 | 118 | int thread_keys = total_keys / thread_number; 119 | pthread_t ids[thread_number]; 120 | for (int i = 0; i < thread_number; ++i) { 121 | struct thread_arg *ta = malloc(sizeof(struct thread_arg)); 122 | ta->mt = mt; 123 | ta->file = i + file; 124 | ta->total_keys = thread_keys; 125 | ta->write = 1; 126 | assert(pthread_create(&ids[i], 0, run, (void *)ta) == 0); 127 | } 128 | 129 | for (int i = 0; i < thread_number; ++i) { 130 | struct thread_arg *ta; 131 | assert(pthread_join(ids[i], (void **)&ta) == 0); 132 | free(ta); 133 | } 134 | 135 | mass_tree_validate(mt); 136 | 137 | for (int i = 0; i < thread_number; ++i) { 138 | struct thread_arg *ta = malloc(sizeof(struct thread_arg)); 139 | ta->mt = mt; 140 | ta->file = i + file; 141 | ta->total_keys = thread_keys; 142 | ta->write = 0; 143 | assert(pthread_create(&ids[i], 0, run, (void *)ta) == 0); 144 | } 145 | 146 | for (int i = 0; i < thread_number; ++i) { 147 | struct thread_arg *ta; 148 | assert(pthread_join(ids[i], (void **)&ta) == 0); 149 | free(ta); 150 | } 151 | 152 | free_mass_tree(mt); 153 | } 154 | 155 | int main(int argc, char **argv) 156 | { 157 | if (argc < 4) { 158 | printf("file_name thread_number key_number\n"); 159 | exit(1); 160 | } 161 | 162 | int file = atoi(argv[1]); 163 | int thread_number = atoi(argv[2]); 164 | int total_keys = atoi(argv[3]); 165 | if (total_keys <= 0) total_keys = 1; 166 | if (thread_number <= 0) thread_number = 1; 167 | 168 | test_mass_tree(file, thread_number, total_keys); 169 | 170 | return 0; 171 | } 172 | -------------------------------------------------------------------------------- /test/one_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-03-14 4 | * license: BSD-3 5 | **/ 6 | 7 | /* one benchfuck to rule them all! */ 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | #include "../palm/palm_tree.h" 17 | #include "../palm/metric.h" 18 | #include "../blink/blink_tree.h" 19 | #include "../mass/mass_tree.h" 20 | #include "../art/art.h" 21 | #include "../util/rng.h" 22 | #ifdef Allocator 23 | #include "../palm/allocator.h" 24 | #endif 25 | 26 | static long long mstime() 27 | { 28 | struct timeval tv; 29 | long long ust; 30 | 31 | gettimeofday(&tv, NULL); 32 | ust = ((long long)tv.tv_sec)*1000000; 33 | ust += tv.tv_usec; 34 | return ust / 1000; 35 | } 36 | 37 | typedef enum tree_type 38 | { 39 | NONE = 0, 40 | PALM = 1, 41 | BLINK = 2, 42 | MASS = 3, 43 | ART = 4, 44 | }tree_type; 45 | 46 | struct thread_arg 47 | { 48 | tree_type tp; 49 | int id; 50 | int total; 51 | union { 52 | palm_tree *pt; 53 | blink_tree *bt; 54 | mass_tree *mt; 55 | adaptive_radix_tree *art; 56 | }tree; 57 | int keys; 58 | int is_put; 59 | }; 60 | 61 | static void* run(void *arg) 62 | { 63 | void* (*alloc)(size_t); 64 | #ifdef Allocator 65 | alloc = &allocator_alloc_small; 66 | #else 67 | alloc = &malloc; 68 | #endif // Allocator 69 | struct thread_arg *ta = (struct thread_arg *)arg; 70 | int keys = ta->keys; 71 | 72 | rng r; 73 | rng_init(&r, ta->id, ta->id + ta->total); 74 | 75 | long long before = mstime(); 76 | 77 | if (ta->is_put) { 78 | switch (ta->tp) { 79 | case PALM: { 80 | batch *batches[8 /* queue_size */ + 1]; 81 | for (int i = 0; i < 9; ++i) 82 | batches[i] = new_batch(); 83 | int idx = 0; 84 | batch *cb = batches[idx]; 85 | for (int i = 0; i < keys; ++i) { 86 | uint64_t key = rng_next(&r); 87 | if (batch_add_write(cb, &key, 8, (void *)3190) == -1) { 88 | palm_tree_execute(ta->tree.pt, cb); 89 | idx = idx == 8 ? 0 : idx + 1; 90 | cb = batches[idx]; 91 | batch_clear(cb); 92 | assert(batch_add_write(cb, &key, 8, (void *)3190) == 1); 93 | } 94 | } 95 | 96 | // finish remained work 97 | palm_tree_execute(ta->tree.pt, cb); 98 | palm_tree_flush(ta->tree.pt); 99 | 100 | for (int i = 0; i < 9; ++i) 101 | free_batch(batches[i]); 102 | 103 | show_metric(); 104 | } 105 | break; 106 | case BLINK: { 107 | for (int i = 0; i < keys; ++i) { 108 | uint64_t *key = (*alloc)(8); 109 | *key = rng_next(&r); 110 | blink_tree_write(ta->tree.bt, key, 8, (void *)3190); 111 | } 112 | } 113 | break; 114 | case MASS: { 115 | for (int i = 0; i < keys; ++i) { 116 | uint64_t *key = (*alloc)(8); 117 | *key = rng_next(&r); 118 | mass_tree_put(ta->tree.mt, key, 8, 0); 119 | } 120 | } 121 | break; 122 | case ART: { 123 | for (int i = 0; i < keys; ++i) { 124 | char *key = (*alloc)(16); 125 | key[0] = 8; 126 | *(uint64_t *)(key + 1)= rng_next(&r); 127 | assert(adaptive_radix_tree_put(ta->tree.art, (const void *)(key + 1), 8) == 0); 128 | } 129 | } 130 | break; 131 | default: 132 | assert(0); 133 | } 134 | } else { 135 | switch (ta->tp) { 136 | case PALM: { 137 | batch *batches[8 /* queue_size */ + 1]; 138 | for (int i = 0; i < 9; ++i) 139 | batches[i] = new_batch(); 140 | int idx = 0; 141 | batch *cb = batches[idx]; 142 | for (int i = 0; i < keys; ++i) { 143 | uint64_t key = rng_next(&r); 144 | if (batch_add_read(cb, &key, 8) == -1) { 145 | palm_tree_execute(ta->tree.pt, cb); 146 | idx = idx == 8 ? 0 : idx + 1; 147 | cb = batches[idx]; 148 | for (uint32_t j = 0; j < cb->keys; ++j) 149 | assert((uint64_t)batch_get_value_at(cb, j) == 3190); 150 | batch_clear(cb); 151 | assert(batch_add_read(cb, &key, 8) == 1); 152 | } 153 | } 154 | 155 | // finish remained work 156 | palm_tree_execute(ta->tree.pt, cb); 157 | palm_tree_flush(ta->tree.pt); 158 | for (int i = 0; i < 9; ++i) { 159 | cb = batches[i]; 160 | for (uint32_t j = 0; j < cb->keys; ++j) 161 | assert((uint64_t)batch_get_value_at(cb, j) == 3190); 162 | } 163 | for (int i = 0; i < 9; ++i) 164 | free_batch(batches[i]); 165 | } 166 | break; 167 | case BLINK: { 168 | for (int i = 0; i < keys; ++i) { 169 | uint64_t key = rng_next(&r); 170 | uint64_t val; 171 | assert(blink_tree_read(ta->tree.bt, &key, 8, (void **)&val) == 1); 172 | } 173 | } 174 | break; 175 | case MASS: { 176 | for (int i = 0; i < keys; ++i) { 177 | uint64_t key = rng_next(&r); 178 | void *val = mass_tree_get(ta->tree.mt, &key, 8); 179 | assert(val); 180 | } 181 | } 182 | break; 183 | case ART: { 184 | for (int i = 0; i < keys; ++i) { 185 | uint64_t key = rng_next(&r); 186 | void *val = adaptive_radix_tree_get(ta->tree.art, &key, 8); 187 | if (val == 0) { 188 | unsigned char *n = (unsigned char *)&key; 189 | for (int i = 0; i < 8; ++i) { 190 | printf("%d ", n[i]); 191 | } 192 | printf("\n"); 193 | } 194 | assert(val); 195 | } 196 | } 197 | break; 198 | default: 199 | assert(0); 200 | } 201 | } 202 | 203 | long long after = mstime(); 204 | printf("\033[31mtotal: %d\033[0m\n\033[32mtime: %.4f s\033[0m\n", keys, (float)(after - before) / 1000); 205 | (void)before; 206 | (void)after; 207 | 208 | return (void *)ta; 209 | } 210 | 211 | void benchfuck(tree_type tp, int thread_number, int thread_key_num) 212 | { 213 | struct thread_arg ta; 214 | ta.tp = tp; 215 | ta.total = thread_number; 216 | ta.keys = thread_key_num; 217 | if (tp == PALM) { 218 | ta.tree.pt = new_palm_tree(thread_number, 8 /* queue_size */); 219 | ta.keys *= thread_number; 220 | thread_number = 1; 221 | } 222 | if (tp == BLINK) { 223 | ta.tree.bt = new_blink_tree(thread_number); 224 | } 225 | if (tp == MASS) { 226 | ta.tree.mt = new_mass_tree(); 227 | } 228 | if (tp == ART) { 229 | ta.tree.art = new_adaptive_radix_tree(); 230 | } 231 | 232 | //unsigned int seed = time(0); 233 | //printf("seed:%u\n", seed); 234 | //srand(seed); 235 | printf("-- write start --\n"); 236 | 237 | pthread_t ids[thread_number]; 238 | for (int i = 0; i < thread_number; ++i) { 239 | struct thread_arg *t = malloc(sizeof(struct thread_arg)); 240 | *t = ta; 241 | t->is_put = 1; 242 | t->id = i + 1; 243 | assert(pthread_create(&ids[i], 0, run, (void *)t) == 0); 244 | } 245 | 246 | for (int i = 0; i < thread_number; ++i) { 247 | struct thread_arg *t; 248 | assert(pthread_join(ids[i], (void **)&t) == 0); 249 | free(t); 250 | } 251 | 252 | printf("-- read start --\n"); 253 | 254 | for (int i = 0; i < thread_number; ++i) { 255 | struct thread_arg *t = malloc(sizeof(struct thread_arg)); 256 | *t = ta; 257 | t->is_put = 0; 258 | t->id = i + 1; 259 | assert(pthread_create(&ids[i], 0, run, (void *)t) == 0); 260 | } 261 | 262 | for (int i = 0; i < thread_number; ++i) { 263 | struct thread_arg *t; 264 | assert(pthread_join(ids[i], (void **)&t) == 0); 265 | free(t); 266 | } 267 | } 268 | 269 | int main(int argc, char **argv) 270 | { 271 | if (argc < 4) { 272 | printf("tree_type thread_num thread_key_num\n"); 273 | return 0; 274 | } 275 | 276 | tree_type tp = NONE; 277 | if (strcasecmp(argv[1], "palm") == 0) 278 | tp = PALM; 279 | if (strcasecmp(argv[1], "blink") == 0) 280 | tp = BLINK; 281 | if (strcasecmp(argv[1], "mass") == 0) 282 | tp = MASS; 283 | if (strcasecmp(argv[1], "art") == 0) 284 | tp = ART; 285 | 286 | if (tp == NONE) { 287 | printf("tree_type(palm|blink|mass|art) thread_num thread_key_num\n"); 288 | return 0; 289 | } 290 | 291 | #ifdef Allocator 292 | init_allocator(); 293 | #endif 294 | 295 | int thread_num = atoi(argv[2]); 296 | if (thread_num < 1) thread_num = 1; 297 | int thread_key_num = atoi(argv[3]); 298 | if (thread_key_num < 1) thread_key_num = 1; 299 | 300 | benchfuck(tp, thread_num, thread_key_num); 301 | 302 | return 0; 303 | } 304 | -------------------------------------------------------------------------------- /test/palm_batch_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-08-24 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "../palm/node.h" 14 | 15 | #define key_buf(k, l) \ 16 | uint32_t len = l; \ 17 | char k[len]; \ 18 | for (uint32_t i = 0; i < len; ++i) \ 19 | k[i] = '0'; \ 20 | 21 | void test_set_batch_size() 22 | { 23 | printf("test set batch size\n"); 24 | 25 | set_batch_size(node_min_size + 1); 26 | assert(get_batch_size() == node_min_size); 27 | set_batch_size(node_max_size + 1); 28 | assert(get_batch_size() == node_max_size); 29 | } 30 | 31 | void test_new_batch() 32 | { 33 | printf("test new batch\n"); 34 | batch *b = new_batch(); 35 | 36 | assert(b->keys == 0); 37 | assert(b->off == 0); 38 | 39 | free_batch(b); 40 | } 41 | 42 | void test_batch_clear() 43 | { 44 | printf("test batch clear\n"); 45 | 46 | key_buf(key, 10); 47 | 48 | batch *b = new_batch(); 49 | 50 | assert(batch_add_write(b, key, len, (void *)0) == 1); 51 | 52 | batch_clear(b); 53 | 54 | assert(b->keys == 0); 55 | assert(b->off == 0); 56 | 57 | free_batch(b); 58 | } 59 | 60 | void test_batch_write() 61 | { 62 | printf("test batch write\n"); 63 | 64 | key_buf(key, 10); 65 | 66 | batch *b = new_batch(); 67 | 68 | // test sequential insert 69 | for (uint32_t i = 0; i < 10; ++i) { 70 | key[len - i - 1] = '1'; 71 | if ((i % 2) == 0) 72 | assert(batch_add_write(b, key, len, (void *)(uint64_t)i) == 1); 73 | else 74 | assert(batch_add_read(b, key, len) == 1); 75 | key[len - i - 1] = '0'; 76 | } 77 | 78 | key[3] = '1'; 79 | assert(batch_add_read(b, key, len) == 1); 80 | key[3] = '0'; 81 | 82 | assert(b->keys == 11); 83 | batch_validate(b); 84 | 85 | free_batch(b); 86 | 87 | b = new_batch(); 88 | 89 | // test random insert 90 | srand(time(NULL)); 91 | for (uint32_t i = 0; i < 50; ++i) { 92 | int idx = rand() % len; 93 | key[idx] = '1'; 94 | if ((i % 2) == 0) 95 | assert(batch_add_write(b, key, len, (void *)(uint64_t)i) == 1); 96 | else 97 | assert(batch_add_read(b, key, len) == 1); 98 | key[idx] = '0'; 99 | } 100 | 101 | assert(b->keys == 50); 102 | batch_validate(b); 103 | 104 | free_batch(b); 105 | } 106 | 107 | void test_batch_read() 108 | { 109 | printf("test batch read\n"); 110 | 111 | key_buf(key, 10); 112 | 113 | batch *b = new_batch(); 114 | 115 | // test sequential insert 116 | for (uint32_t i = 0; i < 10; ++i) { 117 | key[len - i - 1] = '1'; 118 | if ((i % 2) == 0) 119 | assert(batch_add_write(b, key, len, (void *)(uint64_t)i) == 1); 120 | else 121 | assert(batch_add_read(b, key, len) == 1); 122 | key[len - i - 1] = '0'; 123 | } 124 | 125 | uint32_t op; 126 | char *key2; 127 | uint32_t len2; 128 | void *val; 129 | for (uint32_t i = 0; i < 10; ++i) { 130 | key[len - i - 1] = '1'; 131 | batch_read_at(b, i, &op, (void **)&key2, &len2, &val); 132 | assert((i % 2 == 0) ? op == Write : op == Read); 133 | assert(compare_key(key, len, key2, len2) == 0); 134 | assert((i % 2 == 0) ? (*(val_t *)val == i) : (*(val_t *)val == 0)); 135 | key[len - i - 1] = '0'; 136 | } 137 | 138 | free_batch(b); 139 | } 140 | 141 | void test_print_batch() 142 | { 143 | printf("test print batch\n"); 144 | 145 | key_buf(key, 10); 146 | 147 | batch *b = new_batch(); 148 | // test random insert 149 | srand(time(NULL)); 150 | for (uint32_t i = 0; i < 20; ++i) { 151 | int idx = rand() % len; 152 | key[idx] = '1'; 153 | if ((i % 2) == 0) 154 | assert(batch_add_write(b, key, len, (void *)(uint64_t)i) == 1); 155 | else 156 | assert(batch_add_read(b, key, len) == 1); 157 | key[idx] = '0'; 158 | } 159 | 160 | batch_print(b, 0); 161 | batch_print(b, 1); 162 | 163 | free_batch(b); 164 | } 165 | 166 | int main() 167 | { 168 | test_set_batch_size(); 169 | test_new_batch(); 170 | test_batch_clear(); 171 | test_batch_write(); 172 | test_batch_read(); 173 | test_print_batch(); 174 | 175 | return 0; 176 | } 177 | -------------------------------------------------------------------------------- /test/palm_node_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-08-19 4 | * license: BSD-3 5 | **/ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "../palm/node.h" 14 | 15 | #define key_buf(k, l) \ 16 | uint32_t len = l; \ 17 | char k[len]; \ 18 | for (uint32_t i = 0; i < len; ++i) \ 19 | k[i] = '0'; \ 20 | 21 | void test_set_node_size() 22 | { 23 | printf("test set node size\n"); 24 | 25 | set_node_size(3190); 26 | assert(get_node_size() == node_min_size); 27 | set_node_size(1024 * 1024 * 2); 28 | assert(get_node_size() == node_max_size); 29 | set_node_size(10000); 30 | assert(get_node_size() == 8192); 31 | 32 | set_node_size(node_min_size); 33 | } 34 | 35 | void test_new_node() 36 | { 37 | printf("test new node\n"); 38 | node *n = new_node(Leaf, 1); 39 | 40 | assert(n->type == Leaf); 41 | assert(n->level == 1); 42 | assert(n->keys == 0); 43 | assert(n->pre == 0); 44 | assert(n->off == 0); 45 | assert(n->next == 0); 46 | assert(n->first == 0); 47 | 48 | free_node(n); 49 | } 50 | 51 | void test_node_insert() 52 | { 53 | printf("test node insert\n"); 54 | 55 | key_buf(key, 10); 56 | 57 | node *n = new_node(0, 0); 58 | // test sequential insert 59 | for (uint32_t i = 0; i < len; ++i) { 60 | key[len - i - 1] = '1'; 61 | assert(node_insert(n, key, len, (void *)(uint64_t)i) == 1); 62 | key[len - i - 1] = '0'; 63 | } 64 | 65 | assert(n->keys == 10); 66 | node_validate(n); 67 | 68 | free_node(n); 69 | 70 | n = new_node(0, 0); 71 | 72 | // test random insert 73 | srand(time(NULL)); 74 | for (uint32_t i = 0; i < 50; ++i) { 75 | key[rand() % len] = 'a' + (rand() % 26); 76 | assert(node_insert(n, key, len, (void *)(uint64_t)i) >= 0); 77 | } 78 | 79 | node_validate(n); 80 | 81 | free_node(n); 82 | } 83 | 84 | void test_node_insert_no_space() 85 | { 86 | printf("test node insert no space\n"); 87 | 88 | node *n = new_node(0, 0); 89 | 90 | // each key occupies `unit` bytes space 91 | uint32_t unit = 50 + key_byte + index_byte + value_bytes; 92 | // max keys a node can hold 93 | uint32_t max = (get_node_size() - (n->data - (char *)n)) / unit; 94 | key_buf(key, 50); 95 | char c = '0'; 96 | for (uint32_t i = 0, j = len - 1; i < max; ++i) { 97 | if (c++ > '9') { 98 | c = '1'; 99 | --j; 100 | } 101 | key[j] = c; 102 | assert(node_insert(n, key, len, (void *)(uint64_t)i) == 1); 103 | key[j] = '0'; 104 | } 105 | 106 | key[0] = 'a'; 107 | assert(node_insert(n, key, len, (void *)0) == -1); 108 | 109 | free_node(n); 110 | } 111 | 112 | void test_node_search() 113 | { 114 | printf("test node search\n"); 115 | 116 | node *n = new_node(Leaf, 0); 117 | 118 | key_buf(key, 50); 119 | 120 | srand(time(NULL)); 121 | for (uint32_t i = 0; i < 50; ++i) { 122 | key[rand() % len] = '0' + (rand() % 10); 123 | int r = node_insert(n, key, len, (void *)(uint64_t)i); 124 | if (r == 1) 125 | assert((val_t)node_search(n, key, len) == i); 126 | else if (r == -1) 127 | assert(0); 128 | } 129 | 130 | node_validate(n); 131 | 132 | free_node(n); 133 | } 134 | 135 | void test_node_descend() 136 | { 137 | printf("test node descend\n"); 138 | 139 | node *n = new_node(Branch, 1); 140 | 141 | key_buf(key, 10); 142 | 143 | for (uint32_t i = 0; i < len; ++i) { 144 | key[len - i - 1] = '2'; 145 | assert(node_insert(n, key, len, (void *)(uint64_t)(i+1))); 146 | key[len - i - 1] = '0'; 147 | } 148 | 149 | for (uint32_t i = 0; i < len; ++i) { 150 | key[len - i - 1] = '1'; 151 | assert((val_t)node_descend(n, key, len) == i); 152 | key[len - i - 1] = '0'; 153 | } 154 | 155 | key[0] = '3'; 156 | assert((val_t)node_descend(n, key, len) == len); 157 | 158 | free_node(n); 159 | } 160 | 161 | void test_node_split_level_0() 162 | { 163 | printf("test node split level 0\n"); 164 | 165 | node *old = new_node(Leaf, 0); // level 0 166 | 167 | key_buf(key, 10); 168 | 169 | for (uint32_t i = 0; i < len; ++i) { 170 | key[len - i - 1] = '1'; 171 | assert(node_insert(old, key, len, (void *)(uint64_t)(i+1))); 172 | key[len - i - 1] = '0'; 173 | } 174 | 175 | node *new = new_node(Leaf, 0); 176 | 177 | char buf[len]; 178 | uint32_t buf_len; 179 | node_split(old, new, buf, &buf_len); 180 | 181 | assert(old->keys == len / 2); 182 | assert(new->keys == len / 2); 183 | 184 | assert(old->next == new); 185 | assert(new->next == 0); 186 | 187 | node_validate(old); 188 | node_validate(new); 189 | 190 | node_print(old, 1); 191 | node_print(new, 1); 192 | 193 | free_node(old); 194 | free_node(new); 195 | } 196 | 197 | void test_node_split_level_1() 198 | { 199 | printf("test node split level 1\n"); 200 | 201 | node *old = new_node(Branch, 1); // level 1 202 | 203 | key_buf(key, 11); 204 | 205 | for (uint32_t i = 0; i < len; ++i) { 206 | key[len - i - 1] = '1'; 207 | assert(node_insert(old, key, len, (void *)(uint64_t)(i+1))); 208 | key[len - i - 1] = '0'; 209 | } 210 | 211 | node *new = new_node(Branch, 1); 212 | 213 | char buf[len]; 214 | uint32_t buf_len; 215 | node_split(old, new, buf, &buf_len); 216 | 217 | assert(old->keys == len / 2); 218 | assert(new->keys == len / 2); 219 | 220 | assert(old->next == new); 221 | assert(new->next == 0); 222 | 223 | assert((uint64_t)new->first == (len / 2 + 1)); 224 | 225 | node_validate(old); 226 | node_validate(new); 227 | 228 | free_node(old); 229 | free_node(new); 230 | } 231 | 232 | void test_print_node() 233 | { 234 | printf("test print node\n"); 235 | 236 | key_buf(key, 10); 237 | 238 | node *n = new_node(Leaf, 0); 239 | 240 | node_print(n, 1); 241 | 242 | for (uint32_t i = 0; i < len; ++i) { 243 | key[len - i - 1] = '1'; 244 | assert(node_insert(n, key, len, (void *)(uint64_t)i) == 1); 245 | key[len - i - 1] = '0'; 246 | } 247 | 248 | node_print(n, 0); 249 | 250 | node_print(n, 1); 251 | 252 | free_node(n); 253 | } 254 | 255 | void test_node_compression() 256 | { 257 | printf("test node compression\n"); 258 | 259 | key_buf(key, 20); 260 | 261 | node *n = new_node(Leaf, 0); 262 | 263 | for (uint32_t i = 0; i < 10; ++i) { 264 | key[len - i - 1] = '1'; 265 | assert(node_insert(n, key, len, (void *)(uint64_t)i) == 1); 266 | key[len - i - 1] = '0'; 267 | } 268 | 269 | node_print(n, 1); 270 | 271 | assert(node_try_compression(n, key, len) == 1); 272 | 273 | node_print(n, 1); 274 | 275 | assert(node_try_compression(n, key, len) == 0); 276 | 277 | free_node(n); 278 | } 279 | 280 | void test_node_adjust_few() 281 | { 282 | printf("test node adjust few\n"); 283 | 284 | key_buf(key, 50); 285 | 286 | node *left = new_node(Leaf, 0); 287 | 288 | srand(time(NULL)); 289 | for (uint32_t i = 0; i < 30; ++i) { 290 | key[len - (rand() % 30) - 1] = 'a' + (rand() % 26); 291 | node_insert(left, key, len, (void *)(uint64_t)i); 292 | } 293 | 294 | node *right = new_node(Leaf, 0); 295 | 296 | key[0] = '1'; 297 | for (uint32_t i = 0; i < 30; ++i) { 298 | key[len - (rand() % 30) - 1] = 'a' + (rand() % 26); 299 | node_insert(right, key, len, (void *)(uint64_t)i); 300 | } 301 | 302 | node_print(left, 1); 303 | node_print(right, 1); 304 | 305 | char okey[max_key_size], nkey[max_key_size]; 306 | uint32_t olen, nlen; 307 | assert(node_adjust_few(left, right, okey, &olen, nkey, &nlen)); 308 | 309 | okey[olen] = 0; 310 | nkey[nlen] = 0; 311 | 312 | node_print(left, 1); 313 | node_print(right, 1); 314 | 315 | printf("%s\n%s\n", okey, nkey); 316 | 317 | free_node(left); 318 | free_node(right); 319 | } 320 | 321 | void test_node_adjust_many() 322 | { 323 | printf("test node adjust many\n"); 324 | 325 | key_buf(key, 50); 326 | 327 | node *left = new_node(Leaf, 0); 328 | 329 | srand(time(NULL)); 330 | for (uint32_t i = 0; i < 30; ++i) { 331 | key[len - (rand() % 30) - 1] = 'a' + (rand() % 26); 332 | node_insert(left, key, len, (void *)(uint64_t)i); 333 | } 334 | 335 | node *right = new_node(Leaf, 0); 336 | 337 | key[0] = '1'; 338 | for (uint32_t i = 0; i < 30; ++i) { 339 | key[len - (rand() % 30) - 1] = 'a' + (rand() % 26); 340 | node_insert(right, key, len, (void *)(uint64_t)i); 341 | } 342 | 343 | node_print(left, 1); 344 | node_print(right, 1); 345 | 346 | node *new = new_node(Leaf, 0); 347 | 348 | char okey[max_key_size], nkey[max_key_size], fkey[max_key_size]; 349 | uint32_t olen, nlen, flen; 350 | node_adjust_many(new, left, right, okey, &olen, nkey, &nlen, fkey, &flen); 351 | 352 | okey[olen] = 0; 353 | nkey[nlen] = 0; 354 | fkey[flen] = 0; 355 | 356 | node_print(left, 1); 357 | node_print(new, 1); 358 | node_print(right, 1); 359 | 360 | printf("%s\n%s\n%s\n", okey, nkey, fkey); 361 | 362 | free_node(left); 363 | free_node(new); 364 | free_node(right); 365 | } 366 | 367 | void test_node_replace_key() 368 | { 369 | printf("test node replace key\n"); 370 | 371 | key_buf(key, 20); 372 | 373 | node *n = new_node(Branch, 1); 374 | 375 | srand(time(NULL)); 376 | for (uint32_t i = 0; i < 20; ++i) { 377 | key[i] = '1'; 378 | node_insert(n, key, len, (void *)(uint64_t)i); 379 | key[i] = '0'; 380 | } 381 | node_validate(n); 382 | 383 | key[10] = '1'; 384 | char nkey[max_key_size]; 385 | memcpy(nkey, key, len); 386 | nkey[12] = '1'; 387 | node_replace_key(n, key, len, (void *)(uint64_t)10, nkey, len); 388 | node_validate(n); 389 | 390 | key[12] = '1'; 391 | 392 | nkey[len] = '1'; 393 | node_replace_key(n, key, len, (void *)(uint64_t)10, nkey, len+1); 394 | node_validate(n); 395 | 396 | free_node(n); 397 | } 398 | 399 | int main() 400 | { 401 | test_set_node_size(); 402 | test_new_node(); 403 | test_node_insert(); 404 | test_node_insert_no_space(); 405 | test_node_search(); 406 | test_node_descend(); 407 | test_node_split_level_0(); 408 | test_node_split_level_1(); 409 | test_print_node(); 410 | test_node_compression(); 411 | test_node_adjust_few(); 412 | test_node_adjust_many(); 413 | test_node_replace_key(); 414 | 415 | return 0; 416 | } 417 | -------------------------------------------------------------------------------- /test/palm_tree_test.c: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2018-08-31 4 | * license: BSD-3 5 | **/ 6 | 7 | #define _XOPEN_SOURCE 500 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | #include "../palm/palm_tree.h" 18 | #include "../palm/metric.h" 19 | 20 | static const uint64_t value = 3190; 21 | static char *file_str; 22 | static int queue_size; 23 | static int thread_number; 24 | static int total_keys; 25 | 26 | static long long mstime() 27 | { 28 | struct timeval tv; 29 | long long ust; 30 | 31 | gettimeofday(&tv, NULL); 32 | ust = ((long long)tv.tv_sec)*1000000; 33 | ust += tv.tv_usec; 34 | return ust / 1000; 35 | } 36 | 37 | void test_palm_tree() 38 | { 39 | palm_tree *pt = new_palm_tree(thread_number, queue_size); 40 | batch *batches[queue_size + 1]; 41 | for (int i = 0; i < queue_size + 1; ++i) 42 | batches[i] = new_batch(); 43 | 44 | char file_name[32]; 45 | memset(file_name, 0, 32); 46 | memcpy(file_name, "./data/", 7); 47 | memcpy(file_name + 7, file_str, strlen(file_str)); 48 | 49 | int fd = open(file_name, O_RDONLY); 50 | assert(fd > 0); 51 | int block = 65536, curr = 0, ptr = 0, count = 0; 52 | char buf[block]; 53 | int flag = 1; 54 | long long before = mstime(); 55 | int idx = 0; 56 | batch *cb = batches[idx]; 57 | for (; (ptr = pread(fd, buf, block, curr)) > 0 && flag; curr += ptr) { 58 | while (--ptr && buf[ptr] != '\n' && buf[ptr] != '\0') buf[ptr] = '\0'; 59 | if (ptr) buf[ptr++] = '\0'; 60 | else break; 61 | for (int i = 0; i < ptr; ++i) { 62 | char *key = buf + i, *tmp = key; 63 | uint32_t len = 0; 64 | while (tmp[len] != '\0' && tmp[len] != '\n') 65 | ++len; 66 | tmp[len] = '\0'; 67 | i += len; 68 | 69 | if (count && (count % 1000000) == 0) 70 | printf("%d\n", count); 71 | 72 | if (count++ == total_keys) { 73 | flag = 0; 74 | break; 75 | } 76 | 77 | if (batch_add_write(cb, key, len, (void *)value) == -1) { 78 | palm_tree_execute(pt, cb); 79 | idx = idx == queue_size ? 0 : idx + 1; 80 | cb = batches[idx]; 81 | batch_clear(cb); 82 | assert(batch_add_write(cb, key, len, (void *)value) == 1); 83 | } 84 | } 85 | } 86 | 87 | // finish remained work 88 | palm_tree_execute(pt, cb); 89 | palm_tree_flush(pt); 90 | 91 | long long after = mstime(); 92 | printf("\033[31mtotal: %d\033[0m\n\033[32mput time: %.4f s\033[0m\n", total_keys, (float)(after - before) / 1000); 93 | show_metric(); 94 | 95 | for (int i = 0; i < queue_size + 1; ++i) 96 | batch_clear(batches[i]); 97 | 98 | // palm_tree_validate(pt); 99 | 100 | curr = 0; 101 | flag = 1; 102 | count = 0; 103 | before = mstime(); 104 | for (; (ptr = pread(fd, buf, block, curr)) > 0 && flag; curr += ptr) { 105 | while (--ptr && buf[ptr] != '\n' && buf[ptr] != '\0') buf[ptr] = '\0'; 106 | if (ptr) buf[ptr++] = '\0'; 107 | else break; 108 | for (int i = 0; i < ptr; ++i) { 109 | char *key = buf + i, *tmp = key; 110 | uint32_t len = 0; 111 | while (tmp[len] != '\0' && tmp[len] != '\n') 112 | ++len; 113 | tmp[len] = '\0'; 114 | i += len; 115 | 116 | if (count && (count % 1000000) == 0) 117 | printf("%d\n", count); 118 | 119 | if (count++ == total_keys) { 120 | flag = 0; 121 | break; 122 | } 123 | 124 | if (batch_add_read(cb, key, len) == -1) { 125 | palm_tree_execute(pt, cb); 126 | idx = idx == queue_size ? 0 : idx + 1; 127 | cb = batches[idx]; 128 | for (uint32_t j = 0; j < cb->keys; ++j) 129 | assert((uint64_t)batch_get_value_at(cb, j) == value); 130 | batch_clear(cb); 131 | assert(batch_add_read(cb, key, len) == 1); 132 | } 133 | } 134 | } 135 | 136 | // finish remained work 137 | palm_tree_execute(pt, cb); 138 | palm_tree_flush(pt); 139 | 140 | for (int i = 0; i < queue_size + 1; ++i) { 141 | cb = batches[i]; 142 | for (uint32_t j = 0; j < cb->keys; ++j) 143 | assert((uint64_t)batch_get_value_at(cb, j) == value); 144 | } 145 | 146 | after = mstime(); 147 | printf("\033[34mget time: %.4f s\033[0m\n", (float)(after - before) / 1000); 148 | 149 | close(fd); 150 | 151 | show_metric(); 152 | 153 | for (int i = 0; i < queue_size + 1; ++i) 154 | free_batch(batches[i]); 155 | 156 | free_palm_tree(pt); 157 | } 158 | 159 | int main(int argc, char **argv) 160 | { 161 | if (argc < 7) { 162 | printf("file_name node_size batch_size thread_number queue_size key_number\n"); 163 | exit(1); 164 | } 165 | 166 | file_str = argv[1]; 167 | int node_size = atoi(argv[2]); 168 | int batch_size = atoi(argv[3]); 169 | thread_number = atoi(argv[4]); 170 | queue_size = atoi(argv[5]); 171 | total_keys = atoi(argv[6]); 172 | if (total_keys <= 0) total_keys = 1; 173 | if (queue_size <= 0) queue_size = 1; 174 | if (thread_number <= 0) thread_number = 1; 175 | set_node_size(node_size); 176 | set_batch_size(batch_size); 177 | 178 | test_palm_tree(); 179 | 180 | return 0; 181 | } 182 | -------------------------------------------------------------------------------- /third_party/c_hashmap/License.md: -------------------------------------------------------------------------------- 1 |

2 | 4 | CC0 5 | 6 |
7 | To the extent possible under law, 8 | 10 | Pete Warden 11 | has waived all copyright and related or neighboring rights to 12 | C hashmap. 13 |

14 | -------------------------------------------------------------------------------- /third_party/c_hashmap/README: -------------------------------------------------------------------------------- 1 | This is a simple C hashmap, using strings for the keys. 2 | 3 | Originally based on code by Eliot Back at http://elliottback.com/wp/hashmap-implementation-in-c/ 4 | Reworked by Pete Warden - http://petewarden.typepad.com/searchbrowser/2010/01/c-hashmap.html 5 | 6 | main.c contains an example that tests the functionality of the hashmap module. 7 | To compile it, run something like this on your system: 8 | gcc main.c hashmap.c -o hashmaptest 9 | 10 | There are no restrictions on how you reuse this code. -------------------------------------------------------------------------------- /third_party/c_hashmap/hashmap.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Generic map implementation. 3 | */ 4 | #include "hashmap.h" 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #define INITIAL_SIZE (256) 11 | #define MAX_CHAIN_LENGTH (8) 12 | 13 | /* We need to keep keys and values */ 14 | typedef struct _hashmap_element{ 15 | char* key; 16 | int in_use; 17 | any_t data; 18 | } hashmap_element; 19 | 20 | /* A hashmap has some maximum size and current size, 21 | * as well as the data to hold. */ 22 | typedef struct _hashmap_map{ 23 | int table_size; 24 | int size; 25 | hashmap_element *data; 26 | } hashmap_map; 27 | 28 | /* 29 | * Return an empty hashmap, or NULL on failure. 30 | */ 31 | map_t hashmap_new() { 32 | hashmap_map* m = (hashmap_map*) malloc(sizeof(hashmap_map)); 33 | if(!m) goto err; 34 | 35 | m->data = (hashmap_element*) calloc(INITIAL_SIZE, sizeof(hashmap_element)); 36 | if(!m->data) goto err; 37 | 38 | m->table_size = INITIAL_SIZE; 39 | m->size = 0; 40 | 41 | return m; 42 | err: 43 | if (m) 44 | hashmap_free(m); 45 | return NULL; 46 | } 47 | 48 | /* The implementation here was originally done by Gary S. Brown. I have 49 | borrowed the tables directly, and made some minor changes to the 50 | crc32-function (including changing the interface). //ylo */ 51 | 52 | /* ============================================================= */ 53 | /* COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or */ 54 | /* code or tables extracted from it, as desired without restriction. */ 55 | /* */ 56 | /* First, the polynomial itself and its table of feedback terms. The */ 57 | /* polynomial is */ 58 | /* X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 */ 59 | /* */ 60 | /* Note that we take it "backwards" and put the highest-order term in */ 61 | /* the lowest-order bit. The X^32 term is "implied"; the LSB is the */ 62 | /* X^31 term, etc. The X^0 term (usually shown as "+1") results in */ 63 | /* the MSB being 1. */ 64 | /* */ 65 | /* Note that the usual hardware shift register implementation, which */ 66 | /* is what we're using (we're merely optimizing it by doing eight-bit */ 67 | /* chunks at a time) shifts bits into the lowest-order term. In our */ 68 | /* implementation, that means shifting towards the right. Why do we */ 69 | /* do it this way? Because the calculated CRC must be transmitted in */ 70 | /* order from highest-order term to lowest-order term. UARTs transmit */ 71 | /* characters in order from LSB to MSB. By storing the CRC this way, */ 72 | /* we hand it to the UART in the order low-byte to high-byte; the UART */ 73 | /* sends each low-bit to hight-bit; and the result is transmission bit */ 74 | /* by bit from highest- to lowest-order term without requiring any bit */ 75 | /* shuffling on our part. Reception works similarly. */ 76 | /* */ 77 | /* The feedback terms table consists of 256, 32-bit entries. Notes: */ 78 | /* */ 79 | /* The table can be generated at runtime if desired; code to do so */ 80 | /* is shown later. It might not be obvious, but the feedback */ 81 | /* terms simply represent the results of eight shift/xor opera- */ 82 | /* tions for all combinations of data and CRC register values. */ 83 | /* */ 84 | /* The values must be right-shifted by eight bits by the "updcrc" */ 85 | /* logic; the shift must be unsigned (bring in zeroes). On some */ 86 | /* hardware you could probably optimize the shift in assembler by */ 87 | /* using byte-swap instructions. */ 88 | /* polynomial $edb88320 */ 89 | /* */ 90 | /* -------------------------------------------------------------------- */ 91 | 92 | static unsigned long crc32_tab[] = { 93 | 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, 94 | 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, 95 | 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, 96 | 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, 97 | 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, 98 | 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, 99 | 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, 100 | 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, 101 | 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, 102 | 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, 103 | 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, 104 | 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, 105 | 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, 106 | 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, 107 | 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, 108 | 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, 109 | 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, 110 | 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, 111 | 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, 112 | 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, 113 | 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, 114 | 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, 115 | 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, 116 | 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, 117 | 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, 118 | 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, 119 | 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, 120 | 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, 121 | 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, 122 | 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, 123 | 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, 124 | 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, 125 | 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, 126 | 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, 127 | 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, 128 | 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, 129 | 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, 130 | 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, 131 | 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, 132 | 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, 133 | 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, 134 | 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, 135 | 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, 136 | 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, 137 | 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, 138 | 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, 139 | 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, 140 | 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, 141 | 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, 142 | 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, 143 | 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, 144 | 0x2d02ef8dL 145 | }; 146 | 147 | /* Return a 32-bit CRC of the contents of the buffer. */ 148 | 149 | unsigned long crc32(const unsigned char *s, unsigned int len) 150 | { 151 | unsigned int i; 152 | unsigned long crc32val; 153 | 154 | crc32val = 0; 155 | for (i = 0; i < len; i ++) 156 | { 157 | crc32val = 158 | crc32_tab[(crc32val ^ s[i]) & 0xff] ^ 159 | (crc32val >> 8); 160 | } 161 | return crc32val; 162 | } 163 | 164 | /* 165 | * Hashing function for a string 166 | */ 167 | unsigned int hashmap_hash_int(hashmap_map * m, char* keystring){ 168 | 169 | unsigned long key = crc32((unsigned char*)(keystring), strlen(keystring)); 170 | 171 | /* Robert Jenkins' 32 bit Mix Function */ 172 | key += (key << 12); 173 | key ^= (key >> 22); 174 | key += (key << 4); 175 | key ^= (key >> 9); 176 | key += (key << 10); 177 | key ^= (key >> 2); 178 | key += (key << 7); 179 | key ^= (key >> 12); 180 | 181 | /* Knuth's Multiplicative Method */ 182 | key = (key >> 3) * 2654435761; 183 | 184 | return key % m->table_size; 185 | } 186 | 187 | /* 188 | * Return the integer of the location in data 189 | * to store the point to the item, or MAP_FULL. 190 | */ 191 | int hashmap_hash(map_t in, char* key){ 192 | int curr; 193 | int i; 194 | 195 | /* Cast the hashmap */ 196 | hashmap_map* m = (hashmap_map *) in; 197 | 198 | /* If full, return immediately */ 199 | if(m->size >= (m->table_size/2)) return MAP_FULL; 200 | 201 | /* Find the best index */ 202 | curr = hashmap_hash_int(m, key); 203 | 204 | /* Linear probing */ 205 | for(i = 0; i< MAX_CHAIN_LENGTH; i++){ 206 | if(m->data[curr].in_use == 0) 207 | return curr; 208 | 209 | if(m->data[curr].in_use == 1 && (strcmp(m->data[curr].key,key)==0)) 210 | return curr; 211 | 212 | curr = (curr + 1) % m->table_size; 213 | } 214 | 215 | return MAP_FULL; 216 | } 217 | 218 | /* 219 | * Doubles the size of the hashmap, and rehashes all the elements 220 | */ 221 | int hashmap_rehash(map_t in){ 222 | int i; 223 | int old_size; 224 | hashmap_element* curr; 225 | 226 | /* Setup the new elements */ 227 | hashmap_map *m = (hashmap_map *) in; 228 | hashmap_element* temp = (hashmap_element *) 229 | calloc(2 * m->table_size, sizeof(hashmap_element)); 230 | if(!temp) return MAP_OMEM; 231 | 232 | /* Update the array */ 233 | curr = m->data; 234 | m->data = temp; 235 | 236 | /* Update the size */ 237 | old_size = m->table_size; 238 | m->table_size = 2 * m->table_size; 239 | m->size = 0; 240 | 241 | /* Rehash the elements */ 242 | for(i = 0; i < old_size; i++){ 243 | int status; 244 | 245 | if (curr[i].in_use == 0) 246 | continue; 247 | 248 | status = hashmap_put(m, curr[i].key, curr[i].data); 249 | if (status != MAP_OK) 250 | return status; 251 | } 252 | 253 | free(curr); 254 | 255 | return MAP_OK; 256 | } 257 | 258 | /* 259 | * Add a pointer to the hashmap with some key 260 | */ 261 | int hashmap_put(map_t in, char* key, any_t value){ 262 | int index; 263 | hashmap_map* m; 264 | 265 | /* Cast the hashmap */ 266 | m = (hashmap_map *) in; 267 | 268 | /* Find a place to put our value */ 269 | index = hashmap_hash(in, key); 270 | while(index == MAP_FULL){ 271 | if (hashmap_rehash(in) == MAP_OMEM) { 272 | return MAP_OMEM; 273 | } 274 | index = hashmap_hash(in, key); 275 | } 276 | 277 | /* Set the data */ 278 | m->data[index].data = value; 279 | m->data[index].key = key; 280 | m->data[index].in_use = 1; 281 | m->size++; 282 | 283 | return MAP_OK; 284 | } 285 | 286 | /* 287 | * Get your pointer out of the hashmap with a key 288 | */ 289 | int hashmap_get(map_t in, char* key, any_t *arg){ 290 | int curr; 291 | int i; 292 | hashmap_map* m; 293 | 294 | /* Cast the hashmap */ 295 | m = (hashmap_map *) in; 296 | 297 | /* Find data location */ 298 | curr = hashmap_hash_int(m, key); 299 | 300 | /* Linear probing, if necessary */ 301 | for(i = 0; idata[curr].in_use; 304 | if (in_use == 1){ 305 | if (strcmp(m->data[curr].key,key)==0){ 306 | *arg = (m->data[curr].data); 307 | return MAP_OK; 308 | } 309 | } 310 | 311 | curr = (curr + 1) % m->table_size; 312 | } 313 | 314 | *arg = NULL; 315 | 316 | /* Not found */ 317 | return MAP_MISSING; 318 | } 319 | 320 | /* 321 | * Iterate the function parameter over each element in the hashmap. The 322 | * additional any_t argument is passed to the function as its first 323 | * argument and the hashmap element is the second. 324 | */ 325 | int hashmap_iterate(map_t in, PFany f, any_t item) { 326 | int i; 327 | 328 | /* Cast the hashmap */ 329 | hashmap_map* m = (hashmap_map*) in; 330 | 331 | /* On empty hashmap, return immediately */ 332 | if (hashmap_length(m) <= 0) 333 | return MAP_MISSING; 334 | 335 | /* Linear probing */ 336 | for(i = 0; i< m->table_size; i++) 337 | if(m->data[i].in_use != 0) { 338 | any_t data = (any_t) (m->data[i].data); 339 | int status = f(item, data); 340 | if (status != MAP_OK) { 341 | return status; 342 | } 343 | } 344 | 345 | return MAP_OK; 346 | } 347 | 348 | /* 349 | * Remove an element with that key from the map 350 | */ 351 | int hashmap_remove(map_t in, char* key){ 352 | int i; 353 | int curr; 354 | hashmap_map* m; 355 | 356 | /* Cast the hashmap */ 357 | m = (hashmap_map *) in; 358 | 359 | /* Find key */ 360 | curr = hashmap_hash_int(m, key); 361 | 362 | /* Linear probing, if necessary */ 363 | for(i = 0; idata[curr].in_use; 366 | if (in_use == 1){ 367 | if (strcmp(m->data[curr].key,key)==0){ 368 | /* Blank out the fields */ 369 | m->data[curr].in_use = 0; 370 | m->data[curr].data = NULL; 371 | m->data[curr].key = NULL; 372 | 373 | /* Reduce the size */ 374 | m->size--; 375 | return MAP_OK; 376 | } 377 | } 378 | curr = (curr + 1) % m->table_size; 379 | } 380 | 381 | /* Data not found */ 382 | return MAP_MISSING; 383 | } 384 | 385 | /* Deallocate the hashmap */ 386 | void hashmap_free(map_t in){ 387 | hashmap_map* m = (hashmap_map*) in; 388 | free(m->data); 389 | free(m); 390 | } 391 | 392 | /* Return the length of the hashmap */ 393 | int hashmap_length(map_t in){ 394 | hashmap_map* m = (hashmap_map *) in; 395 | if(m != NULL) return m->size; 396 | else return 0; 397 | } -------------------------------------------------------------------------------- /third_party/c_hashmap/hashmap.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Generic hashmap manipulation functions 3 | * 4 | * Originally by Elliot C Back - http://elliottback.com/wp/hashmap-implementation-in-c/ 5 | * 6 | * Modified by Pete Warden to fix a serious performance problem, support strings as keys 7 | * and removed thread synchronization - http://petewarden.typepad.com 8 | */ 9 | #ifndef __HASHMAP_H__ 10 | #define __HASHMAP_H__ 11 | 12 | #define MAP_MISSING -3 /* No such element */ 13 | #define MAP_FULL -2 /* Hashmap is full */ 14 | #define MAP_OMEM -1 /* Out of Memory */ 15 | #define MAP_OK 0 /* OK */ 16 | 17 | /* 18 | * any_t is a pointer. This allows you to put arbitrary structures in 19 | * the hashmap. 20 | */ 21 | typedef void *any_t; 22 | 23 | /* 24 | * PFany is a pointer to a function that can take two any_t arguments 25 | * and return an integer. Returns status code.. 26 | */ 27 | typedef int (*PFany)(any_t, any_t); 28 | 29 | /* 30 | * map_t is a pointer to an internally maintained data structure. 31 | * Clients of this package do not need to know how hashmaps are 32 | * represented. They see and manipulate only map_t's. 33 | */ 34 | typedef any_t map_t; 35 | 36 | /* 37 | * Return an empty hashmap. Returns NULL if empty. 38 | */ 39 | extern map_t hashmap_new(); 40 | 41 | /* 42 | * Iteratively call f with argument (item, data) for 43 | * each element data in the hashmap. The function must 44 | * return a map status code. If it returns anything other 45 | * than MAP_OK the traversal is terminated. f must 46 | * not reenter any hashmap functions, or deadlock may arise. 47 | */ 48 | extern int hashmap_iterate(map_t in, PFany f, any_t item); 49 | 50 | /* 51 | * Add an element to the hashmap. Return MAP_OK or MAP_OMEM. 52 | */ 53 | extern int hashmap_put(map_t in, char* key, any_t value); 54 | 55 | /* 56 | * Get an element from the hashmap. Return MAP_OK or MAP_MISSING. 57 | */ 58 | extern int hashmap_get(map_t in, char* key, any_t *arg); 59 | 60 | /* 61 | * Remove an element from the hashmap. Return MAP_OK or MAP_MISSING. 62 | */ 63 | extern int hashmap_remove(map_t in, char* key); 64 | 65 | /* 66 | * Get any element. Return MAP_OK or MAP_MISSING. 67 | * remove - should the element be removed from the hashmap 68 | */ 69 | extern int hashmap_get_one(map_t in, any_t *arg, int remove); 70 | 71 | /* 72 | * Free the hashmap 73 | */ 74 | extern void hashmap_free(map_t in); 75 | 76 | /* 77 | * Get the current size of a hashmap 78 | */ 79 | extern int hashmap_length(map_t in); 80 | 81 | #endif /* __HASHMAP_H__ */ -------------------------------------------------------------------------------- /third_party/c_hashmap/main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * A unit test and example of how to use the simple C hashmap 3 | */ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include "hashmap.h" 10 | 11 | #define KEY_MAX_LENGTH (256) 12 | #define KEY_PREFIX ("somekey") 13 | #define KEY_COUNT (1024*1024) 14 | 15 | typedef struct data_struct_s 16 | { 17 | char key_string[KEY_MAX_LENGTH]; 18 | int number; 19 | } data_struct_t; 20 | 21 | int main(char* argv, int argc) 22 | { 23 | int index; 24 | int error; 25 | map_t mymap; 26 | char key_string[KEY_MAX_LENGTH]; 27 | data_struct_t* value; 28 | 29 | mymap = hashmap_new(); 30 | 31 | /* First, populate the hash map with ascending values */ 32 | for (index=0; indexkey_string, KEY_MAX_LENGTH, "%s%d", KEY_PREFIX, index); 37 | value->number = index; 38 | 39 | error = hashmap_put(mymap, value->key_string, value); 40 | assert(error==MAP_OK); 41 | } 42 | 43 | /* Now, check all of the expected values are there */ 44 | for (index=0; indexnumber==index); 53 | } 54 | 55 | /* Make sure that a value that wasn't in the map can't be found */ 56 | snprintf(key_string, KEY_MAX_LENGTH, "%s%d", KEY_PREFIX, KEY_COUNT); 57 | 58 | error = hashmap_get(mymap, key_string, (void**)(&value)); 59 | 60 | /* Make sure the value was not found */ 61 | assert(error==MAP_MISSING); 62 | 63 | /* Free all of the values we allocated and remove them from the map */ 64 | for (index=0; index> (64 - k)); 13 | } 14 | 15 | void rng_init(rng *r, uint64_t seed1, uint64_t seed2) 16 | { 17 | r->state[0] = seed1; 18 | r->state[1] = seed2; 19 | } 20 | 21 | inline uint64_t rng_next(rng *r) 22 | { 23 | const uint64_t s0 = r->state[0]; 24 | uint64_t s1 = r->state[1]; 25 | const uint64_t value = s0 + s1; 26 | 27 | s1 ^= s0; 28 | r->state[0] = rotl(s0, 55) ^ s1 ^ (s1 << 14); 29 | r->state[1] = rotl(s1, 36); 30 | 31 | return value; 32 | } 33 | 34 | void rng_jump(rng *r) 35 | { 36 | static const uint64_t j[] = {0xbeac0467eba5facb, 0xd86b048b86aa9922}; 37 | 38 | uint64_t s0 = 0, s1 = 0; 39 | for (int i = 0; i < 2; i++) { 40 | for (int b = 0; b < 64; b++) { 41 | if (j[i] & (uint64_t)1 << b) { 42 | s0 ^= r->state[0]; 43 | s1 ^= r->state[1]; 44 | } 45 | rng_next(r); 46 | } 47 | } 48 | 49 | r->state[0] = s0; 50 | r->state[1] = s1; 51 | } 52 | 53 | -------------------------------------------------------------------------------- /util/rng.h: -------------------------------------------------------------------------------- 1 | /** 2 | * author: UncP 3 | * date: 2019-03-14 4 | * license: BSD-3 5 | **/ 6 | 7 | #ifndef _rng_h_ 8 | #define _rng_h_ 9 | 10 | #include 11 | 12 | typedef struct rng 13 | { 14 | uint64_t state[2]; 15 | }rng; 16 | 17 | void rng_init(rng *r, uint64_t seed1, uint64_t seed2); 18 | uint64_t rng_next(rng *r); 19 | 20 | #endif /* _rng_h_ */ 21 | --------------------------------------------------------------------------------