├── .gitignore ├── LICENSE ├── Makefile ├── README.md ├── c ├── Makefile.common ├── ctypes.h ├── dbcdf.c ├── dbtest1.c ├── kv.c ├── kv.h ├── lib.c ├── lib.h ├── ord.c ├── ord.h ├── wh.c ├── wh.h └── ycsbtest.c ├── flexdb.c ├── flexdb.h ├── flexfile.c ├── flexfile.h ├── flextree.c ├── flextree.h ├── generic.c ├── generic.h ├── test_flexdb.c ├── test_flexfile.c ├── test_flextree.c ├── test_flextree_check.c ├── wrapper.c └── wrapper.h /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*.c 3 | !*.h 4 | !Makefile* 5 | !.gitignore 6 | !.gitmodules 7 | !c 8 | !*_benchmark 9 | !*.md 10 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) Chen Chen 2 | 3 | Permission is hereby granted, free of charge, to any 4 | person obtaining a copy of this software and associated 5 | documentation files (the "Software"), to deal in the 6 | Software without restriction, including without 7 | limitation the rights to use, copy, modify, merge, 8 | publish, distribute, sublicense, and/or sell copies of 9 | the Software, and to permit persons to whom the Software 10 | is furnished to do so, subject to the following 11 | conditions: 12 | 13 | The above copyright notice and this permission notice 14 | shall be included in all copies or substantial portions 15 | of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF 18 | ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED 19 | TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A 20 | PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT 21 | SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 22 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 23 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR 24 | IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 25 | DEALINGS IN THE SOFTWARE. 26 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # Makefile 2 | # rules (always with .out) 3 | # SRC-X.out += abc # extra source: abc.c 4 | # MOD-X.out += abc # extra module: abc.c abc.h 5 | # ASM-X.out += abc # extra assembly: abc.S 6 | # DEP-X.out += abc # extra dependency: abc 7 | # FLG-X.out += -finline # extra flags 8 | # LIB-X.out += abc # extra -labc options 9 | 10 | # X.out : xyz.h xyz.c # for extra dependences that are to be compiled/linked. 11 | 12 | VPATH += .:c/ 13 | 14 | # X => X.out 15 | TARGETS += test_flextree test_flexfile test_flexdb 16 | # X => X.c only 17 | SOURCES += 18 | # X => X.S only 19 | ASSMBLY += 20 | # X => X.c X.h 21 | MODULES += c/lib c/kv c/ord generic flextree flexfile flexdb 22 | # X => X.h 23 | HEADERS += 24 | 25 | # EXTERNSRC/EXTERNDEP do not belong to this repo. 26 | # extern-src will be linked 27 | EXTERNSRC += 28 | # extern-dep will not be linked 29 | EXTERNDEP += 30 | 31 | FLG += 32 | LIB += rt m uring 33 | 34 | ifeq ($(LEVELDB),y) 35 | FLG += -DLEVELDB 36 | LIB += leveldb 37 | endif 38 | ifeq ($(ROCKSDB),y) 39 | FLG += -DROCKSDB 40 | LIB += rocksdb 41 | endif 42 | ifeq ($(LMDB),y) 43 | FLG += -DLMDB 44 | LIB += lmdb 45 | endif 46 | ifeq ($(KVELL),y) 47 | FLG += -DKVELL -L. 48 | LIB += kvell 49 | endif 50 | 51 | # when $ make FORKER_PAPI=y 52 | ifeq ($(strip $(FORKER_PAPI)),y) 53 | LIB += papi 54 | FLG += -DFORKER_PAPI 55 | endif 56 | 57 | include c/Makefile.common 58 | 59 | libflexfile.so : Makefile Makefile.common c/lib.h c/lib.c flextree.h \ 60 | flextree.c flexfile.h flexfile.c generic.h generic.c wrapper.c wrapper.h 61 | $(eval ALLFLG := $(CSTD) $(EXTRA) $(FLG) -shared -fPIC) 62 | $(eval ALLLIB := $(addprefix -l,$(LIB) $(LIB-$@))) 63 | $(CCC) $(ALLFLG) -o $@ wrapper.c flextree.c flexfile.c generic.c c/lib.c $(ALLLIB) -ldl 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # FlexTree, FlexSpace and FlexDB 2 | 3 | This repository contains the reference implementation of FlexTree, FlexSpace and FlexDB presented in the paper 4 | ''Building an Efficient Key-Value Store in a Flexible Address Space'' on EuroSys 2022. 5 | 6 | [[ACM DL](https://dl.acm.org/doi/10.1145/3492321.3519555)] 7 | [[Paper PDF](https://www.roychan.org/assets/publications/eurosys22chen.pdf)] 8 | [[Slides](https://www.roychan.org/assets/publications/eurosys22chen-slides.pdf)] 9 | 10 | ## Repository Structure 11 | 12 | Note: the terms we used in the implementation has some difference from the paper. 13 | Thw following table shows the mapping between the paper terms and API prefixes in the code. 14 | 15 | | Paper Term | Code Term | 16 | | --- | --- | 17 | | FlexTree | `flextree` | 18 | | Sorted Extent Array | `brute_force` | 19 | | FlexSpace | `flexfile` | 20 | | FlexSpace Segment | `block` and `BLOCK` in macros | 21 | | FlexDB | `flexdb` | 22 | 23 | The content of this repository is organized as follows: 24 | - `c/`: external library dependencies (including the thread-safe skip list used as FlexDB's MemTable) 25 | - `flextree.h`, `flextree.c`: FlexTree implementation and its APIs, including reference code of sorted extent array 26 | - `flexfile.h`, `flexfile.c`: FlexSpace implementation and its APIs 27 | - `flexdb.h`, `flexdb.c`: FlexDB implementation and its APIs 28 | - `generic.h`, `generic.c`: A generic syscall wrapper for different platforms (only Linux currently) 29 | - `wrapper.h`, `wrapper.h`: A library that implements POSIX-file-like interface for FlexSpace 30 | (not coupled with its core functionalities) 31 | - `test_flextree.c`, `test_flexfile.c`, `test_flexdb.c`: Test cases for each component's core functionalities 32 | (correctness only) 33 | - `LICENSE`, `Makefile`: namely 34 | 35 | ## Implementation Details 36 | 37 | You can refer to the header files for more details of the implementation. 38 | 39 | ### FlexTree and FlexSpace 40 | 41 | The FlexTree implementation supports storing the tree in the main memory and using CoW to checkpoint the in-memory 42 | tree into an on-disk file. 43 | The code also contains a naive implementation of a sorted extent array to verify the correctness of FlexTree. 44 | The test program for FlexTree contains ten micro-benchmarks to test its functionalities. 45 | 46 | The FlexSpace implementation provides a set of file-like APIs to support 47 | read/(over)write/insert-range/collapse-range/sync operations. 48 | It is backed with the functionalities of log-structured space allocation, 49 | segment-based garbage collection and crash recovery mechanisms. 50 | The test program for FlexSpace tests the basic data I/O operations. 51 | Specifically, the implementation also contains an extra set of APIs that support 52 | setting a tag at a specific logical address. 53 | A tag is a 16-bit value and it shifts with the data. 54 | 55 | *Please note that the current implementation of FlexSpace and FlexTree is not thread-safe.* 56 | 57 | ### FlexDB 58 | 59 | The FlexDB implementation provides a fully-functional persistent key-value store. 60 | The code contains the implementation of a volatile sparse KV index, an interval cache, 61 | a skip-list-based MemTable, write-ahead logging and the according crash recovery mechanisms. 62 | FlexDB supports regular KV operations including write/update (`PUT`), point query (`GET`), range query (`SCAN`) 63 | and deletion (`DELETE`). 64 | You can refer to `flexdb.h` to better understand the implementation of FlexDB, as well as the APIs provided. 65 | 66 | *The implementation of FlexDB is thread-safe and can be used with multiple concurrent threads.* 67 | 68 | ## Demo 69 | 70 | The test programs for each component is a good starting point to demonstrate the basic usage. 71 | To compile the test program, simply using `make` will compile their executable binaries. 72 | The codebase is verified to compile with the following environment and dependencies: 73 | 74 | - Linux kernel 5.10.32 LTS (support io_uring) 75 | - clang 12.0.1 76 | - jemalloc 5.2.1 77 | - liburing 2.0 78 | 79 | The basic usage of each testing program is: 80 | 81 | - `test_flextree.out`: this program tests the basic functionalities of the FlexTree implementation, and it uses sorted 82 | extent array as reference to verify the correctness. It contains ten test cases and each can be run by using the 83 | command `./test_flextree.out ` where `n` is the test ID (0-9). Note that most tests are conducted in DRAM only but 84 | a few tests will use `/tmp` (though it's also usually a memory resident). You can refer to the code to see the content 85 | of each test. 86 | - `test_flexfile.out`: this program is a minimal test to verify the correctness of insertions and deletions in a 87 | FlexSpace. It will create a FlexSpace in `/tmp` and performs a few I/O operations on it, and verify the results with 88 | expected output. You can simply run `./test_flexfile.out` to see if the outputs are correct. 89 | - `test_flexdb.out`: this program tests the basic functionality of FlexDB. It creates a store in `/tmp` and perform a 90 | range of regular KV operations on it. To run the program, simply use `./test_flexdb.out`. 91 | 92 | ## Citation (BibTeX) 93 | 94 | ``` 95 | @inproceedings{chen2022flexspace, 96 | author = {Chen, Chen and Zhong, Wenshao and Wu, Xingbo}, 97 | title = {Building an Efficient Key-Value Store in a Flexible Address Space}, 98 | year = {2022}, 99 | isbn = {9781450391627}, 100 | publisher = {Association for Computing Machinery}, 101 | address = {New York, NY, USA}, 102 | url = {https://doi.org/10.1145/3492321.3519555}, 103 | doi = {10.1145/3492321.3519555}, 104 | booktitle = {Proceedings of the Seventeenth European Conference on Computer Systems}, 105 | pages = {51–68}, 106 | numpages = {18}, 107 | keywords = {key-value store, storage, address space}, 108 | location = {Rennes, France}, 109 | series = {EuroSys '22} 110 | } 111 | ``` 112 | -------------------------------------------------------------------------------- /c/Makefile.common: -------------------------------------------------------------------------------- 1 | #usage: include Makefile.common at the end of your Makefile 2 | 3 | # no builtin rules/vars (CC, CXX, etc. are still defined but will be empty) 4 | MAKEFLAGS += -r -R 5 | 6 | HDR = $(addsuffix .h,$(MODULES) $(HEADERS)) 7 | SRC = $(addsuffix .c,$(MODULES) $(SOURCES)) 8 | ASM = $(addsuffix .S,$(ASSMBLY)) 9 | OBJ = $(addsuffix .o,$(MODULES) $(SOURCES) $(ASSEMBLY)) 10 | DEP = Makefile.common Makefile $(HDR) $(EXTERNDEP) $(EXTERNSRC) 11 | BIN = $(addsuffix .out,$(TARGETS)) 12 | DIS = $(addsuffix .dis,$(TARGETS)) 13 | 14 | # clang: 15 | # EXTRA="-Rpass=loop-vectorize" # IDs loops that were successfully V-ed 16 | # EXTRA="-Rpass-missed=loop-vectorize" # IDs loops that failed V 17 | # EXTRA="-Rpass-analysis=loop-vectorize" # IDs the statements that caused V to fail 18 | # EXTRA="-Rpass=\ *" # remarks for all passes 19 | # other passes: https://llvm.org/docs/Passes.html 20 | 21 | O ?= rg 22 | 23 | # predefined OPT: make O={rg,r,0g,3g,p,0s,3s,cov,mc,hc,wn,stk} 24 | ifeq ($O,rg) # make O=rg 25 | OPT ?= -DNDEBUG -g3 -O3 -flto -fno-stack-protector 26 | else ifeq ($O,r) # make O=r (for release) 27 | OPT ?= -DNDEBUG -O3 -flto -fno-stack-protector 28 | else ifeq ($O,ns) # make O=ns (no signal handlers) 29 | OPT ?= -DNDEBUG -O3 -flto -fno-stack-protector -DNOSIGNAL 30 | else ifeq ($O,0g) # make O=0g 31 | OPT ?= -g3 -O0 -fno-inline 32 | else ifeq ($O,2g) # make O=2g 33 | OPT ?= -g3 -O2 34 | else ifeq ($O,3g) # make O=3g 35 | OPT ?= -g3 -O3 -flto -fno-inline 36 | else ifeq ($O,p) # make O=p (profiling: rg+noinline) 37 | OPT ?= -DNDEBUG -g3 -O3 -flto -fno-stack-protector -fno-inline 38 | else ifeq ($O,0s) # make O=0s (address sanitizer) 39 | OPT ?= -g3 -O0 -fno-inline -fsanitize=address -fno-omit-frame-pointer -DHEAPCHECKING 40 | else ifeq ($O,3s) # make O=3s (address sanitizer) 41 | OPT ?= -g3 -O3 -fno-inline -fsanitize=address -fno-omit-frame-pointer -DHEAPCHECKING 42 | else ifeq ($O,t) # make O=0t (thread sanitizer) 43 | OPT ?= -g3 -O1 -fno-inline -fsanitize=thread -fno-stack-protector 44 | else ifeq ($O,cov) # make O=cov (for gcov) 45 | OPT ?= -g3 -DNDEBUG -O0 --coverage 46 | CCC = gcc 47 | else ifeq ($O,mc) # make O=mc (for valgrind memcheck) 48 | OPT ?= -g3 -O1 -fno-inline -DHEAPCHECKING 49 | ARCH ?= broadwell 50 | else ifeq ($O,hc) # make O=hc (for gperftools heapcheck) 51 | OPT ?= -g3 -O1 -fno-inline 52 | LIB += tcmalloc 53 | else ifeq ($O,wn) # more warning 54 | OPT ?= -g3 -O3 -Wvla -Wformat=2 -Wconversion -Wstrict-prototypes -Wmissing-prototypes 55 | else ifeq ($O,stk) # check stack usage with gcc 56 | OPT ?= -g3 -O3 -DNDEBUG -fstack-usage 57 | CCC = gcc 58 | endif 59 | 60 | # malloc: g:glibc, t:tcmalloc, j:jemalloc 61 | M ?= g 62 | 63 | ifeq ($M,t) 64 | LIB += tcmalloc 65 | FLG += -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-free 66 | else ifeq ($M,j) 67 | LIB += jemalloc 68 | endif 69 | 70 | UNAME_S := $(shell uname -s) 71 | ifeq ($(UNAME_S),Linux) 72 | CHECK_S := -D__linux__ 73 | LIB += rt 74 | else ifeq ($(UNAME_S),FreeBSD) 75 | CHECK_S := -D__FreeBSD__ 76 | FLG += -I/usr/local/include -L/usr/local/lib 77 | LIB += rt 78 | LIB += execinfo 79 | TPUT := /usr/local/bin/tput 80 | else ifeq ($(UNAME_S),Darwin) 81 | CHECK_S := -D__APPLE__ -D__MACH__ 82 | # do nothing 83 | else 84 | $(error "Supported Platforms: Linux, FreeBSD, Darwin") 85 | endif 86 | TPUT ?= tput 87 | 88 | CCC ?= clang 89 | CSTD = -std=gnu18 90 | XCC ?= clang++ 91 | XSTD = -std=gnu++17 92 | 93 | UNAME_M := $(shell uname -m) 94 | ifeq ($(UNAME_M),aarch64) # "native" does not work for clang@aarch64 95 | CHECK_M := -D__aarch64__ 96 | ARCH ?= armv8-a+crc 97 | else ifeq ($(UNAME_M),arm64) # "native" does not work for clang@aarch64 98 | CHECK_M := -D__aarch64__ 99 | ARCH ?= armv8-a+crc 100 | else ifeq ($(UNAME_M),x86_64) 101 | CHECK_M := -D__x86_64__ 102 | ARCH ?= native 103 | else ifeq ($(UNAME_M),amd64) # freebsd 104 | CHECK_M := -D__x86_64__ 105 | ARCH ?= native 106 | else 107 | $(error "Supported Platforms: aarch64, x86_64") 108 | endif 109 | 110 | TUNE ?= native 111 | 112 | NBI += memcpy memmove memcmp 113 | 114 | # minimal requirement on x86_64: -march=nehalem 115 | # minimal requirement on aarch64: -march=armv8-a+crc 116 | FLG += -march=$(ARCH) -mtune=$(TUNE) 117 | FLG += -pthread -Wall -Wextra -Wshadow #-Weverything 118 | FLG += $(addprefix -fno-builtin-,$(NBI)) 119 | FLG += $(OPT) 120 | 121 | ifneq ($(shell $(CCC) --version 2>/dev/null | grep clang),) 122 | FLG += -ferror-limit=3 123 | CCCTYPE := clang 124 | else ifneq ($(shell $(CCC) --version 2>/dev/null | grep gcc),) 125 | FLG += -fmax-errors=3 126 | FLG += -Wno-unknown-pragmas 127 | CCCTYPE := gcc 128 | else 129 | $(error "Supported Compilers: clang, gcc") 130 | endif 131 | 132 | ifeq ($(CCCTYPE),clang) 133 | CCINST = /usr/lib/clang/$(shell $(CCC) --version 2>/dev/null | awk '/^clang/ { print $$3 }') 134 | CCINC = $(CCINST)/include 135 | else ifeq ($(CCCTYPE),gcc) 136 | CCINST = /usr/lib/gcc/$(shell $(CCC) -dumpmachine)/$(shell $(CCC) -dumpversion) 137 | CCINC = $(CCINST)/include $(CCINST)/include-fixed 138 | endif 139 | CCINC = /usr/include /usr/local/include 140 | 141 | ifneq ($(shell find $(CCINC) -name backtrace-supported.h 2>/dev/null),) 142 | LIB += backtrace 143 | FLG += -DBACKTRACE 144 | endif 145 | 146 | ifneq ($(shell find $(CCINC) -name liburing.h 2>/dev/null),) 147 | LIB += uring 148 | FLG += -DLIBURING 149 | endif 150 | 151 | 152 | uniq = $(if $1,$(firstword $1) $(call uniq,$(filter-out $(firstword $1),$1))) 153 | magentatxt := $(shell $(TPUT) setaf 5) 154 | greentxt := $(shell $(TPUT) setaf 2) 155 | bluetxt := $(shell $(TPUT) setaf 4) 156 | normaltxt := $(shell $(TPUT) sgr0) 157 | 158 | .PHONY : bin dis def clean cleanx check tags 159 | 160 | bin : $(BIN) 161 | dis : $(DIS) bin 162 | .DEFAULT_GOAL = bin 163 | .SECONDEXPANSION: 164 | 165 | ifeq ($(J),o) 166 | # DANGER. Don't use unless it works! 167 | # build from .o files but target-specific flags are missing in %.o : %.x 168 | %.out : %.o $(OBJ) $$(addsuffix .o,$$(SRC-$$@) $$(MOD-$$@) $$(ASM-$$@)) 169 | $(eval ALLFLG := $(CSTD) $(EXTRA) $(FLG) $(FLG-$*) $(FLG-$*.o) $(FLG-$@) -rdynamic) 170 | $(eval ALLLIB := $(addprefix -l,$(LIB) $(LIB-$@))) 171 | $(CCC) $(ALLFLG) -o $@ $^ $(ALLLIB) 172 | # 173 | else # default: all-in-one command 174 | %.out : %.c $(SRC) $(ASM) $(DEP) $$(DEP-$$@) $$(addsuffix .c,$$(SRC-$$@) $$(MOD-$$@)) $$(addsuffix .h,$$(HDR-$$@) $$(MOD-$$@)) $$(addsuffix .S,$$(ASM-$$@)) 175 | $(eval ALLSRC := $(SRC) $(addsuffix .c,$(SRC-$@) $(MOD-$@)) $(ASM) $(addsuffix .S,$(ASM-$@))) 176 | $(eval UNIQSRC := $(call uniq,$(ALLSRC))) 177 | $(eval ALLFLG := $(CSTD) $(EXTRA) $(FLG) $(FLG-$@) -rdynamic) 178 | $(eval ALLLIB := $(addprefix -l,$(LIB) $(LIB-$@))) 179 | @printf '$(bluetxt)$@$(magentatxt) <= $(greentxt)$< $(UNIQSRC)$(normaltxt)\n' 180 | $(CCC) $(ALLFLG) -o $@ $< $(UNIQSRC) $(ALLLIB) 181 | # 182 | endif 183 | 184 | 185 | %.dis : %.out 186 | objdump -SlwtC $< 1>$@ 2>/dev/null 187 | 188 | %.o : %.cc $(DEP) $$(DEP-$$@) $$(addsuffix .h,$$(HDR-$$@) $$(MOD-$$@)) 189 | $(XCC) $(XSTD) $(EXTRA) $(FLG) $(FLG-$*) $(FLG-$@) -o $@ -c $< 190 | 191 | %.o : %.c $(DEP) $$(DEP-$$@) $$(addsuffix .h,$$(HDR-$$@) $$(MOD-$$@)) 192 | $(CCC) $(CSTD) $(EXTRA) $(FLG) $(FLG-$*) $(FLG-$@) -o $@ -c $< 193 | 194 | %.o : %.S $(DEP) $$(DEP-$$@) $$(addsuffix .h,$$(HDR-$$@) $$(MOD-$$@)) 195 | $(CCC) $(CSTD) $(EXTRA) $(FLG) $(FLG-$*) $(FLG-$@) -o $@ -c $< 196 | 197 | %.s : %.c $(DEP) $$(DEP-$$@) $$(addsuffix .h,$$(HDR-$$@) $$(MOD-$$@)) 198 | $(CCC) $(CSTD) $(EXTRA) $(FLG) $(FLG-$*) $(FLG-$*.o) -S -o $@ -c $< 199 | 200 | def : 201 | $(CCC) $(FLG) -dM -E - 3 | * 4 | * All rights reserved. No warranty, explicit or implicit, provided. 5 | */ 6 | #pragma once 7 | 8 | // C types only; C++ source code don't use this 9 | 10 | #include 11 | #include 12 | 13 | /* C11 atomic types */ 14 | typedef atomic_bool abool; 15 | 16 | typedef atomic_uchar au8; 17 | typedef atomic_ushort au16; 18 | typedef atomic_uint au32; 19 | typedef atomic_ulong au64; 20 | static_assert(sizeof(au8) == 1, "sizeof(au8)"); 21 | static_assert(sizeof(au16) == 2, "sizeof(au16)"); 22 | static_assert(sizeof(au32) == 4, "sizeof(au32)"); 23 | static_assert(sizeof(au64) == 8, "sizeof(au64)"); 24 | 25 | typedef atomic_char as8; 26 | typedef atomic_short as16; 27 | typedef atomic_int as32; 28 | typedef atomic_long as64; 29 | static_assert(sizeof(as8) == 1, "sizeof(as8)"); 30 | static_assert(sizeof(as16) == 2, "sizeof(as16)"); 31 | static_assert(sizeof(as32) == 4, "sizeof(as32)"); 32 | static_assert(sizeof(as64) == 8, "sizeof(as64)"); 33 | 34 | // shorten long names 35 | #define MO_RELAXED memory_order_relaxed 36 | #define MO_CONSUME memory_order_consume 37 | #define MO_ACQUIRE memory_order_acquire 38 | #define MO_RELEASE memory_order_release 39 | #define MO_ACQ_REL memory_order_acq_rel 40 | #define MO_SEQ_CST memory_order_seq_cst 41 | -------------------------------------------------------------------------------- /c/dbcdf.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016--2021 Wu, Xingbo 3 | * 4 | * All rights reserved. No warranty, explicit or implicit, provided. 5 | */ 6 | #define _GNU_SOURCE 7 | 8 | #include "lib.h" 9 | #include "kv.h" 10 | 11 | struct priv { 12 | void * ref; 13 | u32 klen; 14 | union { 15 | u32 vlen; 16 | u32 nscan; 17 | }; 18 | struct kv * tmp; 19 | struct kv * out; 20 | }; 21 | 22 | #define VCTRSZ ((10000)) 23 | 24 | static bool 25 | kvmap_analyze(void * const passdata[2], const u64 dt, const struct vctr * const va, struct damp * const d, char * const out) 26 | { 27 | (void)dt; 28 | (void)d; 29 | const struct kvmap_api * const api = passdata[0]; 30 | if (api->async) { 31 | api->fprint(passdata[1], stdout); 32 | strcpy(out, "\n"); 33 | return true; 34 | } 35 | 36 | u64 sum = 0; 37 | for (u64 i = 0; i < VCTRSZ; i++) 38 | sum += vctr_get(va, i); 39 | 40 | const u64 tot = sum; 41 | const double totd = (double)tot; 42 | sum = 0; 43 | u64 last = 0; 44 | printf("time_us count delta cdf\n0 0 0 0.000\n"); 45 | for (u64 i = 1; i < VCTRSZ; i++) { 46 | const u64 tmp = vctr_get(va, i); 47 | if (tmp) { 48 | if ((i-1) != last) 49 | printf("%lu %lu %lu %.3lf\n", i-1, sum, 0lu, (double)sum * 100.0 / totd); 50 | sum += tmp; 51 | printf("%lu %lu %lu %.3lf\n", i, sum, tmp, (double)sum * 100.0 / totd); 52 | last = i; 53 | } 54 | } 55 | 56 | sprintf(out, "total %lu\n", tot); 57 | return true; 58 | } 59 | 60 | static void 61 | latency_add(struct vctr * const vctr, const u64 dt) 62 | { 63 | debug_assert(dt); 64 | const u64 us = (dt + 999) / 1000; 65 | if (us < VCTRSZ) { 66 | vctr_add1_atomic(vctr, us); 67 | } else { 68 | vctr_add1_atomic(vctr, VCTRSZ-1); 69 | printf("%s micro-second %lu\n", __func__, us); 70 | } 71 | } 72 | 73 | // (parallel) load; nr <= nr_kvs 74 | static void 75 | kvmap_batch_put_par(const struct forker_worker_info * const info, 76 | const struct priv * const priv, const u64 nr) 77 | { 78 | const struct kvmap_api * const api = info->passdata[0]; 79 | void * const ref = priv->ref; 80 | if (info->end_type != FORKER_END_COUNT) 81 | return; 82 | 83 | struct kv * const tmp = priv->tmp; 84 | const u64 nr1 = nr / info->conc; 85 | const u64 id0 = nr1 * info->worker_id; 86 | const u64 end = (info->worker_id == (info->conc - 1)) ? nr : (id0 + nr1); 87 | for (u64 i = id0; i < end; i++) { 88 | kv_refill_hex64_klen(tmp, i, priv->klen, NULL, 0); 89 | tmp->vlen = priv->vlen; 90 | const u64 t0 = time_nsec(); 91 | (void)kvmap_kv_put(api, ref, tmp); 92 | const u64 dt = time_diff_nsec(t0); 93 | latency_add(info->vctr, dt); 94 | } 95 | } 96 | 97 | static void 98 | kvmap_batch_put(const struct forker_worker_info * const info, 99 | const struct priv * const priv, const u64 nr) 100 | { 101 | const struct kvmap_api * const api = info->passdata[0]; 102 | void * const ref = priv->ref; 103 | struct rgen * const gen = info->gen; 104 | rgen_next_func next = info->rgen_next_write; 105 | struct kv * const tmp = priv->tmp; 106 | 107 | for (u64 i = 0; i < nr; i++) { 108 | kv_refill_hex64_klen(tmp, next(gen), priv->klen, NULL, 0); 109 | tmp->vlen = priv->vlen; 110 | const u64 t0 = time_nsec(); 111 | (void)kvmap_kv_put(api, ref, tmp); 112 | const u64 dt = time_diff_nsec(t0); 113 | latency_add(info->vctr, dt); 114 | } 115 | } 116 | 117 | static void 118 | kvmap_batch_del(const struct forker_worker_info * const info, 119 | const struct priv * const priv, const u64 nr) 120 | { 121 | const struct kvmap_api * const api = info->passdata[0]; 122 | void * const ref = priv->ref; 123 | struct rgen * const gen = info->gen; 124 | rgen_next_func next = info->rgen_next_write; 125 | struct kv * const tmp = priv->tmp; 126 | 127 | for (u64 i = 0; i < nr; i++) { 128 | kv_refill_hex64_klen(tmp, next(gen), priv->klen, NULL, 0); 129 | const u64 t0 = time_nsec(); 130 | (void)kvmap_kv_del(api, ref, tmp); 131 | const u64 dt = time_diff_nsec(t0); 132 | latency_add(info->vctr, dt); 133 | } 134 | } 135 | 136 | static void 137 | kvmap_batch_get(const struct forker_worker_info * const info, 138 | const struct priv * const priv, const u64 nr) 139 | { 140 | const struct kvmap_api * const api = info->passdata[0]; 141 | void * const ref = priv->ref; 142 | struct rgen * const gen = info->gen; 143 | rgen_next_func next = info->rgen_next; 144 | struct kv * const tmp = priv->tmp; 145 | 146 | for (u64 i = 0; i < nr; i++) { 147 | kv_refill_hex64_klen(tmp, next(gen), priv->klen, NULL, 0); 148 | const u64 t0 = time_nsec(); 149 | (void)kvmap_kv_get(api, ref, tmp, priv->out); 150 | const u64 dt = time_diff_nsec(t0); 151 | latency_add(info->vctr, dt); 152 | } 153 | } 154 | 155 | static void 156 | kvmap_batch_pro(const struct forker_worker_info * const info, 157 | const struct priv * const priv, const u64 nr) 158 | { 159 | const struct kvmap_api * const api = info->passdata[0]; 160 | void * const ref = priv->ref; 161 | struct rgen * const gen = info->gen; 162 | rgen_next_func next = info->rgen_next; 163 | struct kv * const tmp = priv->tmp; 164 | 165 | for (u64 i = 0; i < nr; i++) { 166 | kv_refill_hex64_klen(tmp, next(gen), priv->klen, NULL, 0); 167 | const u64 t0 = time_nsec(); 168 | (void)kvmap_kv_probe(api, ref, tmp); 169 | const u64 dt = time_diff_nsec(t0); 170 | latency_add(info->vctr, dt); 171 | } 172 | } 173 | 174 | static void 175 | kvmap_batch_seek_next(const struct forker_worker_info * const info, 176 | const struct priv * const priv, const u64 nr) 177 | { 178 | const struct kvmap_api * const api = info->passdata[0]; 179 | void * const ref = priv->ref; 180 | void * const iter = api->iter_create(ref); 181 | const u32 nscan = priv->nscan; 182 | debug_assert(iter); 183 | struct rgen * const gen = info->gen; 184 | rgen_next_func next = info->rgen_next; 185 | 186 | for (u64 i = 0; i < nr; i++) { 187 | kv_refill_hex64_klen(priv->tmp, next(gen), priv->klen, NULL, 0); 188 | const u64 t0 = time_nsec(); 189 | kvmap_kv_iter_seek(api, iter, priv->tmp); 190 | for (u32 j = 0; j < nscan; j++) 191 | api->iter_next(iter, priv->out); 192 | const u64 dt = time_diff_nsec(t0); 193 | latency_add(info->vctr, dt); 194 | } 195 | api->iter_destroy(iter); 196 | } 197 | 198 | static void 199 | kvmap_batch_seek_skip(const struct forker_worker_info * const info, 200 | const struct priv * const priv, const u64 nr) 201 | { 202 | const struct kvmap_api * const api = info->passdata[0]; 203 | void * const ref = priv->ref; 204 | void * const iter = api->iter_create(ref); 205 | const u32 nscan = priv->nscan; 206 | debug_assert(iter); 207 | struct rgen * const gen = info->gen; 208 | rgen_next_func next = info->rgen_next; 209 | 210 | for (u64 i = 0; i < nr; i++) { 211 | kv_refill_hex64_klen(priv->tmp, next(gen), priv->klen, NULL, 0); 212 | const u64 t0 = time_nsec(); 213 | kvmap_kv_iter_seek(api, iter, priv->tmp); 214 | api->iter_skip(iter, nscan); 215 | const u64 dt = time_diff_nsec(t0); 216 | latency_add(info->vctr, dt); 217 | } 218 | api->iter_destroy(iter); 219 | } 220 | 221 | static void * 222 | kvmap_worker(void * const ptr) 223 | { 224 | struct forker_worker_info * const info = (typeof(info))ptr; 225 | srandom_u64(info->seed); 226 | 227 | const char op = info->argv[0][0]; 228 | typeof(kvmap_batch_pro) * batch_func = NULL; 229 | switch (op) { 230 | case 'p': batch_func = kvmap_batch_pro; break; 231 | case 'g': batch_func = kvmap_batch_get; break; 232 | case 's': batch_func = kvmap_batch_put; break; 233 | case 'S': batch_func = kvmap_batch_put_par; break; 234 | case 'd': batch_func = kvmap_batch_del; break; 235 | case 'n': batch_func = kvmap_batch_seek_next; break; 236 | case 'k': batch_func = kvmap_batch_seek_skip; break; 237 | default: debug_die(); break; 238 | } 239 | 240 | struct priv p; 241 | p.klen = a2u32(info->argv[1]); 242 | p.vlen = a2u32(info->argv[2]); // vlen/nscan 243 | const struct kvmap_api * const api = info->passdata[0]; 244 | p.ref = kvmap_ref(api, info->passdata[1]); 245 | const u64 outlen = sizeof(struct kv) + p.klen + p.vlen + 4096; 246 | p.tmp = yalloc(outlen); 247 | debug_assert(p.tmp); 248 | memset(p.tmp, 0, outlen); 249 | p.out = yalloc(outlen); 250 | debug_assert(p.out); 251 | if (info->end_type == FORKER_END_TIME) { 252 | do { 253 | batch_func(info, &p, 1lu << 14); // batch size 254 | } while (time_nsec() < info->end_magic); 255 | } else if (info->end_type == FORKER_END_COUNT) { 256 | batch_func(info, &p, info->end_magic); 257 | } 258 | kvmap_unref(api, p.ref); 259 | free(p.out); 260 | free(p.tmp); 261 | return NULL; 262 | } 263 | 264 | #define NARGS ((3)) 265 | static void 266 | dbtest_help_message(void) 267 | { 268 | fprintf(stderr, "%s Usage: {api ... {rgen ... {pass ...}}}\n", __func__); 269 | kvmap_api_helper_message(); 270 | forker_passes_message(); 271 | fprintf(stderr, "%s dbtest wargs[%d]: \n", __func__, NARGS); 272 | fprintf(stderr, "%s s:set S:load d:del g:get p:probe n:seeknext k:seekskip\n", __func__); 273 | } 274 | 275 | static int 276 | test_kvmap(const int argc, char ** const argv) 277 | { 278 | const struct kvmap_api * api = NULL; 279 | void * map = NULL; 280 | const int n1 = kvmap_api_helper(argc, argv, NULL, &api, &map); 281 | if (n1 < 0) 282 | return n1; 283 | 284 | char *pref[64] = {}; 285 | memcpy(pref, argv, sizeof(pref[0]) * (size_t)n1); 286 | 287 | struct pass_info pi = {}; 288 | pi.passdata[0] = (void *)api; 289 | pi.passdata[1] = map; 290 | pi.vctr_size = VCTRSZ; 291 | pi.wf = kvmap_worker; 292 | pi.af = kvmap_analyze; 293 | const int n2 = forker_passes(argc - n1, argv + n1, pref, &pi, NARGS); 294 | 295 | if (api->fprint) 296 | api->fprint(map, stderr); 297 | 298 | api->destroy(map); 299 | if (n2 < 0) { 300 | return n2; 301 | } else { 302 | return n1 + n2; 303 | } 304 | } 305 | 306 | int 307 | main(int argc, char ** argv) 308 | { 309 | if (argc < 3) { 310 | dbtest_help_message(); 311 | exit(0); 312 | } 313 | 314 | const bool r = forker_main(argc - 1, argv + 1, test_kvmap); 315 | if (r == false) 316 | dbtest_help_message(); 317 | return 0; 318 | } 319 | -------------------------------------------------------------------------------- /c/dbtest1.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016--2021 Wu, Xingbo 3 | * 4 | * All rights reserved. No warranty, explicit or implicit, provided. 5 | */ 6 | #define _GNU_SOURCE 7 | 8 | #include "lib.h" 9 | #include "kv.h" 10 | 11 | struct priv { 12 | void * ref; 13 | u32 klen; 14 | union { 15 | u32 vlen; 16 | u32 nscan; 17 | }; 18 | struct kv * tmp; 19 | struct kv * out; 20 | }; 21 | 22 | #define XSA ((0)) // Set-All 23 | #define XSS ((1)) // Set-Success 24 | #define XDA ((2)) 25 | #define XDS ((3)) 26 | #define XGA ((4)) 27 | #define XGS ((5)) 28 | #define XPA ((6)) 29 | #define XPS ((7)) 30 | #define XNA ((8)) 31 | #define XNS ((9)) 32 | #define XKA ((10)) 33 | #define XKS ((11)) 34 | #define VCTRSZ ((12)) 35 | 36 | static bool 37 | kvmap_analyze(void * const passdata[2], const u64 dt, const struct vctr * const va, struct damp * const d, char * const out) 38 | { 39 | (void)passdata; 40 | size_t v[VCTRSZ]; 41 | for (u64 i = 0; i < VCTRSZ; i++) 42 | v[i] = vctr_get(va, i); 43 | 44 | const u64 nrop = v[XSA] + v[XDA] + v[XGA] + v[XPA] + v[XNA] + v[XKA]; 45 | const double mops = ((double)nrop) * 1e3 / ((double)dt); 46 | const bool done = damp_add_test(d, mops); 47 | char buf[64]; 48 | if (v[XSA]) { 49 | sprintf(buf, " set %zu %zu", v[XSA], v[XSS]); 50 | } else if (v[XDA]) { 51 | sprintf(buf, " del %zu %zu", v[XDA], v[XDS]); 52 | } else if (v[XGA]) { 53 | sprintf(buf, " get %zu %zu", v[XGA], v[XGS]); 54 | } else if (v[XPA]) { 55 | sprintf(buf, " pro %zu %zu", v[XPA], v[XPS]); 56 | } else if (v[XNA]) { 57 | sprintf(buf, " seeknext %zu %zu", v[XNA], v[XNS]); 58 | } else if (v[XKA]) { 59 | sprintf(buf, " seekskip %zu %zu", v[XKA], v[XKS]); 60 | } else { 61 | buf[0] = '\0'; 62 | } 63 | sprintf(out, "%s mops %.4lf avg %.4lf ravg %.4lf\n", buf, mops, damp_avg(d), damp_ravg(d)); 64 | return done; 65 | } 66 | 67 | static void 68 | kvmap_batch_nop(const struct forker_worker_info * const info, 69 | const struct priv * const priv, const u64 nr) 70 | { 71 | (void)info; 72 | (void)priv; 73 | for (u64 i = 0; i < nr; i++) 74 | cpu_pause(); 75 | } 76 | 77 | // (parallel) load 78 | static void 79 | kvmap_batch_put_par(const struct forker_worker_info * const info, 80 | const struct priv * const priv, const u64 nr) 81 | { 82 | if (info->end_type != FORKER_END_COUNT) 83 | return; 84 | 85 | const struct kvmap_api * const api = info->passdata[0]; 86 | void * const ref = priv->ref; 87 | struct kv * const tmp = priv->tmp; 88 | const u64 nr1 = nr / info->conc; 89 | const u64 id0 = nr1 * info->worker_id; 90 | const u64 end = (info->worker_id == (info->conc - 1)) ? nr : (id0 + nr1); 91 | u64 ss = 0; 92 | for (u64 i = id0; i < end; i++) { 93 | kv_refill_hex64_klen(tmp, i, priv->klen, NULL, 0); 94 | tmp->vlen = priv->vlen; 95 | if (kvmap_kv_put(api, ref, tmp)) 96 | ss++; 97 | } 98 | vctr_add(info->vctr, XSA, end - id0); 99 | vctr_add(info->vctr, XSS, ss); 100 | } 101 | 102 | // (parallel) probe 103 | static void 104 | kvmap_batch_probe_par(const struct forker_worker_info * const info, 105 | const struct priv * const priv, const u64 nr) 106 | { 107 | if (info->end_type != FORKER_END_COUNT) 108 | return; 109 | 110 | const struct kvmap_api * const api = info->passdata[0]; 111 | void * const ref = priv->ref; 112 | struct kv * const tmp = priv->tmp; 113 | const u64 nr1 = nr / info->conc; 114 | const u64 id0 = nr1 * info->worker_id; 115 | const u64 end = (info->worker_id == (info->conc - 1)) ? nr : (id0 + nr1); 116 | u64 ss = 0; 117 | for (u64 i = id0; i < end; i++) { 118 | kv_refill_hex64_klen(tmp, i, priv->klen, NULL, 0); 119 | if (kvmap_kv_probe(api, ref, tmp)) 120 | ss++; 121 | } 122 | vctr_add(info->vctr, XPA, end - id0); 123 | vctr_add(info->vctr, XPS, ss); 124 | } 125 | 126 | // (parallel) get 127 | static void 128 | kvmap_batch_get_par(const struct forker_worker_info * const info, 129 | const struct priv * const priv, const u64 nr) 130 | { 131 | if (info->end_type != FORKER_END_COUNT) 132 | return; 133 | 134 | const struct kvmap_api * const api = info->passdata[0]; 135 | void * const ref = priv->ref; 136 | struct kv * const tmp = priv->tmp; 137 | const u64 nr1 = nr / info->conc; 138 | const u64 id0 = nr1 * info->worker_id; 139 | const u64 end = (info->worker_id == (info->conc - 1)) ? nr : (id0 + nr1); 140 | u64 ss = 0; 141 | for (u64 i = id0; i < end; i++) { 142 | kv_refill_hex64_klen(tmp, i, priv->klen, NULL, 0); 143 | if (kvmap_kv_get(api, ref, tmp, priv->out)) 144 | ss++; 145 | } 146 | vctr_add(info->vctr, XGA, end - id0); 147 | vctr_add(info->vctr, XGS, ss); 148 | } 149 | 150 | // (parallel) del 151 | static void 152 | kvmap_batch_del_par(const struct forker_worker_info * const info, 153 | const struct priv * const priv, const u64 nr) 154 | { 155 | if (info->end_type != FORKER_END_COUNT) 156 | return; 157 | 158 | const struct kvmap_api * const api = info->passdata[0]; 159 | void * const ref = priv->ref; 160 | struct kv * const tmp = priv->tmp; 161 | const u64 nr1 = nr / info->conc; 162 | const u64 id0 = nr1 * info->worker_id; 163 | const u64 end = (info->worker_id == (info->conc - 1)) ? nr : (id0 + nr1); 164 | u64 ss = 0; 165 | for (u64 i = id0; i < end; i++) { 166 | kv_refill_hex64_klen(tmp, i, priv->klen, NULL, 0); 167 | if (kvmap_kv_del(api, ref, tmp)) 168 | ss++; 169 | } 170 | vctr_add(info->vctr, XDA, end - id0); 171 | vctr_add(info->vctr, XDS, ss); 172 | } 173 | 174 | static void 175 | kvmap_batch_put(const struct forker_worker_info * const info, 176 | const struct priv * const priv, const u64 nr) 177 | { 178 | const struct kvmap_api * const api = info->passdata[0]; 179 | void * const ref = priv->ref; 180 | struct rgen * const gen = info->gen; 181 | rgen_next_func next = info->rgen_next_write; 182 | struct kv * const tmp = priv->tmp; 183 | u64 ss = 0lu; 184 | 185 | for (u64 i = 0; i < nr; i++) { 186 | kv_refill_hex64_klen(tmp, next(gen), priv->klen, NULL, 0); 187 | tmp->vlen = priv->vlen; 188 | if (kvmap_kv_put(api, ref, tmp)) 189 | ss++; 190 | } 191 | vctr_add(info->vctr, XSA, nr); 192 | vctr_add(info->vctr, XSS, ss); 193 | } 194 | 195 | static void 196 | kvmap_batch_del(const struct forker_worker_info * const info, 197 | const struct priv * const priv, const u64 nr) 198 | { 199 | const struct kvmap_api * const api = info->passdata[0]; 200 | void * const ref = priv->ref; 201 | struct rgen * const gen = info->gen; 202 | rgen_next_func next = info->rgen_next_write; 203 | struct kv * const tmp = priv->tmp; 204 | u64 ss = 0lu; 205 | 206 | for (u64 i = 0; i < nr; i++) { 207 | kv_refill_hex64_klen(tmp, next(gen), priv->klen, NULL, 0); 208 | if (kvmap_kv_del(api, ref, tmp)) 209 | ss++; 210 | } 211 | vctr_add(info->vctr, XDA, nr); 212 | vctr_add(info->vctr, XDS, ss); 213 | } 214 | 215 | static void 216 | kvmap_batch_get(const struct forker_worker_info * const info, 217 | const struct priv * const priv, const u64 nr) 218 | { 219 | const struct kvmap_api * const api = info->passdata[0]; 220 | void * const ref = priv->ref; 221 | struct rgen * const gen = info->gen; 222 | rgen_next_func next = info->rgen_next; 223 | struct kv * const tmp = priv->tmp; 224 | u64 ss = 0lu; 225 | 226 | for (u64 i = 0; i < nr; i++) { 227 | kv_refill_hex64_klen(tmp, next(gen), priv->klen, NULL, 0); 228 | if (kvmap_kv_get(api, ref, tmp, priv->out)) 229 | ss++; 230 | } 231 | vctr_add(info->vctr, XGA, nr); 232 | vctr_add(info->vctr, XGS, ss); 233 | } 234 | 235 | static void 236 | kvmap_batch_probe(const struct forker_worker_info * const info, 237 | const struct priv * const priv, const u64 nr) 238 | { 239 | const struct kvmap_api * const api = info->passdata[0]; 240 | void * const ref = priv->ref; 241 | struct rgen * const gen = info->gen; 242 | rgen_next_func next = info->rgen_next; 243 | struct kv * const tmp = priv->tmp; 244 | u64 ss = 0lu; 245 | 246 | for (u64 i = 0; i < nr; i++) { 247 | kv_refill_hex64_klen(tmp, next(gen), priv->klen, NULL, 0); 248 | if (kvmap_kv_probe(api, ref, tmp)) 249 | ss++; 250 | } 251 | vctr_add(info->vctr, XPA, nr); 252 | vctr_add(info->vctr, XPS, ss); 253 | } 254 | 255 | static void 256 | kvmap_batch_seek_next(const struct forker_worker_info * const info, 257 | const struct priv * const priv, const u64 nr) 258 | { 259 | const struct kvmap_api * const api = info->passdata[0]; 260 | void * const ref = priv->ref; 261 | void * const iter = api->iter_create(ref); 262 | const u32 nscan = priv->nscan; 263 | debug_assert(iter); 264 | struct rgen * const gen = info->gen; 265 | rgen_next_func next = info->rgen_next; 266 | u64 ss = 0lu; 267 | 268 | for (u64 i = 0; i < nr; i++) { 269 | kv_refill_hex64_klen(priv->tmp, next(gen), priv->klen, NULL, 0); 270 | kvmap_kv_iter_seek(api, iter, priv->tmp); 271 | for (u32 j = 0; j < nscan; j++) 272 | api->iter_next(iter, priv->out); 273 | if (api->iter_valid(iter)) 274 | ss++; 275 | } 276 | vctr_add(info->vctr, XNA, nr); 277 | vctr_add(info->vctr, XNS, ss); 278 | api->iter_destroy(iter); 279 | } 280 | 281 | static void 282 | kvmap_batch_seek_skip(const struct forker_worker_info * const info, 283 | const struct priv * const priv, const u64 nr) 284 | { 285 | const struct kvmap_api * const api = info->passdata[0]; 286 | void * const ref = priv->ref; 287 | void * const iter = api->iter_create(ref); 288 | const u32 nscan = priv->nscan; 289 | debug_assert(iter); 290 | struct rgen * const gen = info->gen; 291 | rgen_next_func next = info->rgen_next; 292 | u64 ss = 0lu; 293 | 294 | for (u64 i = 0; i < nr; i++) { 295 | kv_refill_hex64_klen(priv->tmp, next(gen), priv->klen, NULL, 0); 296 | kvmap_kv_iter_seek(api, iter, priv->tmp); 297 | api->iter_skip(iter, nscan); 298 | if (api->iter_peek(iter, priv->out)) 299 | ss++; 300 | } 301 | vctr_add(info->vctr, XKA, nr); 302 | vctr_add(info->vctr, XKS, ss); 303 | api->iter_destroy(iter); 304 | } 305 | 306 | static void * 307 | kvmap_worker(void * const ptr) 308 | { 309 | struct forker_worker_info * const info = (typeof(info))ptr; 310 | srandom_u64(info->seed); 311 | 312 | const char op = info->argv[0][0]; 313 | typeof(kvmap_batch_probe) * batch_func = NULL; 314 | switch (op) { 315 | case 's': batch_func = kvmap_batch_put; break; 316 | case 'd': batch_func = kvmap_batch_del; break; 317 | case 'p': batch_func = kvmap_batch_probe; break; 318 | case 'g': batch_func = kvmap_batch_get; break; 319 | case 'n': batch_func = kvmap_batch_seek_next; break; 320 | case 'k': batch_func = kvmap_batch_seek_skip; break; 321 | case 'S': batch_func = kvmap_batch_put_par; break; 322 | case 'D': batch_func = kvmap_batch_del_par; break; 323 | case 'P': batch_func = kvmap_batch_probe_par; break; 324 | case 'G': batch_func = kvmap_batch_get_par; break; 325 | default: batch_func = kvmap_batch_nop; break; 326 | } 327 | 328 | struct priv p; 329 | p.klen = a2u32(info->argv[1]); 330 | p.vlen = a2u32(info->argv[2]); // vlen/nscan 331 | const struct kvmap_api * const api = info->passdata[0]; 332 | p.ref = kvmap_ref(api, info->passdata[1]); 333 | const u64 outlen = sizeof(struct kv) + p.klen + p.vlen + 4096; 334 | p.tmp = yalloc(outlen); 335 | debug_assert(p.tmp); 336 | memset(p.tmp, 0, outlen); 337 | p.out = yalloc(outlen); 338 | debug_assert(p.out); 339 | if (info->end_type == FORKER_END_TIME) { 340 | do { 341 | batch_func(info, &p, 1lu << 14); // batch size 342 | } while (time_nsec() < info->end_magic); 343 | } else if (info->end_type == FORKER_END_COUNT) { 344 | batch_func(info, &p, info->end_magic); 345 | } 346 | kvmap_unref(api, p.ref); 347 | free(p.out); 348 | free(p.tmp); 349 | return NULL; 350 | } 351 | 352 | #define NARGS ((3)) 353 | static void 354 | dbtest_help_message(void) 355 | { 356 | fprintf(stderr, "%s Usage: {api ... {rgen ... {pass ...}}}\n", __func__); 357 | kvmap_api_helper_message(); 358 | forker_passes_message(); 359 | fprintf(stderr, "%s dbtest wargs[%d]: \n", __func__, NARGS); 360 | fprintf(stderr, "%s s:set d:del g:get p:probe n:seeknext k:seekskip\n", __func__); 361 | fprintf(stderr, "%s S:set D:del G:get P:probe (auto-parallel: magic-type=1; magic=nr_kvs; rgen ignored)\n", __func__); 362 | } 363 | 364 | static int 365 | test_kvmap(const int argc, char ** const argv) 366 | { 367 | const struct kvmap_api * api = NULL; 368 | void * map = NULL; 369 | const int n1 = kvmap_api_helper(argc, argv, NULL, &api, &map); 370 | if (n1 < 0) 371 | return n1; 372 | 373 | char *pref[64] = {}; 374 | memcpy(pref, argv, sizeof(pref[0]) * (size_t)n1); 375 | pref[n1] = NULL; 376 | 377 | struct pass_info pi = {}; 378 | pi.passdata[0] = (void *)api; 379 | pi.passdata[1] = map; 380 | pi.vctr_size = VCTRSZ; 381 | pi.wf = kvmap_worker; 382 | pi.af = kvmap_analyze; 383 | const int n2 = forker_passes(argc - n1, argv + n1, pref, &pi, NARGS); 384 | 385 | if (api->fprint) 386 | api->fprint(map, stderr); 387 | 388 | api->destroy(map); 389 | if (n2 < 0) { 390 | return n2; 391 | } else { 392 | return n1 + n2; 393 | } 394 | } 395 | 396 | int 397 | main(int argc, char ** argv) 398 | { 399 | if (argc < 3) { 400 | dbtest_help_message(); 401 | exit(0); 402 | } 403 | 404 | const bool r = forker_main(argc - 1, argv + 1, test_kvmap); 405 | if (r == false) 406 | dbtest_help_message(); 407 | return 0; 408 | } 409 | -------------------------------------------------------------------------------- /c/kv.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016--2021 Wu, Xingbo 3 | * 4 | * All rights reserved. No warranty, explicit or implicit, provided. 5 | */ 6 | #pragma once 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | // crc32c {{{ 13 | #define KV_CRC32C_SEED ((0xDEADBEEFu)) 14 | 15 | extern u32 16 | kv_crc32c(const void * const ptr, u32 len); 17 | 18 | extern u64 19 | kv_crc32c_extend(const u32 crc32c); 20 | // }}} crc32c 21 | 22 | // kv {{{ 23 | 24 | // struct {{{ 25 | /* 26 | * Some internal union names can be ignored: 27 | * struct kv { 28 | * u32 klen; 29 | * u32 vlen; 30 | * u64 hash; 31 | * u8 kv[]; 32 | * }; 33 | */ 34 | struct kv { 35 | union { // the first u64 36 | u64 kvlen; 37 | struct { 38 | u32 klen; 39 | union { u32 vlen; u32 refcnt; }; 40 | }; 41 | }; 42 | union { 43 | u64 hash; // hashvalue of the key 44 | u64 priv; // can hide a value here if hash is not used 45 | void * privptr; 46 | struct { u32 hashlo; u32 hashhi; }; // little endian 47 | struct { u32 privlo; u32 privhi; }; 48 | }; 49 | u8 kv[0]; // len(kv) == klen + vlen 50 | } __attribute__((packed)); 51 | 52 | struct kref { 53 | u32 len; 54 | union { u32 hash32; u32 priv; }; 55 | const u8 * ptr; 56 | } __attribute__((packed)); 57 | 58 | struct kvref { 59 | const u8 * kptr; // read-only 60 | const u8 * vptr; // read-only 61 | struct kv hdr; // hdr.kv[] is invalid 62 | }; 63 | // }}} struct 64 | 65 | // kv {{{ 66 | typedef int (*kv_kv_cmp_func)(const struct kv *, const struct kv *); 67 | 68 | extern size_t 69 | kv_size(const struct kv * const kv); 70 | 71 | extern size_t 72 | kv_size_align(const struct kv * const kv, const u64 align); 73 | 74 | extern size_t 75 | key_size(const struct kv * const key); 76 | 77 | extern size_t 78 | key_size_align(const struct kv * const key, const u64 align); 79 | 80 | extern void 81 | kv_update_hash(struct kv * const kv); 82 | 83 | extern void 84 | kv_refill_value(struct kv * const kv, const void * const value, const u32 vlen); 85 | 86 | extern void 87 | kv_refill(struct kv * const kv, const void * const key, const u32 klen, 88 | const void * const value, const u32 vlen); 89 | 90 | extern void 91 | kv_refill_str(struct kv * const kv, const char * const key, 92 | const void * const value, const u32 vlen); 93 | 94 | extern void 95 | kv_refill_str_str(struct kv * const kv, const char * const key, 96 | const char * const value); 97 | 98 | // the u64 key is filled in big-endian byte order 99 | extern void 100 | kv_refill_u64(struct kv * const kv, const u64 key, const void * const value, const u32 vlen); 101 | 102 | extern void 103 | kv_refill_hex32(struct kv * const kv, const u32 hex, const void * const value, const u32 vlen); 104 | 105 | extern void 106 | kv_refill_hex64(struct kv * const kv, const u64 hex, const void * const value, const u32 vlen); 107 | 108 | extern void 109 | kv_refill_hex64_klen(struct kv * const kv, const u64 hex, const u32 klen, 110 | const void * const value, const u32 vlen); 111 | 112 | extern void 113 | kv_refill_kref(struct kv * const kv, const struct kref * const kref); 114 | 115 | extern void 116 | kv_refill_kref_v(struct kv * const kv, const struct kref * const kref, 117 | const void * const value, const u32 vlen); 118 | 119 | extern struct kref 120 | kv_kref(const struct kv * const key); 121 | 122 | extern struct kv * 123 | kv_create(const void * const key, const u32 klen, const void * const value, const u32 vlen); 124 | 125 | extern struct kv * 126 | kv_create_str(const char * const key, const void * const value, const u32 vlen); 127 | 128 | extern struct kv * 129 | kv_create_str_str(const char * const key, const char * const value); 130 | 131 | extern struct kv * 132 | kv_create_kref(const struct kref * const kref, const void * const value, const u32 vlen); 133 | 134 | // a static kv with klen == 0 135 | extern const struct kv * 136 | kv_null(void); 137 | 138 | extern struct kv * 139 | kv_dup(const struct kv * const kv); 140 | 141 | extern struct kv * 142 | kv_dup_key(const struct kv * const kv); 143 | 144 | extern struct kv * 145 | kv_dup2(const struct kv * const from, struct kv * const to); 146 | 147 | extern struct kv * 148 | kv_dup2_key(const struct kv * const from, struct kv * const to); 149 | 150 | extern struct kv * 151 | kv_dup2_key_prefix(const struct kv * const from, struct kv * const to, const u32 plen); 152 | 153 | extern bool 154 | kv_match(const struct kv * const key1, const struct kv * const key2); 155 | 156 | extern bool 157 | kv_match_hash(const struct kv * const key1, const struct kv * const key2); 158 | 159 | extern bool 160 | kv_match_full(const struct kv * const kv1, const struct kv * const kv2); 161 | 162 | extern bool 163 | kv_match_kv128(const struct kv * const sk, const u8 * const kv128); 164 | 165 | extern int 166 | kv_compare(const struct kv * const kv1, const struct kv * const kv2); 167 | 168 | extern int 169 | kv_k128_compare(const struct kv * const sk, const u8 * const k128); 170 | 171 | extern int 172 | kv_kv128_compare(const struct kv * const sk, const u8 * const kv128); 173 | 174 | extern void 175 | kv_qsort(struct kv ** const kvs, const size_t nr); 176 | 177 | extern u32 178 | kv_key_lcp(const struct kv * const key1, const struct kv * const key2); 179 | 180 | extern u32 181 | kv_key_lcp_skip(const struct kv * const key1, const struct kv * const key2, const u32 lcp0); 182 | 183 | extern void 184 | kv_psort(struct kv ** const kvs, const u64 nr, const u64 tlo, const u64 thi); 185 | 186 | extern void * 187 | kv_vptr(struct kv * const kv); 188 | 189 | extern void * 190 | kv_kptr(struct kv * const kv); 191 | 192 | extern const void * 193 | kv_vptr_c(const struct kv * const kv); 194 | 195 | extern const void * 196 | kv_kptr_c(const struct kv * const kv); 197 | 198 | extern void 199 | kv_print(const struct kv * const kv, const char * const cmd, FILE * const out); 200 | // }}} kv 201 | 202 | // mm {{{ 203 | typedef struct kv * (* kvmap_mm_in_func)(struct kv * kv, void * priv); 204 | typedef struct kv * (* kvmap_mm_out_func)(struct kv * kv, struct kv * out); 205 | typedef void (* kvmap_mm_free_func)(struct kv * kv, void * priv); 206 | 207 | // manage internal kv data of kvmap 208 | struct kvmap_mm { 209 | // to create a private copy of "kv" 210 | // see put() functions 211 | kvmap_mm_in_func in; 212 | // to duplicate a private copy of "kv" to "out" 213 | // see get() and iter_peek() functions 214 | kvmap_mm_out_func out; 215 | // to free a kv 216 | // see del() and put() functions 217 | kvmap_mm_free_func free; 218 | void * priv; 219 | }; 220 | 221 | extern struct kv * 222 | kvmap_mm_in_noop(struct kv * const kv, void * const priv); 223 | 224 | extern struct kv * 225 | kvmap_mm_out_noop(struct kv * const kv, struct kv * const out); 226 | 227 | extern void 228 | kvmap_mm_free_noop(struct kv * const kv, void * const priv); 229 | 230 | extern struct kv * 231 | kvmap_mm_in_dup(struct kv * const kv, void * const priv); 232 | 233 | extern struct kv * 234 | kvmap_mm_out_dup(struct kv * const kv, struct kv * const out); 235 | 236 | extern void 237 | kvmap_mm_free_free(struct kv * const kv, void * const priv); 238 | 239 | // the default mm 240 | extern const struct kvmap_mm kvmap_mm_dup; // in:Dup, out:Dup, free:Free 241 | extern const struct kvmap_mm kvmap_mm_ndf; // in:Noop, out:Dup, free:Free 242 | // }}} mm 243 | 244 | // ref {{{ 245 | typedef int (*kref_kv_cmp_func)(const struct kref *, const struct kv *); 246 | 247 | // ptr and len only 248 | extern void 249 | kref_ref_raw(struct kref * const kref, const u8 * const ptr, const u32 len); 250 | 251 | // this calculates hash32 252 | extern void 253 | kref_ref_hash32(struct kref * const kref, const u8 * const ptr, const u32 len); 254 | 255 | extern void 256 | kref_update_hash32(struct kref * const kref); 257 | 258 | extern void 259 | kref_ref_kv(struct kref * const kref, const struct kv * const kv); 260 | 261 | extern void 262 | kref_ref_kv_hash32(struct kref * const kref, const struct kv * const kv); 263 | 264 | extern bool 265 | kref_match(const struct kref * const k1, const struct kref * const k2); 266 | 267 | extern bool 268 | kref_kv_match(const struct kref * const kref, const struct kv * const k); 269 | 270 | extern int 271 | kref_compare(const struct kref * const kref1, const struct kref * const kref2); 272 | 273 | extern int 274 | kref_kv_compare(const struct kref * const kref, const struct kv * const k); 275 | 276 | extern u32 277 | kref_lcp(const struct kref * const k1, const struct kref * const k2); 278 | 279 | extern u32 280 | kref_kv_lcp(const struct kref * const kref, const struct kv * const kv); 281 | 282 | extern int 283 | kref_k128_compare(const struct kref * const sk, const u8 * const k128); 284 | 285 | extern int 286 | kref_kv128_compare(const struct kref * const sk, const u8 * const kv128); 287 | 288 | extern const struct kref * 289 | kref_null(void); 290 | 291 | extern void 292 | kvref_ref_kv(struct kvref * const ref, struct kv * const kv); 293 | 294 | extern struct kv * 295 | kvref_dup2_kv(struct kvref * const ref, struct kv * const to); 296 | 297 | extern struct kv * 298 | kvref_dup2_key(struct kvref * const ref, struct kv * const to); 299 | 300 | extern int 301 | kvref_kv_compare(const struct kvref * const ref, const struct kv * const kv); 302 | // }}} ref 303 | 304 | // kv128 {{{ 305 | extern size_t 306 | kv128_estimate_kv(const struct kv * const kv); 307 | 308 | extern u8 * 309 | kv128_encode_kv(const struct kv * const kv, u8 * const out, size_t * const pesize); 310 | 311 | extern struct kv * 312 | kv128_decode_kv(const u8 * const ptr, struct kv * const out, size_t * const pesize); 313 | 314 | extern size_t 315 | kv128_size(const u8 * const ptr); 316 | // }}} kv128 317 | 318 | // }}} kv 319 | 320 | // kvmap {{{ 321 | 322 | // kvmap_api {{{ 323 | typedef void (* kv_inp_func)(struct kv * const curr, void * const priv); 324 | 325 | // the merge function should: 326 | // 1: return NULL if the origin kv is not changed at all 327 | // 2: return kv0 if updates has been applied in-place 328 | // 3: return a different kv if the original kv must be replaced 329 | // In an in-memory kvmap, 2==1 and no further action is needed 330 | // In a persistent kv store with a memtable, 2 will need an insertion if kv0 is not from the memtable 331 | typedef struct kv * (* kv_merge_func)(struct kv * const kv0, void * const priv); 332 | 333 | struct kvmap_api { 334 | // feature bits 335 | bool hashkey; // true: caller needs to provide correct hash in kv/kref 336 | bool ordered; // true: has iter_seek 337 | bool threadsafe; // true: support thread_safe access 338 | bool readonly; // true: no put() and del() 339 | bool irefsafe; // true: iter's kref/kvref can be safely accessed after iter_seek/iter_skip/iter_park 340 | bool unique; // provide unique keys, especially for iterators 341 | bool refpark; // ref has park() and resume() 342 | bool async; // XXX for testing KVell 343 | 344 | // put (aka put/upsert): return true on success; false on error 345 | // mm.in() controls how things move into the kvmap; the default mm make a copy with malloc() 346 | // mm.free() controls how old kv get disposed when replaced 347 | bool (* put) (void * const ref, struct kv * const kv); 348 | // get: search and return a kv if found, or NULL if not 349 | // with the default mm: malloc() if out == NULL; otherwise, use out as buffer 350 | // with custom kvmap_mm: mm.out() controls buffer; use with caution 351 | // caller should use the returned ptr even if out is provided 352 | struct kv * (* get) (void * const ref, const struct kref * const key, struct kv * const out); 353 | // probe: return true on found, false on not found 354 | bool (* probe) (void * const ref, const struct kref * const key); 355 | // del: return true on something deleted, false on not found 356 | // mm.free() controls how old kv get disposed when replaced 357 | bool (* del) (void * const ref, const struct kref * const key); 358 | // inp: inplace operation if key exists; otherwise return false; uf() is always executed even with NULL key 359 | // inpr/inpw acquires r/w locks respectively. 360 | // Note that in inpw() you can only change the value. 361 | bool (* inpr) (void * const ref, const struct kref * const key, kv_inp_func uf, void * const priv); 362 | bool (* inpw) (void * const ref, const struct kref * const key, kv_inp_func uf, void * const priv); 363 | // merge: put+callback on old/new keys; another name: read-modify-write 364 | // return true if successfull; return false on error 365 | bool (* merge) (void * const ref, const struct kref * const key, kv_merge_func uf, void * const priv); 366 | // delete-range: delete all keys from start (inclusive) to end (exclusive) 367 | u64 (* delr) (void * const ref, const struct kref * const start, const struct kref * const end); 368 | // make everything persist; for persistent maps only 369 | void (* sync) (void * const ref); 370 | 371 | // general guidelines for thread-safe iters: 372 | // - it is assumed that the key under the cursor is locked/freezed/immutable 373 | // - once created one must call iter_seek to make it valid 374 | // - the ownership of ref is given to the iter so ref should not be used until iter_destroy 375 | // - creating and use more than one iter based on a ref can cause deadlocks 376 | void * (* iter_create) (void * const ref); 377 | // move the cursor to the first key >= search-key; 378 | void (* iter_seek) (void * const iter, const struct kref * const key); 379 | // check if the cursor points to a valid key 380 | bool (* iter_valid) (void * const iter); 381 | // return the current key; copy to out if (out != NULL) 382 | // mm.out() controls copy-out 383 | struct kv * (* iter_peek) (void * const iter, struct kv * const out); 384 | // similar to peek but does not copy; return false if iter is invalid 385 | bool (* iter_kref) (void * const iter, struct kref * const kref); 386 | // similar to iter_kref but also provide the value 387 | bool (* iter_kvref) (void * const iter, struct kvref * const kvref); 388 | // iter_retain makes kref or kvref of the current iter remain valid until released 389 | // the returned opaque pointer should be provided when releasing the hold 390 | u64 (* iter_retain) (void * const iter); 391 | void (* iter_release) (void * const iter, const u64 opaque); 392 | // skip one element 393 | void (* iter_skip1) (void * const iter); 394 | // skip nr elements 395 | void (* iter_skip) (void * const iter, const u32 nr); 396 | // iter_next == iter_peek + iter_skip1 397 | struct kv * (* iter_next) (void * const iter, struct kv * const out); 398 | // perform inplace opeation if the current key is valid; return false if no current key 399 | // the uf() is always executed even with NULL key 400 | bool (* iter_inp) (void * const iter, kv_inp_func uf, void * const priv); 401 | // invalidate the iter to release any resources or locks 402 | // afterward, must call seek() again before accessing data 403 | void (* iter_park) (void * const iter); 404 | // destroy iter 405 | void (* iter_destroy) (void * const iter); 406 | 407 | // misc: 408 | // create refs for maps if required; always use use kvmap_ref() and kvmap_unref() 409 | // if there are ref/unref functions, ref-ptr should be used as map for all kv operations 410 | void * (* ref) (void * map); 411 | // return the original map 412 | void * (* unref) (void * ref); 413 | // pause access without unref; must call resume later before access index again 414 | void (* park) (void * ref); 415 | // resume access of ref; must be paired with a park() 416 | void (* resume) (void * ref); 417 | 418 | // UNSAFE functions: 419 | // empty the map 420 | void (* clean) (void * map); 421 | // erase everything 422 | void (* destroy) (void * map); 423 | // for debugging 424 | void (* fprint) (void * map, FILE * const out); 425 | }; 426 | 427 | // registry 428 | struct kvmap_api_reg { 429 | int nargs; // number of arguments after name 430 | const char * name; 431 | const char * args_msg; // see ...helper_message 432 | // multiple apis may share one create function 433 | // arguments: name (e.g., "rdb"), mm (usually NULL), the remaining args 434 | void * (*create)(const char *, const struct kvmap_mm *, char **); 435 | const struct kvmap_api * api; 436 | }; 437 | 438 | // call this function to register a kvmap_api 439 | extern void 440 | kvmap_api_register(const int nargs, const char * const name, const char * const args_msg, 441 | void * (*create)(const char *, const struct kvmap_mm *, char **), const struct kvmap_api * const api); 442 | 443 | extern void 444 | kvmap_api_helper_message(void); 445 | 446 | extern int 447 | kvmap_api_helper(int argc, char ** const argv, const struct kvmap_mm * const mm, 448 | const struct kvmap_api ** const api_out, void ** const map_out); 449 | // }}} kvmap_api 450 | 451 | // helpers {{{ 452 | extern void 453 | kvmap_inp_steal_kv(struct kv * const kv, void * const priv); 454 | 455 | extern void * 456 | kvmap_ref(const struct kvmap_api * const api, void * const map); 457 | 458 | extern void * 459 | kvmap_unref(const struct kvmap_api * const api, void * const ref); 460 | 461 | extern struct kv * 462 | kvmap_kv_get(const struct kvmap_api * const api, void * const ref, 463 | const struct kv * const key, struct kv * const out); 464 | 465 | extern bool 466 | kvmap_kv_probe(const struct kvmap_api * const api, void * const ref, 467 | const struct kv * const key); 468 | 469 | extern bool 470 | kvmap_kv_put(const struct kvmap_api * const api, void * const ref, 471 | struct kv * const kv); 472 | 473 | extern bool 474 | kvmap_kv_del(const struct kvmap_api * const api, void * const ref, 475 | const struct kv * const key); 476 | 477 | extern bool 478 | kvmap_kv_inpr(const struct kvmap_api * const api, void * const ref, 479 | const struct kv * const key, kv_inp_func uf, void * const priv); 480 | 481 | extern bool 482 | kvmap_kv_inpw(const struct kvmap_api * const api, void * const ref, 483 | const struct kv * const key, kv_inp_func uf, void * const priv); 484 | 485 | extern bool 486 | kvmap_kv_merge(const struct kvmap_api * const api, void * const ref, 487 | const struct kv * const key, kv_merge_func uf, void * const priv); 488 | 489 | extern u64 490 | kvmap_kv_delr(const struct kvmap_api * const api, void * const ref, 491 | const struct kv * const start, const struct kv * const end); 492 | 493 | extern void 494 | kvmap_kv_iter_seek(const struct kvmap_api * const api, void * const iter, 495 | const struct kv * const key); 496 | 497 | extern struct kv * 498 | kvmap_raw_get(const struct kvmap_api * const api, void * const ref, 499 | const u32 len, const u8 * const ptr, struct kv * const out); 500 | 501 | extern bool 502 | kvmap_raw_probe(const struct kvmap_api * const api, void * const ref, 503 | const u32 len, const u8 * const ptr); 504 | 505 | extern bool 506 | kvmap_raw_del(const struct kvmap_api * const api, void * const ref, 507 | const u32 len, const u8 * const ptr); 508 | 509 | extern bool 510 | kvmap_raw_inpr(const struct kvmap_api * const api, void * const ref, 511 | const u32 len, const u8 * const ptr, kv_inp_func uf, void * const priv); 512 | 513 | extern bool 514 | kvmap_raw_inpw(const struct kvmap_api * const api, void * const ref, 515 | const u32 len, const u8 * const ptr, kv_inp_func uf, void * const priv); 516 | 517 | extern void 518 | kvmap_raw_iter_seek(const struct kvmap_api * const api, void * const iter, 519 | const u32 len, const u8 * const ptr); 520 | 521 | extern u64 522 | kvmap_dump_keys(const struct kvmap_api * const api, void * const map, const int fd); 523 | // }}} helpers 524 | 525 | // }}} kvmap 526 | 527 | // miter {{{ 528 | // general-purpose merging iterator 529 | // api functions: 530 | // REQUIRED: 531 | // - iter_create 532 | // - iter_seek 533 | // - iter_peek 534 | // - iter_skip 535 | // - iter_destroy 536 | // - iter_kref 537 | // - iter_kvref 538 | // OPTIONAL (api-specific): 539 | // - ref/unref 540 | // - iter_park 541 | // - resume/park (need also set api->refpark) 542 | // OPTIONAL (performance): 543 | // - api->unique (faster miter_skip_unique) 544 | // - iter_retain/iter_release (less memcpy) 545 | 546 | struct miter; 547 | 548 | extern struct miter * 549 | miter_create(void); 550 | 551 | // caller owns the ref and the iter; miter will not destroy them 552 | // using the iter or the ref with an active miter can lead to undefined behavior 553 | extern bool 554 | miter_add_iter(struct miter * const miter, const struct kvmap_api * const api, void * const ref, void * const iter); 555 | 556 | // caller owns the ref; miter will create and destroy the iter 557 | // using the underlying ref with an active miter can lead to undefined behavior 558 | extern void * 559 | miter_add_ref(struct miter * const miter, const struct kvmap_api * const api, void * const ref); 560 | 561 | // miter will take a ref of the map, create an iter, and clean up everything 562 | // be careful of using another ref/iter in the same thread 563 | extern void * 564 | miter_add(struct miter * const miter, const struct kvmap_api * const api, void * const map); 565 | 566 | extern u32 567 | miter_rank(struct miter * const miter); 568 | 569 | extern void 570 | miter_seek(struct miter * const miter, const struct kref * const key); 571 | 572 | extern void 573 | miter_kv_seek(struct miter * const miter, const struct kv * const key); 574 | 575 | extern bool 576 | miter_valid(struct miter * const miter); 577 | 578 | extern struct kv * 579 | miter_peek(struct miter * const miter, struct kv * const out); 580 | 581 | extern bool 582 | miter_kref(struct miter * const miter, struct kref * const kref); 583 | 584 | extern bool 585 | miter_kvref(struct miter * const miter, struct kvref * const kvref); 586 | 587 | extern void 588 | miter_skip1(struct miter * const miter); 589 | 590 | extern void 591 | miter_skip(struct miter * const miter, const u32 nr); 592 | 593 | extern struct kv * 594 | miter_next(struct miter * const miter, struct kv * const out); 595 | 596 | extern void 597 | miter_skip_unique(struct miter * const miter); 598 | 599 | extern struct kv * 600 | miter_next_unique(struct miter * const miter, struct kv * const out); 601 | 602 | extern void 603 | miter_park(struct miter * const miter); 604 | 605 | extern void 606 | miter_clean(struct miter * const miter); 607 | 608 | extern void 609 | miter_destroy(struct miter * const miter); 610 | // }}} miter 611 | 612 | #ifdef __cplusplus 613 | } 614 | #endif 615 | // vim:fdm=marker 616 | -------------------------------------------------------------------------------- /c/lib.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016--2021 Wu, Xingbo 3 | * 4 | * All rights reserved. No warranty, explicit or implicit, provided. 5 | */ 6 | #pragma once 7 | 8 | // includes {{{ 9 | // C headers 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | // POSIX headers 21 | #include 22 | #include 23 | #include 24 | 25 | // Linux headers 26 | #include 27 | #include 28 | #include 29 | #include 30 | 31 | // SIMD 32 | #if defined(__x86_64__) 33 | #include 34 | #elif defined(__aarch64__) 35 | #include 36 | #include 37 | #endif 38 | // }}} includes 39 | 40 | #ifdef __cplusplus 41 | extern "C" { 42 | #endif 43 | 44 | // types {{{ 45 | typedef char s8; 46 | typedef short s16; 47 | typedef int s32; 48 | typedef long s64; 49 | typedef __int128_t s128; 50 | static_assert(sizeof(s8) == 1, "sizeof(s8)"); 51 | static_assert(sizeof(s16) == 2, "sizeof(s16)"); 52 | static_assert(sizeof(s32) == 4, "sizeof(s32)"); 53 | static_assert(sizeof(s64) == 8, "sizeof(s64)"); 54 | static_assert(sizeof(s128) == 16, "sizeof(s128)"); 55 | 56 | typedef unsigned char u8; 57 | typedef unsigned short u16; 58 | typedef unsigned int u32; 59 | typedef unsigned long u64; 60 | typedef __uint128_t u128; 61 | static_assert(sizeof(u8) == 1, "sizeof(u8)"); 62 | static_assert(sizeof(u16) == 2, "sizeof(u16)"); 63 | static_assert(sizeof(u32) == 4, "sizeof(u32)"); 64 | static_assert(sizeof(u64) == 8, "sizeof(u64)"); 65 | static_assert(sizeof(u128) == 16, "sizeof(u128)"); 66 | 67 | #if defined(__x86_64__) 68 | typedef __m128i m128; 69 | #if defined(__AVX2__) 70 | typedef __m256i m256; 71 | #endif // __AVX2__ 72 | #if defined(__AVX512F__) 73 | typedef __m512i m512; 74 | #endif // __AVX512F__ 75 | #elif defined(__aarch64__) 76 | typedef uint8x16_t m128; 77 | #else 78 | #error Need x86_64 or AArch64. 79 | #endif 80 | // }}} types 81 | 82 | // defs {{{ 83 | #define likely(____x____) __builtin_expect(____x____, 1) 84 | #define unlikely(____x____) __builtin_expect(____x____, 0) 85 | 86 | // ansi colors 87 | // 3X:fg; 4X:bg; 9X:light fg; 10X:light bg; 88 | // X can be one of the following colors: 89 | // 0:black; 1:red; 2:green; 3:yellow; 90 | // 4:blue; 5:magenta; 6:cyan; 7:white; 91 | #define TERMCLR(____code____) "\x1b[" #____code____ "m" 92 | // }}} defs 93 | 94 | // const {{{ 95 | #define PGSZ ((4096lu)) 96 | // }}} const 97 | 98 | // math {{{ 99 | extern u64 100 | mhash64(const u64 v); 101 | 102 | extern u32 103 | mhash32(const u32 v); 104 | 105 | extern u64 106 | gcd64(u64 a, u64 b); 107 | // }}} math 108 | 109 | // random {{{ 110 | extern u64 111 | random_u64(void); 112 | 113 | extern void 114 | srandom_u64(const u64 seed); 115 | 116 | extern double 117 | random_double(void); 118 | // }}} random 119 | 120 | // timing {{{ 121 | extern u64 122 | time_nsec(void); 123 | 124 | extern double 125 | time_sec(void); 126 | 127 | extern u64 128 | time_diff_nsec(const u64 last); 129 | 130 | extern double 131 | time_diff_sec(const double last); 132 | 133 | extern void 134 | time_stamp(char * str, const size_t size); 135 | 136 | extern void 137 | time_stamp2(char * str, const size_t size); 138 | // }}} timing 139 | 140 | // cpucache {{{ 141 | extern void 142 | cpu_pause(void); 143 | 144 | extern void 145 | cpu_mfence(void); 146 | 147 | extern void 148 | cpu_cfence(void); 149 | 150 | extern void 151 | cpu_prefetch0(const void * const ptr); 152 | 153 | extern void 154 | cpu_prefetch1(const void * const ptr); 155 | 156 | extern void 157 | cpu_prefetch2(const void * const ptr); 158 | 159 | extern void 160 | cpu_prefetch3(const void * const ptr); 161 | 162 | extern void 163 | cpu_prefetchw(const void * const ptr); 164 | // }}} cpucache 165 | 166 | // crc32c {{{ 167 | extern u32 168 | crc32c_u8(const u32 crc, const u8 v); 169 | 170 | extern u32 171 | crc32c_u16(const u32 crc, const u16 v); 172 | 173 | extern u32 174 | crc32c_u32(const u32 crc, const u32 v); 175 | 176 | extern u32 177 | crc32c_u64(const u32 crc, const u64 v); 178 | 179 | // 1 <= nr <= 3 180 | extern u32 181 | crc32c_inc_123(const u8 * buf, u32 nr, u32 crc); 182 | 183 | // nr % 4 == 0 184 | extern u32 185 | crc32c_inc_x4(const u8 * buf, u32 nr, u32 crc); 186 | 187 | extern u32 188 | crc32c_inc(const u8 * buf, u32 nr, u32 crc); 189 | // }}} crc32c 190 | 191 | // debug {{{ 192 | extern void 193 | debug_break(void); 194 | 195 | extern void 196 | debug_backtrace(void); 197 | 198 | extern void 199 | watch_u64_usr1(u64 * const ptr); 200 | 201 | #ifndef NDEBUG 202 | extern void 203 | debug_assert(const bool v); 204 | #else 205 | #define debug_assert(expr) ((void)0) 206 | #endif 207 | 208 | __attribute__((noreturn)) 209 | extern void 210 | debug_die(void); 211 | 212 | __attribute__((noreturn)) 213 | extern void 214 | debug_die_perror(void); 215 | 216 | extern void 217 | debug_dump_maps(FILE * const out); 218 | 219 | extern bool 220 | debug_perf_switch(void); 221 | // }}} debug 222 | 223 | // mm {{{ 224 | #ifdef ALLOCFAIL 225 | extern bool 226 | alloc_fail(void); 227 | #endif 228 | 229 | extern void * 230 | xalloc(const size_t align, const size_t size); 231 | 232 | extern void * 233 | yalloc(const size_t size); 234 | 235 | extern void ** 236 | malloc_2d(const size_t nr, const size_t size); 237 | 238 | extern void ** 239 | calloc_2d(const size_t nr, const size_t size); 240 | 241 | extern void 242 | pages_unmap(void * const ptr, const size_t size); 243 | 244 | extern void 245 | pages_lock(void * const ptr, const size_t size); 246 | 247 | /* hugepages */ 248 | // force posix allocators: -DVALGRIND_MEMCHECK 249 | extern void * 250 | pages_alloc_4kb(const size_t nr_4kb); 251 | 252 | extern void * 253 | pages_alloc_2mb(const size_t nr_2mb); 254 | 255 | extern void * 256 | pages_alloc_1gb(const size_t nr_1gb); 257 | 258 | extern void * 259 | pages_alloc_best(const size_t size, const bool try_1gb, u64 * const size_out); 260 | // }}} mm 261 | 262 | // process/thread {{{ 263 | extern void 264 | thread_get_name(const pthread_t pt, char * const name, const size_t len); 265 | 266 | extern void 267 | thread_set_name(const pthread_t pt, const char * const name); 268 | 269 | extern long 270 | process_get_rss(void); 271 | 272 | extern u32 273 | process_affinity_count(void); 274 | 275 | extern u32 276 | process_getaffinity_list(const u32 max, u32 * const cores); 277 | 278 | extern void 279 | thread_setaffinity_list(const u32 nr, const u32 * const list); 280 | 281 | extern void 282 | thread_pin(const u32 cpu); 283 | 284 | extern u64 285 | process_cpu_time_usec(void); 286 | 287 | // if args == true, argx is void ** 288 | // if args == false, argx is void * 289 | extern u64 290 | thread_fork_join(u32 nr, void *(*func) (void *), const bool args, void * const argx); 291 | 292 | extern int 293 | thread_create_at(const u32 cpu, pthread_t * const thread, void *(*start_routine) (void *), void * const arg); 294 | // }}} process/thread 295 | 296 | // locking {{{ 297 | typedef union { 298 | u32 opaque; 299 | } spinlock; 300 | 301 | extern void 302 | spinlock_init(spinlock * const lock); 303 | 304 | extern void 305 | spinlock_lock(spinlock * const lock); 306 | 307 | extern bool 308 | spinlock_trylock(spinlock * const lock); 309 | 310 | extern void 311 | spinlock_unlock(spinlock * const lock); 312 | 313 | typedef union { 314 | u32 opaque; 315 | } rwlock; 316 | 317 | extern void 318 | rwlock_init(rwlock * const lock); 319 | 320 | extern bool 321 | rwlock_trylock_read(rwlock * const lock); 322 | 323 | // low-priority reader-lock; use with trylock_write_hp 324 | extern bool 325 | rwlock_trylock_read_lp(rwlock * const lock); 326 | 327 | extern bool 328 | rwlock_trylock_read_nr(rwlock * const lock, u16 nr); 329 | 330 | extern void 331 | rwlock_lock_read(rwlock * const lock); 332 | 333 | extern void 334 | rwlock_unlock_read(rwlock * const lock); 335 | 336 | extern bool 337 | rwlock_trylock_write(rwlock * const lock); 338 | 339 | extern bool 340 | rwlock_trylock_write_nr(rwlock * const lock, u16 nr); 341 | 342 | extern void 343 | rwlock_lock_write(rwlock * const lock); 344 | 345 | // writer has higher priority; new readers are blocked 346 | extern bool 347 | rwlock_trylock_write_hp(rwlock * const lock); 348 | 349 | extern bool 350 | rwlock_trylock_write_hp_nr(rwlock * const lock, u16 nr); 351 | 352 | extern void 353 | rwlock_lock_write_hp(rwlock * const lock); 354 | 355 | extern void 356 | rwlock_unlock_write(rwlock * const lock); 357 | 358 | extern void 359 | rwlock_write_to_read(rwlock * const lock); 360 | 361 | typedef union { 362 | u64 opqaue[8]; 363 | } mutex; 364 | 365 | extern void 366 | mutex_init(mutex * const lock); 367 | 368 | extern void 369 | mutex_lock(mutex * const lock); 370 | 371 | extern bool 372 | mutex_trylock(mutex * const lock); 373 | 374 | extern void 375 | mutex_unlock(mutex * const lock); 376 | 377 | extern void 378 | mutex_deinit(mutex * const lock); 379 | // }}} locking 380 | 381 | // bits {{{ 382 | extern u32 383 | bits_reverse_u32(const u32 v); 384 | 385 | extern u64 386 | bits_reverse_u64(const u64 v); 387 | 388 | extern u64 389 | bits_rotl_u64(const u64 v, const u8 n); 390 | 391 | extern u64 392 | bits_rotr_u64(const u64 v, const u8 n); 393 | 394 | extern u32 395 | bits_rotl_u32(const u32 v, const u8 n); 396 | 397 | extern u32 398 | bits_rotr_u32(const u32 v, const u8 n); 399 | 400 | extern u64 401 | bits_p2_up_u64(const u64 v); 402 | 403 | extern u32 404 | bits_p2_up_u32(const u32 v); 405 | 406 | extern u64 407 | bits_p2_down_u64(const u64 v); 408 | 409 | extern u32 410 | bits_p2_down_u32(const u32 v); 411 | 412 | extern u64 413 | bits_round_up(const u64 v, const u8 power); 414 | 415 | extern u64 416 | bits_round_up_a(const u64 v, const u64 a); 417 | 418 | extern u64 419 | bits_round_down(const u64 v, const u8 power); 420 | 421 | extern u64 422 | bits_round_down_a(const u64 v, const u64 a); 423 | // }}} bits 424 | 425 | // simd {{{ 426 | extern u32 427 | m128_movemask_u8(const m128 v); 428 | 429 | // extern u32 430 | //m128_movemask_u16(const m128 v); 431 | // 432 | // extern u32 433 | //m128_movemask_u32(const m128 v); 434 | // }}} simd 435 | 436 | // vi128 {{{ 437 | extern u32 438 | vi128_estimate_u32(const u32 v); 439 | 440 | extern u8 * 441 | vi128_encode_u32(u8 * dst, u32 v); 442 | 443 | extern const u8 * 444 | vi128_decode_u32(const u8 * src, u32 * const out); 445 | 446 | extern u32 447 | vi128_estimate_u64(const u64 v); 448 | 449 | extern u8 * 450 | vi128_encode_u64(u8 * dst, u64 v); 451 | 452 | extern const u8 * 453 | vi128_decode_u64(const u8 * src, u64 * const out); 454 | // }}} vi128 455 | 456 | // misc {{{ 457 | // TODO: only works on little endian? 458 | struct entry13 { // what a beautiful name 459 | union { 460 | u16 e1; 461 | struct { // easy for debugging 462 | u64 e1_64:16; 463 | u64 e3:48; 464 | }; 465 | u64 v64; 466 | void * ptr; 467 | }; 468 | }; 469 | 470 | static_assert(sizeof(struct entry13) == 8, "sizeof(entry13) != 8"); 471 | 472 | // directly access read .e1 and .e3 473 | // directly write .e1 474 | // use entry13_update() to update the entire entry 475 | 476 | extern struct entry13 477 | entry13(const u16 e1, const u64 e3); 478 | 479 | extern void 480 | entry13_update_e3(struct entry13 * const e, const u64 e3); 481 | 482 | extern void * 483 | u64_to_ptr(const u64 v); 484 | 485 | extern u64 486 | ptr_to_u64(const void * const ptr); 487 | 488 | extern size_t 489 | m_usable_size(void * const ptr); 490 | 491 | extern size_t 492 | fdsize(const int fd); 493 | 494 | extern u32 495 | memlcp(const u8 * const p1, const u8 * const p2, const u32 max); 496 | 497 | __attribute__ ((format (printf, 2, 3))) 498 | extern void 499 | logger_printf(const int fd, const char * const fmt, ...); 500 | // }}} misc 501 | 502 | // bitmap {{{ 503 | struct bitmap; 504 | 505 | extern struct bitmap * 506 | bitmap_create(const u64 nbits); 507 | 508 | extern void 509 | bitmap_init(struct bitmap * const bm, const u64 nbits); 510 | 511 | extern bool 512 | bitmap_test(const struct bitmap * const bm, const u64 idx); 513 | 514 | extern bool 515 | bitmap_test_all1(struct bitmap * const bm); 516 | 517 | extern bool 518 | bitmap_test_all0(struct bitmap * const bm); 519 | 520 | extern void 521 | bitmap_set1(struct bitmap * const bm, const u64 idx); 522 | 523 | extern void 524 | bitmap_set0(struct bitmap * const bm, const u64 idx); 525 | 526 | extern void 527 | bitmap_set1_safe64(struct bitmap * const bm, const u64 idx); 528 | 529 | extern void 530 | bitmap_set0_safe64(struct bitmap * const bm, const u64 idx); 531 | 532 | extern u64 533 | bitmap_count(struct bitmap * const bm); 534 | 535 | extern u64 536 | bitmap_first(struct bitmap * const bm); 537 | 538 | extern void 539 | bitmap_set_all1(struct bitmap * const bm); 540 | 541 | extern void 542 | bitmap_set_all0(struct bitmap * const bm); 543 | // }}} bitmap 544 | 545 | // slab {{{ 546 | struct slab; 547 | 548 | extern struct slab * 549 | slab_create(const u64 obj_size, const u64 blk_size); 550 | 551 | extern bool 552 | slab_reserve_unsafe(struct slab * const slab, const u64 nr); 553 | 554 | extern void * 555 | slab_alloc_unsafe(struct slab * const slab); 556 | 557 | extern void * 558 | slab_alloc_safe(struct slab * const slab); 559 | 560 | extern void 561 | slab_free_unsafe(struct slab * const slab, void * const ptr); 562 | 563 | extern void 564 | slab_free_safe(struct slab * const slab, void * const ptr); 565 | 566 | extern void 567 | slab_free_all(struct slab * const slab); 568 | 569 | extern u64 570 | slab_get_nalloc(struct slab * const slab); 571 | 572 | extern void 573 | slab_destroy(struct slab * const slab); 574 | // }}} slab 575 | 576 | // qsort {{{ 577 | extern int 578 | compare_u16(const void * const p1, const void * const p2); 579 | 580 | extern void 581 | qsort_u16(u16 * const array, const size_t nr); 582 | 583 | extern u16 * 584 | bsearch_u16(const u16 v, const u16 * const array, const size_t nr); 585 | 586 | extern void 587 | shuffle_u16(u16 * const array, const u64 nr); 588 | 589 | extern int 590 | compare_u32(const void * const p1, const void * const p2); 591 | 592 | extern void 593 | qsort_u32(u32 * const array, const size_t nr); 594 | 595 | extern u32 * 596 | bsearch_u32(const u32 v, const u32 * const array, const size_t nr); 597 | 598 | extern void 599 | shuffle_u32(u32 * const array, const u64 nr); 600 | 601 | extern int 602 | compare_u64(const void * const p1, const void * const p2); 603 | 604 | extern void 605 | qsort_u64(u64 * const array, const size_t nr); 606 | 607 | extern u64 * 608 | bsearch_u64(const u64 v, const u64 * const array, const size_t nr); 609 | 610 | extern void 611 | shuffle_u64(u64 * const array, const u64 nr); 612 | 613 | extern int 614 | compare_double(const void * const p1, const void * const p2); 615 | 616 | extern void 617 | qsort_double(double * const array, const size_t nr); 618 | 619 | extern void 620 | qsort_u64_sample(const u64 * const array0, const u64 nr, const u64 res, FILE * const out); 621 | 622 | extern void 623 | qsort_double_sample(const double * const array0, const u64 nr, const u64 res, FILE * const out); 624 | // }}} qsort 625 | 626 | // string {{{ 627 | // XXX strdec_ and strhex_ functions does not append the trailing '\0' to the output string 628 | // size of out should be >= 10 629 | extern void 630 | strdec_32(void * const out, const u32 v); 631 | 632 | // size of out should be >= 20 633 | extern void 634 | strdec_64(void * const out, const u64 v); 635 | 636 | // size of out should be >= 8 637 | extern void 638 | strhex_32(void * const out, const u32 v); 639 | 640 | // size of out should be >= 16 641 | extern void 642 | strhex_64(void * const out, const u64 v); 643 | 644 | extern u64 645 | a2u64(const void * const str); 646 | 647 | extern u32 648 | a2u32(const void * const str); 649 | 650 | extern s64 651 | a2s64(const void * const str); 652 | 653 | extern s32 654 | a2s32(const void * const str); 655 | 656 | extern void 657 | str_print_hex(FILE * const out, const void * const data, const u32 len); 658 | 659 | extern void 660 | str_print_dec(FILE * const out, const void * const data, const u32 len); 661 | 662 | // user should free returned ptr (and nothing else) after use 663 | extern char ** 664 | strtoks(const char * const str, const char * const delim); 665 | 666 | extern u32 667 | strtoks_count(const char * const * const toks); 668 | // }}} string 669 | 670 | // damp {{{ 671 | struct damp; 672 | 673 | extern struct damp * 674 | damp_create(const u64 cap, const double dshort, const double dlong); 675 | 676 | extern double 677 | damp_avg(const struct damp * const d); 678 | 679 | extern double 680 | damp_ravg(const struct damp * const d); 681 | 682 | extern double 683 | damp_min(const struct damp * const d); 684 | 685 | extern double 686 | damp_max(const struct damp * const d); 687 | 688 | extern void 689 | damp_add(struct damp * const d, const double v); 690 | 691 | extern bool 692 | damp_test(struct damp * const d); 693 | 694 | extern bool 695 | damp_add_test(struct damp * const d, const double v); 696 | 697 | extern void 698 | damp_clean(struct damp * const d); 699 | 700 | extern void 701 | damp_destroy(struct damp * const d); 702 | // }}} damp 703 | 704 | // vctr {{{ 705 | struct vctr; 706 | 707 | extern struct vctr * 708 | vctr_create(const size_t nr); 709 | 710 | extern size_t 711 | vctr_size(const struct vctr * const v); 712 | 713 | extern void 714 | vctr_add(struct vctr * const v, const u64 i, const size_t n); 715 | 716 | extern void 717 | vctr_add1(struct vctr * const v, const u64 i); 718 | 719 | extern void 720 | vctr_add_atomic(struct vctr * const v, const u64 i, const size_t n); 721 | 722 | extern void 723 | vctr_add1_atomic(struct vctr * const v, const u64 i); 724 | 725 | extern void 726 | vctr_set(struct vctr * const v, const u64 i, const size_t n); 727 | 728 | extern size_t 729 | vctr_get(const struct vctr * const v, const u64 i); 730 | 731 | extern void 732 | vctr_merge(struct vctr * const to, const struct vctr * const from); 733 | 734 | extern void 735 | vctr_reset(struct vctr * const v); 736 | 737 | extern void 738 | vctr_destroy(struct vctr * const v); 739 | // }}} vctr 740 | 741 | // rgen {{{ 742 | struct rgen; 743 | 744 | typedef u64 (*rgen_next_func)(struct rgen * const); 745 | 746 | extern struct rgen * rgen_new_rnd64(void); 747 | extern struct rgen * rgen_new_rnd64s(const u64 seed); 748 | extern struct rgen * rgen_new_const(const u64 c); 749 | extern struct rgen * rgen_new_expo(const double percentile, const double range); 750 | extern struct rgen * rgen_new_incs(const u64 min, const u64 max); 751 | extern struct rgen * rgen_new_incu(const u64 min, const u64 max); 752 | extern struct rgen * rgen_new_skips(const u64 min, const u64 max, const s64 inc); 753 | extern struct rgen * rgen_new_skipu(const u64 min, const u64 max, const s64 inc); 754 | extern struct rgen * rgen_new_decs(const u64 min, const u64 max); 755 | extern struct rgen * rgen_new_decu(const u64 min, const u64 max); 756 | extern struct rgen * rgen_new_zipfian(const u64 min, const u64 max); 757 | extern struct rgen * rgen_new_xzipfian(const u64 min, const u64 max); 758 | extern struct rgen * rgen_new_unizipf(const u64 min, const u64 max, const u64 ufactor); 759 | extern struct rgen * rgen_new_zipfuni(const u64 min, const u64 max, const u64 ufactor); 760 | extern struct rgen * rgen_new_latest(const u64 zipf_range); 761 | extern struct rgen * rgen_new_uniform(const u64 min, const u64 max); 762 | extern struct rgen * rgen_new_shuffle(const u64 min, const u64 max); 763 | extern struct rgen * rgen_new_trace32(const char * const filename, const u64 bufsize); 764 | 765 | extern u64 766 | rgen_min(struct rgen * const gen); 767 | 768 | extern u64 769 | rgen_max(struct rgen * const gen); 770 | 771 | extern u64 772 | rgen_next(struct rgen * const gen); 773 | 774 | // same to next() for regular gen; different only in async rgen 775 | extern u64 776 | rgen_next_nowait(struct rgen * const gen); 777 | 778 | extern u64 779 | rgen_next_write(struct rgen * const gen); 780 | 781 | extern void 782 | rgen_destroy(struct rgen * const gen); 783 | 784 | extern void 785 | rgen_helper_message(void); 786 | 787 | extern int 788 | rgen_helper(const int argc, char ** const argv, struct rgen ** const gen_out); 789 | 790 | extern void 791 | rgen_async_wait(struct rgen * const gen); 792 | 793 | extern void 794 | rgen_async_wait_all(struct rgen * const gen); 795 | 796 | extern struct rgen * 797 | rgen_fork(struct rgen * const gen0); 798 | 799 | extern void 800 | rgen_join(struct rgen * const gen); 801 | 802 | extern struct rgen * 803 | rgen_async_create(struct rgen * const gen0, const u32 cpu); 804 | // }}} rgen 805 | 806 | // qsbr {{{ 807 | struct qsbr; 808 | struct qsbr_ref { 809 | #ifdef QSBR_DEBUG 810 | u64 debug[16]; 811 | #endif 812 | u64 opaque[3]; 813 | }; 814 | 815 | extern struct qsbr * 816 | qsbr_create(void); 817 | 818 | extern bool 819 | qsbr_register(struct qsbr * const q, struct qsbr_ref * const qref); 820 | 821 | extern void 822 | qsbr_unregister(struct qsbr * const q, struct qsbr_ref * const qref); 823 | 824 | extern void 825 | qsbr_update(struct qsbr_ref * const qref, const u64 v); 826 | 827 | extern void 828 | qsbr_park(struct qsbr_ref * const qref); 829 | 830 | extern void 831 | qsbr_resume(struct qsbr_ref * const qref); 832 | 833 | extern void 834 | qsbr_wait(struct qsbr * const q, const u64 target); 835 | 836 | extern void 837 | qsbr_destroy(struct qsbr * const q); 838 | // }}} qsbr 839 | 840 | // forker {{{ 841 | #define FORKER_END_TIME ((0)) 842 | #define FORKER_END_COUNT ((1)) 843 | typedef bool (*forker_perf_analyze_func)(void * const passdata[2], const u64, const struct vctr *, struct damp *, char *); 844 | 845 | typedef void * (*forker_worker_func)(void *); 846 | 847 | struct pass_info { 848 | struct rgen * gen0; 849 | void * passdata[2]; // if not testing kv 850 | u64 vctr_size; 851 | forker_worker_func wf; 852 | forker_perf_analyze_func af; 853 | }; 854 | 855 | struct forker_worker_info { 856 | struct rgen * gen; 857 | rgen_next_func rgen_next; 858 | rgen_next_func rgen_next_write; // identical to rgen_next except for sync-latest 859 | void * passdata[2]; // if not testing kv 860 | void * priv; 861 | u32 end_type; 862 | u32 padding; 863 | u64 end_magic; 864 | struct vctr * vctr; 865 | 866 | u64 worker_id; // <= conc 867 | struct rgen * gen_back; 868 | u32 conc; // number of threads 869 | int argc;// user args 870 | char ** argv; 871 | u64 seed; 872 | void * (*thread_func)(void *); 873 | // PAPI 874 | u64 papi_vctr_base; 875 | }; 876 | 877 | extern int 878 | forker_pass(const int argc, char ** const argv, char ** const pref, 879 | struct pass_info * const pi, const int nr_wargs0); 880 | 881 | extern int 882 | forker_passes(int argc, char ** argv, char ** const pref0, 883 | struct pass_info * const pi, const int nr_wargs0); 884 | 885 | extern void 886 | forker_passes_message(void); 887 | 888 | extern bool 889 | forker_main(int argc, char ** argv, int(*test_func)(const int, char ** const)); 890 | // }}} forker 891 | 892 | #ifdef __cplusplus 893 | } 894 | #endif 895 | // vim:fdm=marker 896 | -------------------------------------------------------------------------------- /c/ord.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016--2021 Wu, Xingbo 3 | * 4 | * All rights reserved. No warranty, explicit or implicit, provided. 5 | */ 6 | #define _GNU_SOURCE 7 | 8 | // headers {{{ 9 | #include // static_assert 10 | #include "lib.h" 11 | #include "ctypes.h" 12 | #include "kv.h" 13 | #include "ord.h" 14 | // }}} headers 15 | 16 | // skiplist {{{ 17 | #define SL_MAXH ((32)) 18 | struct skipnode { 19 | struct kv * kv; 20 | union { 21 | struct skipnode * ptr; 22 | au64 a; 23 | } next[0]; 24 | }; 25 | 26 | struct skippath { 27 | struct skipnode * vec[SL_MAXH][2]; // left and right 28 | }; 29 | 30 | struct skiplist { 31 | mutex lock; 32 | u64 padding[7]; 33 | struct kvmap_mm mm; 34 | u64 height; 35 | struct skipnode n0; 36 | }; 37 | 38 | struct skiplist * 39 | skiplist_create(const struct kvmap_mm * const mm) 40 | { 41 | const size_t sz = sizeof(struct skiplist) + (sizeof(void *) * SL_MAXH); 42 | struct skiplist * const list = yalloc(sz); 43 | if (list == NULL) 44 | return NULL; 45 | 46 | memset(list, 0, sz); 47 | mutex_init(&(list->lock)); 48 | list->mm = mm ? (*mm) : kvmap_mm_dup; 49 | list->height = 1; 50 | return list; 51 | } 52 | 53 | static inline struct skipnode * 54 | skiplist_next(struct skipnode * const node, const u64 h) 55 | { 56 | return (struct skipnode *)(void *)atomic_load_explicit(&node->next[h].a, MO_ACQUIRE); 57 | } 58 | 59 | static inline void 60 | skiplist_update_next(struct skipnode * const node, const u64 h, struct skipnode * const next) 61 | { 62 | atomic_store_explicit(&(node->next[h].a), (u64)next, MO_RELEASE); 63 | } 64 | 65 | static inline void 66 | skiplist_lock(struct skiplist * const list) 67 | { 68 | mutex_lock(&(list->lock)); 69 | } 70 | 71 | static inline void 72 | skiplist_unlock(struct skiplist * const list) 73 | { 74 | mutex_unlock(&(list->lock)); 75 | } 76 | 77 | // perform a search on skiplist and record the path 78 | // return true for match, false for mismatch 79 | // *out will have the node >= key 80 | // on match, the path could be incomplete (the path is not used unless for del, which will be handled separately) 81 | // on mismatch, at every level of the path, left < key < right (or NULL); a new key should be inserted in between 82 | static bool 83 | skiplist_search_ge_path(struct skiplist * const list, const struct kref * const key, 84 | struct skipnode ** const out, struct skippath * const path, u64 h) 85 | { 86 | debug_assert(h); 87 | struct skipnode * left = &(list->n0); // leftmost 88 | struct skipnode * next; 89 | while ((--h) < SL_MAXH) { 90 | while ((next = skiplist_next(left, h)) != NULL) { 91 | const int cmp = kref_kv_compare(key, next->kv); 92 | if (cmp > 0) { // forward and continue 93 | left = next; 94 | } else if (cmp < 0) { // done at this level 95 | break; 96 | } else { // match 97 | *out = next; 98 | return true; 99 | } 100 | } 101 | path->vec[h][0] = left; 102 | path->vec[h][1] = next; 103 | } 104 | // no match; return the first node > key 105 | *out = next; 106 | return false; 107 | } 108 | 109 | static bool 110 | skiplist_search_ge(struct skiplist * const list, const struct kref * const key, 111 | struct skipnode ** const out) 112 | { 113 | u64 h = list->height; 114 | debug_assert(h); 115 | struct skipnode * left = &(list->n0); // leftmost 116 | struct skipnode * next; 117 | while ((--h) < SL_MAXH) { 118 | while ((next = skiplist_next(left, h)) != NULL) { 119 | const int cmp = kref_kv_compare(key, next->kv); 120 | if (cmp > 0) { // forward and continue 121 | left = next; 122 | } else if (cmp < 0) { // done at this level 123 | break; 124 | } else { // match 125 | *out = next; 126 | return true; 127 | } 128 | } 129 | } 130 | // no match; return the first node > key 131 | *out = next; 132 | return false; 133 | } 134 | 135 | struct kv * 136 | skiplist_get(struct skiplist * const list, const struct kref * const key, struct kv * const out) 137 | { 138 | struct skipnode * node; 139 | if (skiplist_search_ge(list, key, &node)) { 140 | debug_assert(node && node->kv); 141 | return list->mm.out(node->kv, out); 142 | } 143 | return NULL; 144 | } 145 | 146 | bool 147 | skiplist_probe(struct skiplist * const list, const struct kref * const key) 148 | { 149 | struct skipnode * node; 150 | return skiplist_search_ge(list, key, &node); 151 | } 152 | 153 | // generate a random height; if it's higher than hh, fill the path 154 | static u64 155 | skiplist_random_height(struct skiplist * const list, struct skippath * const path, const u64 hh) 156 | { 157 | const u64 r = random_u64(); // r can be 0 158 | // 1 <= height <= 32 159 | const u64 height = (u64)(__builtin_ctzl(r ? r : 1) >> 1) + 1; 160 | for (u64 i = hh; i < height; i++) { 161 | path->vec[i][0] = &(list->n0); // the very beginning 162 | path->vec[i][1] = NULL; // the very end 163 | } 164 | 165 | return height; 166 | } 167 | 168 | static bool 169 | skiplist_insert_height(struct skiplist * const list, struct skippath * const path, 170 | struct kv * const kv, const u64 height) 171 | { 172 | if (height > list->height) 173 | list->height = height; 174 | 175 | const u64 nodesize = sizeof(list->n0) + (sizeof(list->n0.next[0]) * height); 176 | struct skipnode * const node = malloc(nodesize); 177 | if (!node) { // malloc failed 178 | list->mm.free(kv, list->mm.priv); 179 | return false; 180 | } 181 | kv->privptr = NULL; // end of chain 182 | node->kv = kv; 183 | for (u64 i = 0; i < height; i++) { 184 | node->next[i].ptr = path->vec[i][1]; 185 | skiplist_update_next(path->vec[i][0], i, node); 186 | } 187 | return true; 188 | } 189 | 190 | static bool 191 | skiplist_insert_fix_path(struct skippath * const path, const u64 height, struct kv * const kv) 192 | { 193 | for (u64 h = 0; h < height; h++) { // must check every level 194 | struct skipnode * left = path->vec[h][0]; 195 | struct skipnode * right = left->next[h].ptr; 196 | 197 | if (likely(right == path->vec[h][1])) 198 | continue; 199 | 200 | debug_assert(right); // insertions only; won't disappear 201 | 202 | do { 203 | const int cmp = kv_compare(kv, right->kv); 204 | if (cmp < 0) { // right is ok 205 | break; 206 | } else if (cmp > 0) { // forward path[h] 207 | left = right; 208 | right = left->next[h].ptr; 209 | } else { 210 | kv->privptr = right->kv; 211 | right->kv = kv; 212 | // insertion is already done 213 | return true; 214 | } 215 | } while (right); 216 | path->vec[h][0] = left; 217 | path->vec[h][1] = right; 218 | } 219 | // should continue insert 220 | return false; 221 | } 222 | 223 | static bool 224 | skiplist_insert_helper(struct skiplist * const list, struct skippath * const path, 225 | const u64 hh, struct kv * const kv, const bool safe) 226 | { 227 | const u64 height = skiplist_random_height(list, path, hh); 228 | 229 | if (safe) { // other writers may insert keys to the path 230 | skiplist_lock(list); 231 | if (skiplist_insert_fix_path(path, height, kv)) { 232 | skiplist_unlock(list); 233 | return true; 234 | } 235 | } 236 | 237 | const bool r = skiplist_insert_height(list, path, kv, height); 238 | 239 | if (safe) 240 | skiplist_unlock(list); 241 | return r; 242 | } 243 | 244 | static bool 245 | skiplist_put_helper(struct skiplist * const list, struct kv * const kv, const bool safe) 246 | { 247 | struct kv * const newkv = list->mm.in(kv, list->mm.priv); 248 | if (!newkv) 249 | return false; 250 | 251 | struct kref kref; 252 | kref_ref_kv(&kref, kv); 253 | struct skipnode * node; 254 | struct skippath path; 255 | const u64 hh = list->height; 256 | const bool r = skiplist_search_ge_path(list, &kref, &node, &path, hh); 257 | if (r) { // replace 258 | if (safe) { 259 | skiplist_lock(list); 260 | newkv->privptr = node->kv; 261 | node->kv = newkv; 262 | skiplist_unlock(list); 263 | } else { 264 | list->mm.free(node->kv, list->mm.priv); 265 | newkv->privptr = NULL; 266 | node->kv = newkv; 267 | } 268 | return true; 269 | } 270 | 271 | return skiplist_insert_helper(list, &path, hh, newkv, safe); 272 | } 273 | 274 | bool 275 | skiplist_put(struct skiplist * const list, struct kv * const kv) 276 | { 277 | return skiplist_put_helper(list, kv, false); 278 | } 279 | 280 | bool 281 | skipsafe_put(struct skiplist * const list, struct kv * const kv) 282 | { 283 | return skiplist_put_helper(list, kv, true); 284 | } 285 | 286 | static bool 287 | skiplist_merge_helper(struct skiplist * const list, const struct kref * const kref, 288 | kv_merge_func uf, void * const priv, const bool safe) 289 | { 290 | struct skipnode * node; 291 | struct skippath path; 292 | const u64 hh = list->height; 293 | 294 | const bool r = skiplist_search_ge_path(list, kref, &node, &path, hh); 295 | 296 | if (r) { 297 | if (safe) { 298 | skiplist_lock(list); 299 | struct kv * const old = node->kv; 300 | struct kv * const kv = uf(old, priv); 301 | if ((kv != old) && (kv != NULL)) { // replace 302 | struct kv * const newkv = list->mm.in(kv, list->mm.priv); 303 | if (!newkv) { 304 | skiplist_unlock(list); 305 | return false; 306 | } 307 | newkv->privptr = old; 308 | node->kv = newkv; 309 | } 310 | skiplist_unlock(list); 311 | } else { // unsafe 312 | struct kv * const old = node->kv; 313 | struct kv * const kv = uf(old, priv); 314 | if (kv != old) { // replace 315 | struct kv * const newkv = list->mm.in(kv, list->mm.priv); 316 | if (!newkv) 317 | return false; 318 | 319 | list->mm.free(old, list->mm.priv); 320 | newkv->privptr = NULL; 321 | node->kv = newkv; 322 | } 323 | } 324 | return true; 325 | } 326 | 327 | struct kv * const kv = uf(NULL, priv); 328 | if (!kv) // do nothing 329 | return true; 330 | 331 | struct kv * const newkv = list->mm.in(kv, list->mm.priv); 332 | if (!newkv) 333 | return false; 334 | 335 | return skiplist_insert_helper(list, &path, hh, newkv, safe); 336 | } 337 | 338 | bool 339 | skiplist_merge(struct skiplist * const list, const struct kref * const kref, 340 | kv_merge_func uf, void * const priv) 341 | { 342 | return skiplist_merge_helper(list, kref, uf, priv, false); 343 | } 344 | 345 | bool 346 | skipsafe_merge(struct skiplist * const list, const struct kref * const kref, 347 | kv_merge_func uf, void * const priv) 348 | { 349 | return skiplist_merge_helper(list, kref, uf, priv, true); 350 | } 351 | 352 | bool 353 | skiplist_inp(struct skiplist * const list, const struct kref * const key, 354 | kv_inp_func uf, void * const priv) 355 | { 356 | struct skipnode * node; 357 | const bool r = skiplist_search_ge(list, key, &node); 358 | uf(r ? node->kv : NULL, priv); 359 | return r; 360 | } 361 | 362 | // return the previous node if ret->next matches the key 363 | static u64 364 | skiplist_search_del_prev(struct skiplist * const list, const struct kref * const key, 365 | struct skipnode ** const prev) 366 | { 367 | debug_assert(list->height); 368 | u64 h = list->height; 369 | struct skipnode * left = &(list->n0); // leftmost 370 | struct skipnode * next; 371 | while ((--h) < SL_MAXH) { 372 | while ((next = skiplist_next(left, h)) != NULL) { 373 | const int cmp = kref_kv_compare(key, next->kv); 374 | if (cmp > 0) { // forward and continue 375 | left = next; 376 | } else if (cmp < 0) { // done at this level 377 | break; 378 | } else { // match 379 | *prev = left; 380 | return h; 381 | } 382 | } 383 | } 384 | return SL_MAXH; 385 | } 386 | 387 | // for unsafe skiplist only 388 | bool 389 | skiplist_del(struct skiplist * const list, const struct kref * const key) 390 | { 391 | struct skipnode * prev = NULL; 392 | u64 h = skiplist_search_del_prev(list, key, &prev); 393 | if (h == SL_MAXH) 394 | return false; 395 | 396 | struct skipnode * const victim = skiplist_next(prev, h); 397 | prev->next[h].ptr = victim->next[h].ptr; 398 | 399 | while ((--h) < SL_MAXH) { 400 | while (prev->next[h].ptr != victim) 401 | prev = prev->next[h].ptr; 402 | prev->next[h].ptr = victim->next[h].ptr; 403 | } 404 | 405 | list->mm.free(victim->kv, list->mm.priv); 406 | free(victim); 407 | return true; 408 | } 409 | 410 | void 411 | skiplist_clean(struct skiplist * const list) 412 | { 413 | struct skipnode * iter = list->n0.next[0].ptr; 414 | while (iter) { 415 | struct skipnode * const next = iter->next[0].ptr; 416 | struct kv * kviter = iter->kv; 417 | while (kviter) { // free the chain 418 | struct kv * tmp = kviter->privptr; 419 | list->mm.free(kviter, list->mm.priv); 420 | kviter = tmp; 421 | } 422 | free(iter); 423 | iter = next; 424 | } 425 | for (u64 i = 0; i < SL_MAXH; i++) 426 | list->n0.next[i].ptr = NULL; 427 | } 428 | 429 | void 430 | skiplist_destroy(struct skiplist * const list) 431 | { 432 | skiplist_clean(list); 433 | free(list); 434 | } 435 | 436 | void 437 | skiplist_fprint(struct skiplist * const list, FILE * const out) 438 | { 439 | u64 hs[SL_MAXH] = {}; 440 | u32 costs[SL_MAXH]; 441 | struct skipnode * nexts[SL_MAXH+1]; 442 | const u64 hh = list->height; 443 | debug_assert(hh && hh < SL_MAXH); 444 | for (u64 i = 0; i < hh; i++) { 445 | nexts[i] = skiplist_next(&list->n0, i); 446 | costs[i] = 1u; 447 | } 448 | nexts[hh] = NULL; 449 | 450 | struct skipnode * iter = nexts[0]; // the first item 451 | u64 totcost = 0; 452 | u64 totkv = 0; 453 | while (iter) { 454 | u64 h = 0; 455 | while ((h+1) < SL_MAXH && nexts[h+1] == iter) { 456 | costs[h] = 1; 457 | nexts[h] = skiplist_next(iter, h); 458 | h++; 459 | } 460 | nexts[h] = skiplist_next(iter, h); 461 | hs[h]++; 462 | 463 | u32 cost = 0; 464 | for (u64 i = h; i < hh; i++) 465 | cost += costs[i]; 466 | 467 | // uncomment to print 468 | //fprintf(out, "h=%2lu c=%3u", h, cost); 469 | //kv_print(iter->kv, "sn", out); 470 | 471 | costs[h]++; 472 | iter = skiplist_next(iter, 0); 473 | totcost += cost; 474 | totkv++; 475 | } 476 | 477 | const double avgcost = (double)totcost / (double)totkv; 478 | fprintf(out, "SKIPLIST count %lu height %lu avgcost %.3lf\n", totkv, hh, avgcost); 479 | for (u64 i = 0; i < hh; i += 4) { 480 | fprintf(out, "SKIPLIST H[%lu] %lu H[%lu] %lu H[%lu] %lu H[%lu] %lu\n", 481 | i, hs[i], i+1, hs[i+1], i+2, hs[i+2], i+3, hs[i+3]); 482 | } 483 | } 484 | 485 | struct skiplist_iter { 486 | struct skipnode * curr; 487 | struct skiplist * list; 488 | }; 489 | 490 | struct skiplist_iter * 491 | skiplist_iter_create(struct skiplist * const list) 492 | { 493 | struct skiplist_iter * const iter = malloc(sizeof(*iter)); 494 | if (iter == NULL) 495 | return NULL; 496 | 497 | iter->curr = NULL; // invalid 498 | iter->list = list; 499 | return iter; 500 | } 501 | 502 | void 503 | skiplist_iter_seek(struct skiplist_iter * const iter, const struct kref * const key) 504 | { 505 | struct skiplist * list = iter->list; 506 | skiplist_search_ge(list, key, &iter->curr); 507 | } 508 | 509 | bool 510 | skiplist_iter_valid(struct skiplist_iter * const iter) 511 | { 512 | return iter->curr != NULL; 513 | } 514 | 515 | struct kv * 516 | skiplist_iter_peek(struct skiplist_iter * const iter, struct kv * const out) 517 | { 518 | if (!skiplist_iter_valid(iter)) 519 | return NULL; 520 | struct kv * const curr = iter->curr->kv; 521 | struct kv * const ret = iter->list->mm.out(curr, out); 522 | return ret; 523 | } 524 | 525 | bool 526 | skiplist_iter_kref(struct skiplist_iter * const iter, struct kref * const kref) 527 | { 528 | if (!skiplist_iter_valid(iter)) 529 | return false; 530 | kref_ref_kv(kref, iter->curr->kv); 531 | return true; 532 | } 533 | 534 | bool 535 | skiplist_iter_kvref(struct skiplist_iter * const iter, struct kvref * const kvref) 536 | { 537 | if (!skiplist_iter_valid(iter)) 538 | return false; 539 | kvref_ref_kv(kvref, iter->curr->kv); 540 | return true; 541 | } 542 | 543 | void 544 | skiplist_iter_skip1(struct skiplist_iter * const iter) 545 | { 546 | if (skiplist_iter_valid(iter)) 547 | iter->curr = skiplist_next(iter->curr, 0); 548 | } 549 | 550 | void 551 | skiplist_iter_skip(struct skiplist_iter * const iter, const u32 nr) 552 | { 553 | for (u32 i = 0; i < nr; i++) { 554 | if (!skiplist_iter_valid(iter)) 555 | return; 556 | iter->curr = skiplist_next(iter->curr, 0); 557 | } 558 | } 559 | 560 | struct kv * 561 | skiplist_iter_next(struct skiplist_iter * const iter, struct kv * const out) 562 | { 563 | struct kv * const ret = skiplist_iter_peek(iter, out); 564 | skiplist_iter_skip1(iter); 565 | return ret; 566 | } 567 | 568 | bool 569 | skiplist_iter_inp(struct skiplist_iter * const iter, kv_inp_func uf, void * const priv) 570 | { 571 | struct kv * const kv = iter->curr ? iter->curr->kv : NULL; 572 | uf(kv, priv); // call uf even if (kv == NULL) 573 | return kv != NULL; 574 | } 575 | 576 | void 577 | skiplist_iter_destroy(struct skiplist_iter * const iter) 578 | { 579 | free(iter); 580 | } 581 | 582 | const struct kvmap_api kvmap_api_skiplist = { 583 | .ordered = true, 584 | .unique = true, 585 | .put = (void *)skiplist_put, 586 | .get = (void *)skiplist_get, 587 | .probe = (void *)skiplist_probe, 588 | .del = (void *)skiplist_del, 589 | .inpr = (void *)skiplist_inp, 590 | .inpw = (void *)skiplist_inp, 591 | .merge = (void *)skiplist_merge, 592 | .iter_create = (void *)skiplist_iter_create, 593 | .iter_seek = (void *)skiplist_iter_seek, 594 | .iter_valid = (void *)skiplist_iter_valid, 595 | .iter_peek = (void *)skiplist_iter_peek, 596 | .iter_kref = (void *)skiplist_iter_kref, 597 | .iter_kvref = (void *)skiplist_iter_kvref, 598 | .iter_skip1 = (void *)skiplist_iter_skip1, 599 | .iter_skip = (void *)skiplist_iter_skip, 600 | .iter_next = (void *)skiplist_iter_next, 601 | .iter_inp = (void *)skiplist_iter_inp, 602 | .iter_destroy = (void *)skiplist_iter_destroy, 603 | .clean = (void *)skiplist_clean, 604 | .destroy = (void *)skiplist_destroy, 605 | .fprint = (void *)skiplist_fprint, 606 | }; 607 | 608 | const struct kvmap_api kvmap_api_skipsafe = { 609 | .ordered = true, 610 | .unique = true, 611 | .irefsafe = true, 612 | .put = (void *)skipsafe_put, 613 | .get = (void *)skiplist_get, 614 | .probe = (void *)skiplist_probe, 615 | .del = NULL, 616 | .inpr = (void *)skiplist_inp, 617 | .inpw = (void *)skiplist_inp, 618 | .merge = (void *)skipsafe_merge, 619 | .iter_create = (void *)skiplist_iter_create, 620 | .iter_seek = (void *)skiplist_iter_seek, 621 | .iter_valid = (void *)skiplist_iter_valid, 622 | .iter_peek = (void *)skiplist_iter_peek, 623 | .iter_kref = (void *)skiplist_iter_kref, 624 | .iter_kvref = (void *)skiplist_iter_kvref, 625 | .iter_skip1 = (void *)skiplist_iter_skip1, 626 | .iter_skip = (void *)skiplist_iter_skip, 627 | .iter_next = (void *)skiplist_iter_next, 628 | .iter_inp = (void *)skiplist_iter_inp, 629 | .iter_destroy = (void *)skiplist_iter_destroy, 630 | .clean = (void *)skiplist_clean, 631 | .destroy = (void *)skiplist_destroy, 632 | .fprint = (void *)skiplist_fprint, 633 | }; 634 | 635 | static void * 636 | skiplist_kvmap_api_create(const char * const name, const struct kvmap_mm * const mm, char ** args) 637 | { 638 | if (strcmp(name, "skiplist") && strcmp(name, "skipsafe")) 639 | return NULL; 640 | (void)args; 641 | return skiplist_create(mm); 642 | } 643 | 644 | __attribute__((constructor)) 645 | static void 646 | skiplist_kvmap_api_init(void) 647 | { 648 | kvmap_api_register(0, "skiplist", "", skiplist_kvmap_api_create, &kvmap_api_skiplist); 649 | kvmap_api_register(0, "skipsafe", "", skiplist_kvmap_api_create, &kvmap_api_skipsafe); 650 | } 651 | // }}} skiplist 652 | 653 | // rdb {{{ 654 | #ifdef ROCKSDB 655 | #define ROCKSDB_SYNC_SIZE ((1lu<<25)) 656 | #include 657 | struct rdb { 658 | rocksdb_t * db; 659 | rocksdb_filterpolicy_t * bf; 660 | rocksdb_options_t * dopt; 661 | u64 sync_size; 662 | rocksdb_writeoptions_t * wopt; 663 | rocksdb_writeoptions_t * wopt_sync; 664 | rocksdb_readoptions_t * ropt; 665 | rocksdb_cache_t * cache; 666 | char * path; 667 | char * err; 668 | }; 669 | 670 | struct rdb * 671 | rdb_create(const char * const path, const u64 cache_size_mb) 672 | { 673 | rocksdb_options_t* const dopt = rocksdb_options_create(); 674 | rocksdb_options_set_compression(dopt, 0); 675 | rocksdb_options_set_create_if_missing(dopt, 1); 676 | 677 | // statistics of everything; uncomment to enable 678 | //rocksdb_options_enable_statistics(dopt); 679 | 680 | // Total ordered database, flash storage 681 | // https://github.com/facebook/rocksdb/wiki/RocksDB-Tuning-Guide 682 | rocksdb_env_t * const env = rocksdb_create_default_env(); 683 | rocksdb_env_set_background_threads(env, 4); 684 | rocksdb_options_set_env(dopt, env); 685 | 686 | rocksdb_options_set_compaction_style(dopt, rocksdb_level_compaction); 687 | rocksdb_options_set_write_buffer_size(dopt, 1024lu << 20); 688 | rocksdb_options_set_max_write_buffer_number(dopt, 1); 689 | rocksdb_options_set_max_open_files(dopt, 65536); 690 | rocksdb_options_set_target_file_size_base(dopt, 64lu << 20); 691 | rocksdb_options_set_max_background_compactions(dopt, 4); 692 | rocksdb_options_set_level0_file_num_compaction_trigger(dopt, 8); 693 | rocksdb_options_set_level0_slowdown_writes_trigger(dopt, 17); 694 | rocksdb_options_set_level0_stop_writes_trigger(dopt, 24); 695 | rocksdb_options_set_num_levels(dopt, 4); 696 | rocksdb_options_set_max_bytes_for_level_base(dopt, 512lu << 20); 697 | rocksdb_options_set_max_bytes_for_level_multiplier(dopt, 8); 698 | rocksdb_options_set_allow_mmap_writes(dopt, 0); 699 | 700 | // table options 701 | rocksdb_block_based_table_options_t* const topt = rocksdb_block_based_options_create(); 702 | // bf 703 | rocksdb_filterpolicy_t * bf = rocksdb_filterpolicy_create_bloom(10); 704 | rocksdb_block_based_options_set_filter_policy(topt, bf); 705 | // cache 706 | rocksdb_cache_t * cache = NULL; 707 | if (cache_size_mb) { // use block cache 708 | cache = rocksdb_cache_create_lru(cache_size_mb << 20); // MB 709 | rocksdb_block_based_options_set_block_cache(topt, cache); 710 | rocksdb_options_set_allow_mmap_reads(dopt, 0); 711 | rocksdb_block_based_options_set_cache_index_and_filter_blocks(topt, 1); 712 | rocksdb_block_based_options_set_cache_index_and_filter_blocks_with_high_priority(topt, 1); 713 | rocksdb_block_based_options_set_pin_l0_filter_and_index_blocks_in_cache(topt, 1); 714 | } else { // use mmap 715 | rocksdb_options_set_allow_mmap_reads(dopt, 1); 716 | rocksdb_block_based_options_set_no_block_cache(topt, 1); 717 | } 718 | 719 | rocksdb_options_set_block_based_table_factory(dopt, topt); 720 | 721 | struct rdb * const rdb = malloc(sizeof(*rdb)); 722 | if (rdb == NULL) 723 | return NULL; 724 | rdb->db = rocksdb_open(dopt, path, &rdb->err); 725 | if (rdb->db == NULL) { 726 | free(rdb); 727 | return NULL; 728 | } 729 | rdb->path = strdup(path); 730 | rdb->dopt = dopt; 731 | rocksdb_block_based_options_destroy(topt); 732 | 733 | rdb->bf = bf; 734 | rdb->sync_size = 0; 735 | rdb->wopt = rocksdb_writeoptions_create(); 736 | rdb->wopt_sync = rocksdb_writeoptions_create(); 737 | rocksdb_writeoptions_set_sync(rdb->wopt_sync, 1); 738 | rdb->ropt = rocksdb_readoptions_create(); 739 | rdb->cache = cache; 740 | rocksdb_env_destroy(env); 741 | rocksdb_readoptions_set_fill_cache(rdb->ropt, 1); 742 | rocksdb_readoptions_set_verify_checksums(rdb->ropt, 0); 743 | return rdb; 744 | } 745 | 746 | struct kv * 747 | rdb_get(struct rdb * const map, const struct kref * const key, struct kv * const out) 748 | { 749 | size_t vlen; 750 | char * const ret = rocksdb_get(map->db, map->ropt, (const char *)key->ptr, 751 | (size_t)key->len, &vlen, &map->err); 752 | if (ret) { 753 | if (out) { 754 | kv_refill(out, key->ptr, key->len, ret, vlen); 755 | free(ret); 756 | return out; 757 | } else { 758 | struct kv * const new = kv_create(key->ptr, key->len, ret, vlen); 759 | free(ret); 760 | return new; 761 | } 762 | } else { 763 | return NULL; 764 | } 765 | } 766 | 767 | bool 768 | rdb_probe(struct rdb * const map, const struct kref * const key) 769 | { 770 | size_t vlen; 771 | char * const ret = rocksdb_get(map->db, map->ropt, (const char *)key->ptr, 772 | (size_t)key->len, &vlen, &map->err); 773 | free(ret); 774 | return ret != NULL; 775 | } 776 | 777 | bool 778 | rdb_put(struct rdb * const map, struct kv * const kv) 779 | { 780 | map->sync_size += (kv->klen + kv->vlen); 781 | const bool do_sync = (map->sync_size >= ROCKSDB_SYNC_SIZE); 782 | if (do_sync) 783 | map->sync_size -= ROCKSDB_SYNC_SIZE; 784 | 785 | rocksdb_put(map->db, (do_sync ? map->wopt_sync : map->wopt), 786 | (const char *)kv->kv, (size_t)kv->klen, 787 | (const char *)kv_vptr_c(kv), (size_t)kv->vlen, &map->err); 788 | return true; 789 | } 790 | 791 | bool 792 | rdb_del(struct rdb * const map, const struct kref * const key) 793 | { 794 | rocksdb_delete(map->db, map->wopt, (const char *)key->ptr, (size_t)key->len, &map->err); 795 | return true; 796 | } 797 | 798 | void 799 | rdb_destroy(struct rdb * const map) 800 | { 801 | // XXX: rocksdb/gflags has memoryleak on exit. 802 | //rocksdb_filterpolicy_destroy(map->bf); // destroyed by rocksdb_options_destroy() 803 | rocksdb_close(map->db); 804 | rocksdb_readoptions_destroy(map->ropt); 805 | rocksdb_writeoptions_destroy(map->wopt); 806 | rocksdb_writeoptions_destroy(map->wopt_sync); 807 | if (map->cache) 808 | rocksdb_cache_destroy(map->cache); 809 | // uncomment to remove db files 810 | //rocksdb_destroy_db(map->dopt, map->path, &map->err); 811 | free(map->path); 812 | rocksdb_options_destroy(map->dopt); 813 | free(map); 814 | } 815 | 816 | void 817 | rdb_fprint(struct rdb * const map, FILE * const out) 818 | { 819 | char * str = rocksdb_options_statistics_get_string(map->dopt); 820 | if (str) 821 | fprintf(out, "%s", str); 822 | } 823 | 824 | struct rdb_iter * 825 | rdb_iter_create(struct rdb * const map) 826 | { 827 | struct rdb_iter * const iter = (typeof(iter))rocksdb_create_iterator(map->db, map->ropt); 828 | return iter; 829 | } 830 | 831 | void 832 | rdb_iter_seek(struct rdb_iter * const iter, const struct kref * const key) 833 | { 834 | rocksdb_iter_seek((rocksdb_iterator_t *)iter, (const char *)key->ptr, (size_t)key->len); 835 | } 836 | 837 | bool 838 | rdb_iter_valid(struct rdb_iter * const iter) 839 | { 840 | struct rocksdb_iterator_t * riter = (typeof(riter))iter; 841 | return rocksdb_iter_valid(riter); 842 | } 843 | 844 | struct kv * 845 | rdb_iter_peek(struct rdb_iter * const iter, struct kv * const out) 846 | { 847 | struct rocksdb_iterator_t * riter = (typeof(riter))iter; 848 | if (rocksdb_iter_valid(riter)) { 849 | size_t klen, vlen; 850 | const char * const key = rocksdb_iter_key(riter, &klen); 851 | const char * const value = rocksdb_iter_value(riter, &vlen); 852 | if (out) { 853 | kv_refill(out, (u8 *)key, (u32)klen, (u8 *)value, (u32)vlen); 854 | return out; 855 | } else { 856 | return kv_create((u8 *)key, (u32)klen, (u8 *)value, (u32)vlen); 857 | } 858 | } 859 | return NULL; 860 | } 861 | 862 | void 863 | rdb_iter_skip1(struct rdb_iter * const iter) 864 | { 865 | struct rocksdb_iterator_t * riter = (typeof(riter))iter; 866 | if (rocksdb_iter_valid(riter)) 867 | rocksdb_iter_next(riter); 868 | } 869 | 870 | void 871 | rdb_iter_skip(struct rdb_iter * const iter, const u32 nr) 872 | { 873 | struct rocksdb_iterator_t * riter = (typeof(riter))iter; 874 | for (u32 i = 0; i < nr; i++) { 875 | if (!rocksdb_iter_valid(riter)) 876 | break; 877 | rocksdb_iter_next(riter); 878 | } 879 | } 880 | 881 | struct kv * 882 | rdb_iter_next(struct rdb_iter * const iter, struct kv * const out) 883 | { 884 | struct kv * const ret = rdb_iter_peek(iter, out); 885 | rdb_iter_skip1(iter); 886 | return ret; 887 | } 888 | 889 | void 890 | rdb_iter_destroy(struct rdb_iter * const iter) 891 | { 892 | rocksdb_iter_destroy((rocksdb_iterator_t *)iter); 893 | } 894 | 895 | const struct kvmap_api kvmap_api_rdb = { 896 | .ordered = true, 897 | .threadsafe = true, 898 | .unique = true, 899 | .put = (void *)rdb_put, 900 | .get = (void *)rdb_get, 901 | .probe = (void *)rdb_probe, 902 | .del = (void *)rdb_del, 903 | .iter_create = (void *)rdb_iter_create, 904 | .iter_seek = (void *)rdb_iter_seek, 905 | .iter_valid = (void *)rdb_iter_valid, 906 | .iter_peek = (void *)rdb_iter_peek, 907 | .iter_skip1 = (void *)rdb_iter_skip1, 908 | .iter_skip = (void *)rdb_iter_skip, 909 | .iter_next = (void *)rdb_iter_next, 910 | .iter_destroy = (void *)rdb_iter_destroy, 911 | .destroy = (void *)rdb_destroy, 912 | .fprint = (void *)rdb_fprint, 913 | }; 914 | 915 | static void * 916 | rdb_kvmap_api_create(const char * const name, const struct kvmap_mm * const mm, char ** args) 917 | { 918 | if (strcmp(name, "rdb") != 0) 919 | return NULL; 920 | (void)mm; 921 | return rdb_create(args[0], a2u64(args[1])); 922 | } 923 | 924 | // alternatively, call the register function from main() 925 | __attribute__((constructor)) 926 | static void 927 | rdb_kvmap_api_init(void) 928 | { 929 | kvmap_api_register(2, "rdb", " ", rdb_kvmap_api_create, &kvmap_api_rdb); 930 | } 931 | #endif // ROCKSDB 932 | // }}} rdb 933 | 934 | // kvell {{{ 935 | #ifdef KVELL 936 | // https://github.com/wuxb45/KVell 937 | // make libkvell.so and install to /usr/local/lib 938 | // https://github.com/wuxb45/KVell/blob/master/kvell.h 939 | extern void 940 | kvell_init(const char * prefix, const char * nd, const char * wpd, const char * cgb, const char * qd); 941 | 942 | extern void 943 | kvell_get_submit(const void * key, size_t klen, const uint64_t hash, void (*func)(void * item, uint64_t arg1, uint64_t arg2), uint64_t arg1, uint64_t arg2); 944 | 945 | extern void 946 | kvell_put_submit(const void * key, size_t klen, const uint64_t hash, const void * value, size_t vlen, void (*func)(void * item, uint64_t arg1, uint64_t arg2), uint64_t arg1, uint64_t arg2); 947 | 948 | extern void 949 | kvell_del_submit(const void * key, size_t klen, const uint64_t hash, void (*func)(void * item, uint64_t arg1, uint64_t arg2), uint64_t arg1, uint64_t arg2); 950 | 951 | // XXX does 50 scans 952 | extern void 953 | kvell_scan50(const void * key, size_t klen, const uint64_t hash, void (*func)(void * item, uint64_t arg1, uint64_t arg2), uint64_t arg1, uint64_t arg2); 954 | 955 | void * 956 | kvell_create(char ** args) 957 | { 958 | kvell_init(args[0], args[1], args[2], args[3], args[4]); 959 | 960 | struct vctr ** const vctrs = malloc(sizeof(vctrs[0]) * 4); // SET/DEL/GET/SCAN 961 | for (u32 i = 0; i < 4; i++) { 962 | vctrs[i] = vctr_create(1lu << 16); 963 | } 964 | return vctrs; 965 | } 966 | 967 | static void 968 | kvell_cb(void * item, u64 x, u64 y) 969 | { 970 | (void)item; 971 | (void)x; 972 | (void)y; 973 | } 974 | 975 | static void 976 | kvelll_cb(void * item, u64 vctr_ptr, u64 t0) 977 | { 978 | (void)item; 979 | struct vctr * const vctr = (typeof(vctr))vctr_ptr; 980 | const u64 dt_us = (time_diff_nsec(t0) + 999) / 1000; 981 | vctr_add1_atomic(vctr, dt_us < (1lu << 16) ? dt_us : (1lu << 16)-1); 982 | } 983 | 984 | // GET 985 | struct kv * 986 | kvell_get(void * const map, const struct kref * const key, struct kv * const out) 987 | { 988 | (void)map; 989 | kvell_get_submit(key->ptr, key->len, kv_crc32c_extend(key->hash32), kvell_cb, 0, 0); 990 | return out; 991 | } 992 | 993 | struct kv * 994 | kvelll_get(void * const map, const struct kref * const key, struct kv * const out) 995 | { 996 | struct vctr ** const vctrs = map; 997 | kvell_get_submit(key->ptr, key->len, kv_crc32c_extend(key->hash32), kvelll_cb, (u64)vctrs[2], time_nsec()); 998 | return out; 999 | } 1000 | 1001 | // PROBE 1002 | bool 1003 | kvell_probe(void * const map, const struct kref * const key) 1004 | { 1005 | (void)map; 1006 | kvell_get_submit(key->ptr, key->len, kv_crc32c_extend(key->hash32), kvell_cb, 0, 0); 1007 | return true; 1008 | } 1009 | 1010 | bool 1011 | kvelll_probe(void * const map, const struct kref * const key) 1012 | { 1013 | struct vctr ** const vctrs = map; 1014 | kvell_get_submit(key->ptr, key->len, kv_crc32c_extend(key->hash32), kvelll_cb, (u64)vctrs[2], time_nsec()); 1015 | return true; 1016 | } 1017 | 1018 | // SET 1019 | bool 1020 | kvell_put(void * const map, struct kv * const kv) 1021 | { 1022 | (void)map; 1023 | kvell_put_submit(kv_kptr(kv), kv->klen, kv->hash, kv_vptr(kv), kv->vlen, kvell_cb, 0, 0); 1024 | return true; 1025 | } 1026 | 1027 | bool 1028 | kvelll_put(void * const map, struct kv * const kv) 1029 | { 1030 | struct vctr ** const vctrs = map; 1031 | kvell_put_submit(kv_kptr(kv), kv->klen, kv->hash, kv_vptr(kv), kv->vlen, kvelll_cb, (u64)vctrs[0], time_nsec()); 1032 | return true; 1033 | } 1034 | 1035 | // DEL 1036 | bool 1037 | kvell_del(void * const map, const struct kref * const key) 1038 | { 1039 | (void)map; 1040 | kvell_del_submit(key->ptr, key->len, kv_crc32c_extend(key->hash32), kvell_cb, 0, 0); 1041 | return true; 1042 | } 1043 | 1044 | bool 1045 | kvelll_del(void * const map, const struct kref * const key) 1046 | { 1047 | struct vctr ** const vctrs = map; 1048 | kvell_del_submit(key->ptr, key->len, kv_crc32c_extend(key->hash32), kvelll_cb, (u64)vctrs[1], time_nsec()); 1049 | return true; 1050 | } 1051 | 1052 | void 1053 | kvell_clean(void * const map) 1054 | { 1055 | (void)map; 1056 | } 1057 | 1058 | void 1059 | kvell_destroy(void * const map) 1060 | { 1061 | sleep(2); // wait for async completion 1062 | struct vctr ** const vctrs = map; 1063 | for (u32 i = 0; i < 4; i++) 1064 | vctr_destroy(vctrs[i]); 1065 | free(map); 1066 | } 1067 | 1068 | void 1069 | kvell_latency_fprint(void * const map, FILE * const out) 1070 | { 1071 | sleep(1); // wait for all async completion 1072 | struct vctr ** const vctrs = map; 1073 | for (u32 vi = 0; vi < 4; vi++) { 1074 | struct vctr * const v = vctrs[vi]; 1075 | u64 sum = 0; 1076 | for (u64 i = 0; i < (1lu << 16); i++) 1077 | sum += vctr_get(v, i); 1078 | if (sum == 0) 1079 | continue; 1080 | 1081 | const u64 tot = sum; 1082 | const double totd = (double)tot; 1083 | sum = 0; 1084 | u64 last = 0; 1085 | fprintf(out, "[%u] (SET/DEL/GET/SCAN50)\ntime_us count delta cdf\n0 0 0 0.000\n", vi); 1086 | for (u64 i = 1; i < (1lu << 16); i++) { 1087 | const u64 tmp = vctr_get(v, i); 1088 | if (tmp) { 1089 | if ((i-1) != last) 1090 | fprintf(out, "%lu %lu %lu %.3lf\n", i-1, sum, 0lu, (double)sum * 100.0 / totd); 1091 | sum += tmp; 1092 | fprintf(out, "%lu %lu %lu %.3lf\n", i, sum, tmp, (double)sum * 100.0 / totd); 1093 | last = i; 1094 | } 1095 | } 1096 | fprintf(out, "total %lu\n", tot); 1097 | vctr_reset(v); 1098 | } 1099 | } 1100 | 1101 | void 1102 | kvell_fprint(void * const map, FILE * const out) 1103 | { 1104 | (void)map; 1105 | (void)out; 1106 | } 1107 | 1108 | void * 1109 | kvell_iter_create(void * const map) 1110 | { 1111 | return map; 1112 | } 1113 | 1114 | // SCAN50 1115 | void 1116 | kvell_iter_seek(void * const iter, const struct kref * const key) 1117 | { 1118 | (void)iter; 1119 | // XXX: YCSB: do everything in seek 1120 | kvell_scan50(key->ptr, key->len, kv_crc32c_extend(key->hash32), kvell_cb, 0, 0); 1121 | } 1122 | 1123 | void 1124 | kvelll_iter_seek(void * const iter, const struct kref * const key) 1125 | { 1126 | struct vctr ** const vctrs = iter; 1127 | // XXX: YCSB: do everything in seek 1128 | kvell_scan50(key->ptr, key->len, kv_crc32c_extend(key->hash32), kvelll_cb, (u64)vctrs[3], time_nsec()); 1129 | } 1130 | 1131 | bool 1132 | kvell_iter_valid(void * const iter) 1133 | { 1134 | (void)iter; 1135 | return true; 1136 | } 1137 | 1138 | struct kv * 1139 | kvell_iter_peek(void * const iter, struct kv * const out) 1140 | { 1141 | (void)iter; 1142 | (void)out; 1143 | return out; 1144 | } 1145 | 1146 | void 1147 | kvell_iter_skip1(void * const iter) 1148 | { 1149 | (void)iter; 1150 | } 1151 | 1152 | void 1153 | kvell_iter_skip(void * const iter, const u32 nr) 1154 | { 1155 | (void)iter; 1156 | (void)nr; 1157 | } 1158 | 1159 | struct kv * 1160 | kvell_iter_next(void * const iter, struct kv * const out) 1161 | { 1162 | (void)iter; 1163 | (void)out; 1164 | return NULL; 1165 | } 1166 | 1167 | void 1168 | kvell_iter_destroy(void * const iter) 1169 | { 1170 | (void)iter; // is map 1171 | } 1172 | 1173 | const struct kvmap_api kvmap_api_kvell = { 1174 | .hashkey = true, 1175 | .ordered = true, 1176 | .threadsafe = true, 1177 | .unique = true, 1178 | .put = kvell_put, 1179 | .get = kvell_get, 1180 | .probe = kvell_probe, 1181 | .del = kvell_del, 1182 | .iter_create = kvell_iter_create, 1183 | .iter_seek = kvell_iter_seek, 1184 | .iter_valid = kvell_iter_valid, 1185 | .iter_peek = kvell_iter_peek, 1186 | .iter_skip1 = kvell_iter_skip1, 1187 | .iter_skip = kvell_iter_skip, 1188 | .iter_next = kvell_iter_next, 1189 | .iter_destroy = kvell_iter_destroy, 1190 | .clean = kvell_clean, 1191 | .destroy = kvell_destroy, 1192 | .fprint = kvell_fprint, 1193 | }; 1194 | 1195 | const struct kvmap_api kvmap_api_kvelll = { 1196 | .hashkey = true, 1197 | .ordered = true, 1198 | .threadsafe = true, 1199 | .unique = true, 1200 | .async = true, 1201 | .put = kvelll_put, 1202 | .get = kvelll_get, 1203 | .probe = kvelll_probe, 1204 | .del = kvelll_del, 1205 | .iter_create = kvell_iter_create, 1206 | .iter_seek = kvelll_iter_seek, 1207 | .iter_valid = kvell_iter_valid, 1208 | .iter_peek = kvell_iter_peek, 1209 | .iter_skip1 = kvell_iter_skip1, 1210 | .iter_skip = kvell_iter_skip, 1211 | .iter_next = kvell_iter_next, 1212 | .iter_destroy = kvell_iter_destroy, 1213 | .clean = kvell_clean, 1214 | .destroy = kvell_destroy, 1215 | .fprint = kvell_latency_fprint, 1216 | }; 1217 | 1218 | static void * 1219 | kvell_kvmap_api_create(const char * const name, const struct kvmap_mm * const mm, char ** args) 1220 | { 1221 | (void)mm; 1222 | if (0 == strcmp(name, "kvell") || 0 == strcmp(name, "kvelll")) 1223 | return kvell_create(args); 1224 | else 1225 | return NULL; 1226 | } 1227 | 1228 | __attribute__((constructor)) 1229 | static void 1230 | kvell_kvmap_api_init(void) 1231 | { 1232 | kvmap_api_register(5, "kvell", " ", kvell_kvmap_api_create, &kvmap_api_kvell); 1233 | kvmap_api_register(5, "kvelll", " ", kvell_kvmap_api_create, &kvmap_api_kvelll); 1234 | } 1235 | #endif // KVELL 1236 | // }}} kvell 1237 | 1238 | // vim:fdm=marker 1239 | -------------------------------------------------------------------------------- /c/ord.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016--2021 Wu, Xingbo 3 | * 4 | * All rights reserved. No warranty, explicit or implicit, provided. 5 | */ 6 | #pragma once 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | // skiplist {{{ 13 | struct skiplist; 14 | 15 | extern struct skiplist * 16 | skiplist_create(const struct kvmap_mm * const mm); 17 | 18 | extern struct kv * 19 | skiplist_get(struct skiplist * const list, const struct kref * const key, struct kv * const out); 20 | 21 | extern bool 22 | skiplist_probe(struct skiplist * const list, const struct kref * const key); 23 | 24 | extern bool 25 | skiplist_put(struct skiplist * const list, struct kv * const kv); 26 | 27 | extern bool 28 | skipsafe_put(struct skiplist * const list, struct kv * const kv); 29 | 30 | extern bool 31 | skiplist_merge(struct skiplist * const list, const struct kref * const kref, 32 | kv_merge_func uf, void * const priv); 33 | 34 | extern bool 35 | skipsafe_merge(struct skiplist * const list, const struct kref * const kref, 36 | kv_merge_func uf, void * const priv); 37 | 38 | extern bool 39 | skiplist_inp(struct skiplist * const list, const struct kref * const key, 40 | kv_inp_func uf, void * const priv); 41 | 42 | extern bool 43 | skiplist_del(struct skiplist * const list, const struct kref * const key); 44 | 45 | extern void 46 | skiplist_clean(struct skiplist * const list); 47 | 48 | extern void 49 | skiplist_destroy(struct skiplist * const list); 50 | 51 | extern void 52 | skiplist_fprint(struct skiplist * const list, FILE * const out); 53 | 54 | struct skiplist_iter; 55 | 56 | extern struct skiplist_iter * 57 | skiplist_iter_create(struct skiplist * const list); 58 | 59 | extern void 60 | skiplist_iter_seek(struct skiplist_iter * const iter, const struct kref * const key); 61 | 62 | extern bool 63 | skiplist_iter_valid(struct skiplist_iter * const iter); 64 | 65 | extern struct kv * 66 | skiplist_iter_peek(struct skiplist_iter * const iter, struct kv * const out); 67 | 68 | extern bool 69 | skiplist_iter_kref(struct skiplist_iter * const iter, struct kref * const kref); 70 | 71 | extern bool 72 | skiplist_iter_kvref(struct skiplist_iter * const iter, struct kvref * const kvref); 73 | 74 | extern void 75 | skiplist_iter_skip1(struct skiplist_iter * const iter); 76 | 77 | extern void 78 | skiplist_iter_skip(struct skiplist_iter * const iter, const u32 nr); 79 | 80 | extern struct kv * 81 | skiplist_iter_next(struct skiplist_iter * const iter, struct kv * const out); 82 | 83 | extern bool 84 | skiplist_iter_inp(struct skiplist_iter * const iter, kv_inp_func uf, void * const priv); 85 | 86 | extern void 87 | skiplist_iter_destroy(struct skiplist_iter * const iter); 88 | 89 | extern const struct kvmap_api kvmap_api_skiplist; 90 | extern const struct kvmap_api kvmap_api_skipsafe; 91 | // }}} skiplist 92 | 93 | // rdb {{{ 94 | #ifdef ROCKSDB 95 | struct rdb; 96 | struct rdb_iter; 97 | 98 | extern struct rdb * 99 | rdb_create(const char * const path, const u64 cache_size_mb); 100 | 101 | extern struct kv * 102 | rdb_get(struct rdb * const map, const struct kref * const key, struct kv * const out); 103 | 104 | extern bool 105 | rdb_probe(struct rdb * const map, const struct kref * const key); 106 | 107 | extern bool 108 | rdb_put(struct rdb * const map, struct kv * const kv); 109 | 110 | extern bool 111 | rdb_del(struct rdb * const map, const struct kref * const key); 112 | 113 | extern void 114 | rdb_destroy(struct rdb * const map); 115 | 116 | extern void 117 | rdb_fprint(struct rdb * const map, FILE * const out); 118 | 119 | extern struct rdb_iter * 120 | rdb_iter_create(struct rdb * const map); 121 | 122 | extern void 123 | rdb_iter_seek(struct rdb_iter * const iter, const struct kref * const key); 124 | 125 | extern bool 126 | rdb_iter_valid(struct rdb_iter * const iter); 127 | 128 | extern struct kv * 129 | rdb_iter_peek(struct rdb_iter * const iter, struct kv * const out); 130 | 131 | extern void 132 | rdb_iter_skip1(struct rdb_iter * const iter); 133 | 134 | extern void 135 | rdb_iter_skip(struct rdb_iter * const iter, const u32 nr); 136 | 137 | extern struct kv * 138 | rdb_iter_next(struct rdb_iter * const iter, struct kv * const out); 139 | 140 | extern void 141 | rdb_iter_destroy(struct rdb_iter * const iter); 142 | 143 | extern const struct kvmap_api kvmap_api_rdb; 144 | #endif // ROCKSDB 145 | // }}} rdb 146 | 147 | #ifdef __cplusplus 148 | } 149 | #endif 150 | // vim:fdm=marker 151 | -------------------------------------------------------------------------------- /c/wh.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016--2021 Wu, Xingbo 3 | * 4 | * All rights reserved. No warranty, explicit or implicit, provided. 5 | */ 6 | #pragma once 7 | 8 | #ifdef __cplusplus 9 | extern "C" { 10 | #endif 11 | 12 | struct wormhole; 13 | struct wormref; 14 | 15 | // wormhole {{{ 16 | // the wh created by wormhole_create() can work with all of safe/unsafe operations. 17 | extern struct wormhole * 18 | wormhole_create(const struct kvmap_mm * const mm); 19 | 20 | // the wh created by whunsafe_create() can only work with the unsafe operations. 21 | extern struct wormhole * 22 | whunsafe_create(const struct kvmap_mm * const mm); 23 | 24 | extern struct kv * 25 | wormhole_get(struct wormref * const ref, const struct kref * const key, struct kv * const out); 26 | 27 | extern bool 28 | wormhole_probe(struct wormref * const ref, const struct kref * const key); 29 | 30 | extern bool 31 | wormhole_put(struct wormref * const ref, struct kv * const kv); 32 | 33 | extern bool 34 | wormhole_merge(struct wormref * const ref, const struct kref * const kref, 35 | kv_merge_func uf, void * const priv); 36 | 37 | extern bool 38 | wormhole_inpr(struct wormref * const ref, const struct kref * const key, 39 | kv_inp_func uf, void * const priv); 40 | 41 | extern bool 42 | wormhole_inpw(struct wormref * const ref, const struct kref * const key, 43 | kv_inp_func uf, void * const priv); 44 | 45 | extern bool 46 | wormhole_del(struct wormref * const ref, const struct kref * const key); 47 | 48 | extern u64 49 | wormhole_delr(struct wormref * const ref, const struct kref * const start, 50 | const struct kref * const end); 51 | 52 | extern struct wormhole_iter * 53 | wormhole_iter_create(struct wormref * const ref); 54 | 55 | extern void 56 | wormhole_iter_seek(struct wormhole_iter * const iter, const struct kref * const key); 57 | 58 | extern bool 59 | wormhole_iter_valid(struct wormhole_iter * const iter); 60 | 61 | extern struct kv * 62 | wormhole_iter_peek(struct wormhole_iter * const iter, struct kv * const out); 63 | 64 | extern bool 65 | wormhole_iter_kref(struct wormhole_iter * const iter, struct kref * const kref); 66 | 67 | extern bool 68 | wormhole_iter_kvref(struct wormhole_iter * const iter, struct kvref * const kvref); 69 | 70 | extern void 71 | wormhole_iter_skip1(struct wormhole_iter * const iter); 72 | 73 | extern void 74 | wormhole_iter_skip(struct wormhole_iter * const iter, const u32 nr); 75 | 76 | extern struct kv * 77 | wormhole_iter_next(struct wormhole_iter * const iter, struct kv * const out); 78 | 79 | extern bool 80 | wormhole_iter_inp(struct wormhole_iter * const iter, kv_inp_func uf, void * const priv); 81 | 82 | extern void 83 | wormhole_iter_park(struct wormhole_iter * const iter); 84 | 85 | extern void 86 | wormhole_iter_destroy(struct wormhole_iter * const iter); 87 | 88 | extern struct wormref * 89 | wormhole_ref(struct wormhole * const map); 90 | 91 | extern struct wormhole * 92 | wormhole_unref(struct wormref * const ref); 93 | 94 | extern void 95 | wormhole_park(struct wormref * const ref); 96 | 97 | extern void 98 | wormhole_resume(struct wormref * const ref); 99 | 100 | extern void 101 | wormhole_refresh_qstate(struct wormref * const ref); 102 | 103 | // clean with more threads 104 | extern void 105 | wormhole_clean_th(struct wormhole * const map, const u32 nr_threads); 106 | 107 | extern void 108 | wormhole_clean(struct wormhole * const map); 109 | 110 | extern void 111 | wormhole_destroy(struct wormhole * const map); 112 | 113 | // safe API (no need to refresh qstate) 114 | 115 | extern struct kv * 116 | whsafe_get(struct wormref * const ref, const struct kref * const key, struct kv * const out); 117 | 118 | extern bool 119 | whsafe_probe(struct wormref * const ref, const struct kref * const key); 120 | 121 | extern bool 122 | whsafe_put(struct wormref * const ref, struct kv * const kv); 123 | 124 | extern bool 125 | whsafe_merge(struct wormref * const ref, const struct kref * const kref, 126 | kv_merge_func uf, void * const priv); 127 | 128 | extern bool 129 | whsafe_inpr(struct wormref * const ref, const struct kref * const key, 130 | kv_inp_func uf, void * const priv); 131 | 132 | extern bool 133 | whsafe_inpw(struct wormref * const ref, const struct kref * const key, 134 | kv_inp_func uf, void * const priv); 135 | 136 | extern bool 137 | whsafe_del(struct wormref * const ref, const struct kref * const key); 138 | 139 | extern u64 140 | whsafe_delr(struct wormref * const ref, const struct kref * const start, 141 | const struct kref * const end); 142 | 143 | // use wormhole_iter_create 144 | extern void 145 | whsafe_iter_seek(struct wormhole_iter * const iter, const struct kref * const key); 146 | 147 | extern struct kv * 148 | whsafe_iter_peek(struct wormhole_iter * const iter, struct kv * const out); 149 | 150 | // use wormhole_iter_valid 151 | // use wormhole_iter_peek 152 | // use wormhole_iter_kref 153 | // use wormhole_iter_kvref 154 | // use wormhole_iter_skip1 155 | // use wormhole_iter_skip 156 | // use wormhole_iter_next 157 | // use wormhole_iter_inp 158 | 159 | extern void 160 | whsafe_iter_park(struct wormhole_iter * const iter); 161 | 162 | extern void 163 | whsafe_iter_destroy(struct wormhole_iter * const iter); 164 | 165 | extern struct wormref * 166 | whsafe_ref(struct wormhole * const map); 167 | 168 | // use wormhole_unref 169 | 170 | // unsafe API 171 | 172 | extern struct kv * 173 | whunsafe_get(struct wormhole * const map, const struct kref * const key, struct kv * const out); 174 | 175 | extern bool 176 | whunsafe_probe(struct wormhole * const map, const struct kref * const key); 177 | 178 | extern bool 179 | whunsafe_put(struct wormhole * const map, struct kv * const kv); 180 | 181 | extern bool 182 | whunsafe_merge(struct wormhole * const map, const struct kref * const kref, 183 | kv_merge_func uf, void * const priv); 184 | 185 | extern bool 186 | whunsafe_inp(struct wormhole * const map, const struct kref * const key, 187 | kv_inp_func uf, void * const priv); 188 | 189 | extern bool 190 | whunsafe_del(struct wormhole * const map, const struct kref * const key); 191 | 192 | extern u64 193 | whunsafe_delr(struct wormhole * const map, const struct kref * const start, 194 | const struct kref * const end); 195 | 196 | extern struct wormhole_iter * 197 | whunsafe_iter_create(struct wormhole * const map); 198 | 199 | extern void 200 | whunsafe_iter_seek(struct wormhole_iter * const iter, const struct kref * const key); 201 | 202 | // unsafe iter_valid: use wormhole_iter_valid 203 | // unsafe iter_peek: use wormhole_iter_peek 204 | // unsafe iter_kref: use wormhole_iter_kref 205 | 206 | extern void 207 | whunsafe_iter_skip1(struct wormhole_iter * const iter); 208 | 209 | extern void 210 | whunsafe_iter_skip(struct wormhole_iter * const iter, const u32 nr); 211 | 212 | extern struct kv * 213 | whunsafe_iter_next(struct wormhole_iter * const iter, struct kv * const out); 214 | 215 | // unsafe iter_inp: use wormhole_iter_inp 216 | 217 | extern void 218 | whunsafe_iter_destroy(struct wormhole_iter * const iter); 219 | 220 | extern void 221 | wormhole_fprint(struct wormhole * const map, FILE * const out); 222 | 223 | extern const struct kvmap_api kvmap_api_wormhole; 224 | extern const struct kvmap_api kvmap_api_whsafe; 225 | extern const struct kvmap_api kvmap_api_whunsafe; 226 | // }}} wormhole 227 | 228 | // debug {{{ 229 | // verify & debugging 230 | #ifdef WORMHOLE_DEBUG 231 | extern bool 232 | wormhole_merge_at(struct wormref * const ref, const struct kref * const key); 233 | 234 | extern bool 235 | wormhole_split_at(struct wormref * const ref, const struct kref * const key); 236 | 237 | extern void 238 | wormhole_sync_at(struct wormref * const ref, const struct kref * const key); 239 | 240 | extern void * 241 | wormhole_jump_leaf_only(struct wormhole * const map, const struct kref * const key); 242 | 243 | void 244 | wormhole_fprint_verbose(struct wormhole * const map, FILE * const out); 245 | 246 | extern bool 247 | wormhole_verify(struct wormhole * const map); 248 | 249 | extern void 250 | wormhole_dump_memory(struct wormhole * const map, const char * const filename, const char * const opt); 251 | 252 | extern void 253 | wormhole_print_meta_anchors(struct wormhole * const map); 254 | 255 | extern void 256 | wormhole_print_leaf_anchors(struct wormhole * const map, const bool use_hex); 257 | 258 | extern void 259 | wormhole_print_meta_lrmost(struct wormhole * const map); 260 | 261 | extern void 262 | wormhole_print_leaf_keys(struct wormhole * const map, const bool use_hex); 263 | #endif // WORMHOLE_DEBUG 264 | // }}} debug 265 | 266 | // wh {{{ 267 | extern struct wormhole * 268 | wh_create(void); 269 | 270 | extern struct wormref * 271 | wh_ref(struct wormhole * const wh); 272 | 273 | extern void 274 | wh_unref(struct wormref * const ref); 275 | 276 | extern void 277 | wh_park(struct wormref * const ref); 278 | 279 | extern void 280 | wh_resume(struct wormref * const ref); 281 | 282 | extern void 283 | wh_clean(struct wormhole * const map); 284 | 285 | extern void 286 | wh_destroy(struct wormhole * const map); 287 | 288 | extern bool 289 | wh_put(struct wormref * const ref, const void * const kbuf, const u32 klen, 290 | const void * const vbuf, const u32 vlen); 291 | 292 | extern bool 293 | wh_del(struct wormref * const ref, const void * const kbuf, const u32 klen); 294 | 295 | extern bool 296 | wh_probe(struct wormref * const ref, const void * const kbuf, const u32 klen); 297 | 298 | extern bool 299 | wh_get(struct wormref * const ref, const void * const kbuf, const u32 klen, 300 | void * const vbuf_out, const u32 vbuf_size, u32 * const vlen_out); 301 | 302 | extern bool 303 | wh_inpr(struct wormref * const ref, const void * const kbuf, const u32 klen, 304 | kv_inp_func uf, void * const priv); 305 | 306 | extern bool 307 | wh_inpw(struct wormref * const ref, const void * const kbuf, const u32 klen, 308 | kv_inp_func uf, void * const priv); 309 | 310 | extern bool 311 | wh_merge(struct wormref * const ref, const void * const kbuf, const u32 klen, 312 | kv_merge_func uf, void * const priv); 313 | 314 | extern u64 315 | wh_delr(struct wormref * const ref, const void * const kbuf_start, const u32 klen_start, 316 | const void * const kbuf_end, const u32 klen_end); 317 | 318 | extern struct wormhole_iter * 319 | wh_iter_create(struct wormref * const ref); 320 | 321 | extern void 322 | wh_iter_seek(struct wormhole_iter * const iter, const void * const kbuf, const u32 klen); 323 | 324 | extern bool 325 | wh_iter_valid(struct wormhole_iter * const iter); 326 | 327 | extern bool 328 | wh_iter_peek(struct wormhole_iter * const iter, 329 | void * const kbuf_out, const u32 kbuf_size, u32 * const klen_out, 330 | void * const vbuf_out, const u32 vbuf_size, u32 * const vlen_out); 331 | 332 | extern void 333 | wh_iter_skip1(struct wormhole_iter * const iter); 334 | 335 | extern void 336 | wh_iter_skip(struct wormhole_iter * const iter, const u32 nr); 337 | 338 | extern bool 339 | wh_iter_inp(struct wormhole_iter * const iter, kv_inp_func uf, void * const priv); 340 | 341 | extern void 342 | wh_iter_park(struct wormhole_iter * const iter); 343 | 344 | extern void 345 | wh_iter_destroy(struct wormhole_iter * const iter); 346 | // }}} wh 347 | 348 | #ifdef __cplusplus 349 | } 350 | #endif 351 | // vim:fdm=marker 352 | -------------------------------------------------------------------------------- /c/ycsbtest.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2016--2021 Wu, Xingbo 3 | * 4 | * All rights reserved. No warranty, explicit or implicit, provided. 5 | */ 6 | #define _GNU_SOURCE 7 | 8 | #include "lib.h" 9 | #include "kv.h" 10 | 11 | struct priv { 12 | u32 klen; 13 | u32 vlen; 14 | u32 nscan; 15 | u32 cget; 16 | u32 cset; 17 | u32 cupd; 18 | u32 cscn; 19 | void * ref; 20 | void * iter; 21 | struct kv * tmp; 22 | struct kv * out; 23 | struct kv ** kvs; 24 | }; 25 | 26 | // load (use set) same to insert for latest dist. 27 | #define XSA ((0)) 28 | #define XSS ((1)) 29 | // update (use merge), same to rmw (workload f, rarely mentioned) 30 | #define XMA ((2)) 31 | #define XMS ((3)) 32 | // read (get) 33 | #define XGA ((4)) 34 | #define XGS ((5)) 35 | // scan (seek-next) 36 | #define XNA ((6)) 37 | #define XNS ((7)) 38 | // total number 39 | #define XNR ((8)) 40 | 41 | static bool 42 | kvmap_analyze(void * const passdata[2], const u64 dt, const struct vctr * const va, struct damp * const d, char * const out) 43 | { 44 | (void)passdata; 45 | size_t v[XNR]; 46 | for (u64 i = 0; i < XNR; i++) 47 | v[i] = vctr_get(va, i); 48 | 49 | const u64 nrop = v[XSA] + v[XMA] + v[XGA] + v[XNA]; 50 | const double mops = ((double)nrop) * 1e3 / ((double)dt); 51 | const bool done = damp_add_test(d, mops); 52 | const char * const pat = " set %zu %zu upd %zu %zu get %zu %zu scan %zu %zu mops %.4lf avg %.4lf ravg %.4lf\n"; 53 | sprintf(out, pat, v[XSA], v[XSS], v[XMA], v[XMS], v[XGA], v[XGS], v[XNA], v[XNS], mops, damp_avg(d), damp_ravg(d)); 54 | return done; 55 | } 56 | 57 | static struct kv * 58 | kvmap_merge_dummy(struct kv * const key0, void * const priv) 59 | { 60 | (void)key0; 61 | return (struct kv *)priv; 62 | } 63 | 64 | static void 65 | kvmap_batch(const struct forker_worker_info * const info, 66 | const struct priv * const priv, const u64 nr) 67 | { 68 | const struct kvmap_api * const api = (typeof(api))info->passdata[0]; 69 | void * const ref = priv->ref; 70 | struct vctr * const v = info->vctr; 71 | struct rgen * const gen = info->gen; 72 | rgen_next_func next = info->rgen_next; 73 | rgen_next_func next_write = info->rgen_next_write; 74 | struct kv * const tmp = priv->tmp; 75 | 76 | for (u64 i = 0; i < nr; i++) { 77 | const u32 p = random_u64() & 0xffffu; 78 | if (p < priv->cget) { // GET 79 | kv_refill_hex64_klen(tmp, next(gen), priv->klen, NULL, 0); 80 | vctr_add1(v, XGA); 81 | if (kvmap_kv_get(api, ref, tmp, priv->out)) 82 | vctr_add1(v, XGS); 83 | 84 | } else if (p < priv->cscn) { // SCAN 85 | kv_refill_hex64_klen(tmp, next(gen), priv->klen, NULL, 0); 86 | vctr_add1(v, XNA); 87 | 88 | void * const iter = priv->iter; 89 | debug_assert(iter); 90 | 91 | kvmap_kv_iter_seek(api, iter, tmp); 92 | for (u32 k = 0; k < priv->nscan; k++) 93 | api->iter_next(iter, priv->out); 94 | if (api->iter_valid(iter)) 95 | vctr_add1(v, XNS); 96 | 97 | // may need to park 98 | if (api->iter_park) 99 | api->iter_park(iter); 100 | 101 | } else if (p < priv->cset) { // SET 102 | kv_refill_hex64_klen(tmp, next_write(gen), priv->klen, NULL, 0); 103 | tmp->vlen = priv->vlen; 104 | vctr_add1(v, XSA); 105 | if (kvmap_kv_put(api, ref, tmp)) 106 | vctr_add1(v, XSS); 107 | 108 | } else { // UPDATE (RMW) 109 | kv_refill_hex64_klen(tmp, next_write(gen), priv->klen, NULL, 0); 110 | tmp->vlen = priv->vlen; 111 | vctr_add1(v, XMA); 112 | if (api->merge) { // use merge() 113 | if (kvmap_kv_merge(api, ref, tmp, kvmap_merge_dummy, tmp)) 114 | vctr_add1(v, XMS); 115 | } else { // GET & PUT 116 | (void)kvmap_kv_get(api, ref, tmp, priv->out); // read 117 | if (kvmap_kv_put(api, ref, tmp)) // write 118 | vctr_add1(v, XMS); 119 | } 120 | } 121 | } 122 | } 123 | 124 | static void * 125 | kvmap_worker(void * const ptr) 126 | { 127 | struct forker_worker_info * const info = (typeof(info))ptr; 128 | srandom_u64(info->seed); 129 | const struct kvmap_api * const api = (typeof(api))info->passdata[0]; 130 | 131 | struct priv p = {}; 132 | const u32 pset = a2u32(info->argv[0]); 133 | const u32 pupd = a2u32(info->argv[1]); 134 | const u32 pget = a2u32(info->argv[2]); 135 | const u32 pscn = a2u32(info->argv[3]); 136 | // scaled to 65536 137 | p.cget = pget * 65536 / 100; // 1st 138 | p.cscn = (pget + pscn) * 65536 / 100; // 2nd 139 | p.cset = (pget + pscn + pset) * 65536 / 100; // 3rd 140 | p.cupd = 65536; // not used 141 | (void)pupd; 142 | 143 | p.klen = a2u32(info->argv[4]); 144 | p.vlen = a2u32(info->argv[5]); 145 | p.nscan = a2u32(info->argv[6]); 146 | p.ref = kvmap_ref(api, info->passdata[1]); 147 | 148 | if (pscn) { 149 | p.iter = api->iter_create(p.ref); 150 | if (api->iter_park) 151 | api->iter_park(p.iter); 152 | } 153 | 154 | const u64 outlen = sizeof(struct kv) + p.klen + p.vlen + 4096; 155 | p.tmp = yalloc(outlen); 156 | debug_assert(p.tmp); 157 | memset(p.tmp, 0, outlen); 158 | p.out = yalloc(outlen); 159 | debug_assert(p.out); 160 | 161 | if (info->end_type == FORKER_END_TIME) { 162 | do { 163 | kvmap_batch(info, &p, 1lu << 14); 164 | } while (time_nsec() < info->end_magic); 165 | } else if (info->end_type == FORKER_END_COUNT) { 166 | kvmap_batch(info, &p, info->end_magic); 167 | } 168 | 169 | if (pscn) 170 | api->iter_destroy(p.iter); 171 | 172 | kvmap_unref(api, p.ref); 173 | free(p.tmp); 174 | free(p.out); 175 | return NULL; 176 | } 177 | 178 | #define NARGS ((7)) 179 | static void 180 | maptest_help_message(void) 181 | { 182 | fprintf(stderr, "%s Usage: {api ... {rgen ... {pass ...}}}\n", __func__); 183 | kvmap_api_helper_message(); 184 | forker_passes_message(); 185 | fprintf(stderr, "%s wargs[%d]: \n", __func__, NARGS); 186 | fprintf(stderr, "%s load kv samples at cpu: MAPTEST_KVLOAD_CPU=; default:1\n", __func__); 187 | } 188 | 189 | static int 190 | test_kvmap(const int argc, char ** const argv) 191 | { 192 | const struct kvmap_api * api = NULL; 193 | void * map = NULL; 194 | const int n1 = kvmap_api_helper(argc, argv, NULL, &api, &map); 195 | if (n1 < 0) 196 | return n1; 197 | 198 | char *pref[64] = {}; 199 | for (int i = 0; i < n1; i++) { 200 | pref[i] = argv[i]; 201 | } 202 | pref[n1] = NULL; 203 | 204 | struct pass_info pi = {}; 205 | pi.passdata[0] = (void *)api; 206 | pi.passdata[1] = map; 207 | pi.vctr_size = XNR; 208 | pi.wf = kvmap_worker; 209 | pi.af = kvmap_analyze; 210 | const int n2 = forker_passes(argc - n1, argv + n1, pref, &pi, NARGS); 211 | 212 | if (api->fprint) 213 | api->fprint(map, stderr); 214 | 215 | api->destroy(map); 216 | if (n2 < 0) { 217 | return n2; 218 | } else { 219 | return n1 + n2; 220 | } 221 | } 222 | 223 | int 224 | main(int argc, char ** argv) 225 | { 226 | if (argc < 3) { 227 | maptest_help_message(); 228 | exit(0); 229 | } 230 | 231 | const bool r = forker_main(argc - 1, argv + 1, test_kvmap); 232 | if (r == false) 233 | maptest_help_message(); 234 | return 0; 235 | } 236 | -------------------------------------------------------------------------------- /flexdb.h: -------------------------------------------------------------------------------- 1 | #ifndef _FLEXDB_H 2 | #define _FLEXDB_H 3 | 4 | #include "c/ctypes.h" 5 | #include "c/lib.h" 6 | #include "c/kv.h" 7 | #include "c/ord.h" 8 | #include "c/wh.h" 9 | #include "flexfile.h" 10 | 11 | #define FLEXDB_MAX_KV_SIZE (4u << 10) // max size 4K 12 | 13 | #define FLEXDB_TREE_LEAF_CAP (122u) 14 | #define FLEXDB_TREE_INTERNAL_CAP (40u) 15 | 16 | // thresholds by both count and size 17 | #define FLEXDB_TREE_SPARSE_INTERVAL_COUNT (16u) 18 | #define FLEXDB_TREE_SPARSE_INTERVAL_SIZE (16u << 10) 19 | 20 | #define FLEXDB_MEMTABLE_CAP (1024u << 20) // 1G memtable 21 | #define FLEXDB_MEMTABLE_FLUSH_BATCH (1024u) 22 | #define FLEXDB_MEMTABLE_FLUSH_TIME (5u) // every 5s 23 | #define FLEXDB_MEMTABLE_LOG_BUFFER_CAP (4lu << 20) // 4MB buffer 24 | 25 | #define FLEXDB_CACHE_PARTITION_BITS (10u) // 1024 26 | #define FLEXDB_CACHE_PARTITION_COUNT (1u << FLEXDB_CACHE_PARTITION_BITS) 27 | #define FLEXDB_CACHE_PARTITION_MASK (FLEXDB_CACHE_PARTITION_COUNT - 1) 28 | #define FLEXDB_CACHE_ENTRY_CHANCE (2u) 29 | #define FLEXDB_CACHE_ENTRY_CHANCE_WARMUP (3u) 30 | #define FLEXDB_CACHE_ENTRY_LOADING (~(u32)0u) 31 | 32 | #define FLEXDB_UNSORTED_WRITE_QUOTA_COUNT (15u) 33 | #define FLEXDB_TREE_SPARSE_INTERVAL (FLEXDB_TREE_SPARSE_INTERVAL_COUNT + FLEXDB_UNSORTED_WRITE_QUOTA_COUNT + 1) 34 | 35 | #define FLEXDB_LOCK_SHARDING_BITS (4u) 36 | #define FLEXDB_LOCK_SHARDING_COUNT (1u << FLEXDB_LOCK_SHARDING_BITS) 37 | #define FLEXDB_LOCK_SHARDING_MASK (FLEXDB_LOCK_SHARDING_COUNT - 1) 38 | 39 | #define FLEXDB_RECOVERY_WORKER_COUNT (4u) 40 | #define FLEXDB_RECOVERY_SANITY_CHECK (0) 41 | 42 | // #define FLEXDB_USE_RRING 43 | 44 | static_assert(FLEXDB_MAX_KV_SIZE <= FLEXFILE_MAX_EXTENT_SIZE, "one kv must fit in one extent, no more"); 45 | // static_assert(FLEXDB_MAX_KV_SIZE < (16u << 10), "u16 for kv anchor size, so cap it at quarter"); 46 | static_assert(FLEXDB_UNSORTED_WRITE_QUOTA_COUNT < 128, "an u7 for blind writes"); 47 | static_assert(FLEXDB_TREE_SPARSE_INTERVAL < 255, "sparse interval < 255"); 48 | // static_assert(FLEXDB_TREE_SPARSE_INTERVAL_SIZE < ((64u << 10) - FLEXDB_MAX_KV_SIZE), "interval size < 64k"); 49 | // static_assert(FLEXDB_TREE_SPARSE_INTERVAL_SIZE + FLEXDB_UNSORTED_WRITE_QUOTA_COUNT * FLEXDB_MAX_KV_SIZE < (1u << 16), 50 | // "u16 for an anchor's (p)size, so the total size should be smaller"); 51 | static_assert(FLEXDB_TREE_SPARSE_INTERVAL % 16 == 0, "make the interval array aligned for AVX/AVX2"); 52 | 53 | struct flexdb_tree_anchor { 54 | struct kv *key; 55 | u32 loff; 56 | u32 psize; 57 | u8 unsorted; 58 | u8 padding1[7]; 59 | struct flexdb_cache_entry *cache_entry; // :64 60 | }; 61 | 62 | struct flexdb_tree_leaf_entry { 63 | struct flexdb_tree_anchor *anchors[FLEXDB_TREE_LEAF_CAP]; 64 | struct flexdb_tree_node *prev; 65 | struct flexdb_tree_node *next; 66 | }; 67 | 68 | struct flexdb_tree_internal_entry { 69 | struct kv *pivots[FLEXDB_TREE_INTERNAL_CAP]; // n pivots 70 | struct { 71 | struct flexdb_tree_node *node; 72 | s64 shift; 73 | } children[FLEXDB_TREE_INTERNAL_CAP+1]; // n+1 children 74 | }; 75 | 76 | struct flexdb_tree_node { 77 | u32 parent_id; 78 | u32 count; 79 | u8 is_leaf; 80 | u8 padding1[3]; 81 | struct flexdb_tree *tree; 82 | struct flexdb_tree_node *parent; 83 | union { 84 | struct flexdb_tree_internal_entry internal_entry; 85 | struct flexdb_tree_leaf_entry leaf_entry; 86 | }; 87 | }; 88 | 89 | static_assert(sizeof(struct flexdb_tree_node) == 1024, "1k node for flexdb index"); 90 | 91 | struct flexdb_tree_node_handler { 92 | struct flexdb_tree_node *node; 93 | s64 shift; 94 | u32 idx; 95 | }; 96 | 97 | struct flexdb_tree { 98 | struct flexdb *db; 99 | struct flexdb_tree_node *root; 100 | struct flexdb_tree_node *leaf_head; 101 | struct slab *node_slab; 102 | struct slab *anchor_slab; 103 | }; 104 | 105 | struct flexdb_cache_entry { 106 | // 64B 107 | u16 kv_fps[FLEXDB_TREE_SPARSE_INTERVAL]; // fingerprints for each cached key 108 | // 24B here to refcnt 109 | struct flexdb_tree_anchor *anchor; 110 | u32 size; 111 | u8 count; 112 | volatile u8 loading; 113 | u8 padding1[1]; 114 | // for replacement 115 | u8 partial; // not enabled 116 | 117 | u16 frag; 118 | u16 access; 119 | au32 refcnt; 120 | struct kv *kv_interval[FLEXDB_TREE_SPARSE_INTERVAL]; 121 | struct flexdb_cache_entry *prev; 122 | struct flexdb_cache_entry *next; 123 | u64 padding2[3]; 124 | }; 125 | static_assert(sizeof(struct flexdb_cache_entry) % 64 == 0, "cacheline aligned for slab"); 126 | 127 | struct flexdb_cache_partition { 128 | u64 cap; 129 | struct slab *entry_slab; 130 | struct flexdb_cache *cache; 131 | struct flexdb_cache_entry *tick; // clock pointer 132 | u64 padding1[7]; 133 | spinlock spinlock; 134 | u64 padding2[7]; 135 | au64 size; 136 | u64 padding3[7]; 137 | }; 138 | 139 | struct flexdb_cache { 140 | struct flexdb *db; 141 | u64 cap; 142 | // for replacement 143 | struct flexdb_cache_partition partitions[FLEXDB_CACHE_PARTITION_COUNT]; 144 | }; 145 | 146 | struct flexdb_memtable { 147 | struct flexdb *db; 148 | void *map; 149 | u8 *log_buffer; 150 | u32 log_buffer_size; 151 | int log_fd; 152 | volatile u8 hidden; 153 | u64 padding1[7]; 154 | au32 size; 155 | u64 padding2[7]; 156 | spinlock log_buffer_lock; 157 | u64 padding3[7]; 158 | }; 159 | 160 | struct flexdb_iterator { 161 | struct flexdb_ref *dbref; 162 | void *miter; 163 | struct kvref kvref; 164 | struct { 165 | u8 parked; 166 | u8 a; 167 | u8 h1; 168 | u8 h2; 169 | u32 mt_lockid; 170 | u32 ff_lockid; 171 | } status; 172 | }; 173 | 174 | struct flexdb { 175 | char *path; 176 | struct flexfile *flexfile; 177 | struct flexdb_tree *tree; 178 | struct flexdb_memtable memtables[2]; 179 | volatile u32 active_memtable; // active memtable 0 or 1, the other one is read-only 180 | volatile au32 refcnt; 181 | struct flexdb_cache *cache; 182 | struct { 183 | pthread_t thread; 184 | volatile u64 immediate_work; 185 | au64 work; 186 | } flush_worker; 187 | u8 *kvbuf1; 188 | u8 *kvbuf2; 189 | u8 *itvbuf; 190 | void *priv; // the rring placeholder 191 | u64 padding1[4]; 192 | union { 193 | u64 _[8]; 194 | rwlock lock; 195 | } rwlock_memtable[FLEXDB_LOCK_SHARDING_COUNT]; 196 | union { 197 | u64 _[8]; 198 | rwlock lock; 199 | } rwlock_flexfile[FLEXDB_LOCK_SHARDING_COUNT]; 200 | }; 201 | 202 | struct flexdb_ref { 203 | struct flexdb *db; 204 | u8 *kvbuf; 205 | u8 *itvbuf; 206 | void *mrefs[2]; 207 | void *priv; // the rring placeholder 208 | }; 209 | 210 | extern struct flexdb *flexdb_open(const char *const path, const u64 cache_cap_mb); 211 | 212 | extern void flexdb_close(struct flexdb *const db); 213 | 214 | extern struct flexdb_ref *flexdb_ref(struct flexdb *const db); 215 | 216 | extern struct flexdb *flexdb_deref(struct flexdb_ref *const dbref); 217 | 218 | extern int flexdb_put(struct flexdb_ref *const dbref, struct kv *const kv); 219 | 220 | extern struct kv *flexdb_get(struct flexdb_ref *const dbref, const struct kref* const kref, struct kv *const out); 221 | 222 | extern unsigned int flexdb_probe(struct flexdb_ref *const dbref, const struct kref *const kref); 223 | 224 | extern int flexdb_delete(struct flexdb_ref *const dbref, const struct kref *const kref); 225 | 226 | extern void flexdb_sync(struct flexdb_ref *const dbref); 227 | 228 | extern struct kv *flexdb_read_kv(const struct flexfile_handler *const ffh, u8 *const buf, struct kv *const out); 229 | 230 | extern struct flexdb_iterator *flexdb_iterator_create(struct flexdb_ref *const dbref); 231 | 232 | extern void flexdb_iterator_seek(struct flexdb_iterator *const iter, const struct kref *const kref); 233 | 234 | extern bool flexdb_iterator_valid(const struct flexdb_iterator *const iter); 235 | 236 | extern struct kv *flexdb_iterator_peek(const struct flexdb_iterator *const iter, struct kv *const out); 237 | 238 | extern void flexdb_iterator_skip(struct flexdb_iterator *const iter, const u64 step); 239 | 240 | extern struct kv *flexdb_iterator_next(struct flexdb_iterator *const iter, struct kv *const out); 241 | 242 | extern void flexdb_iterator_destroy(struct flexdb_iterator *const iter); 243 | 244 | extern void flexdb_iterator_park(struct flexdb_iterator *const iter); 245 | 246 | extern void flexdb_fprint(const struct flexdb *const db, FILE *const f); 247 | 248 | extern bool flexdb_merge( 249 | struct flexdb_ref *const dbref, const struct kref *const kref, kv_merge_func uf, void *const priv); 250 | 251 | extern const struct kvmap_api kvmap_api_flexdb; 252 | 253 | #endif 254 | -------------------------------------------------------------------------------- /flexfile.h: -------------------------------------------------------------------------------- 1 | #ifndef _FLEXFILE_H 2 | #define _FLEXFILE_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "c/ctypes.h" 14 | #include "c/lib.h" 15 | #include "flextree.h" 16 | #include "generic.h" 17 | 18 | // the address space flexfile manages 19 | #ifndef FLEXFILE_MAX_OFFSET 20 | #define FLEXFILE_MAX_OFFSET (800lu << 30) // 800 GB 21 | #endif 22 | static_assert(FLEXFILE_MAX_OFFSET >= (4lu << 30), "dont manage small space"); 23 | 24 | // block config 25 | #define FLEXFILE_BLOCK_BITS (22u) // 4M 26 | #define FLEXFILE_BLOCK_SIZE (1u << FLEXFILE_BLOCK_BITS) 27 | #define FLEXFILE_BLOCK_COUNT (FLEXFILE_MAX_OFFSET >> FLEXFILE_BLOCK_BITS) 28 | #define FLEXFILE_MAX_EXTENT_BIT (5u) 29 | #define FLEXFILE_MAX_EXTENT_SIZE (FLEXFILE_BLOCK_SIZE >> FLEXFILE_MAX_EXTENT_BIT) // 128K (1/32) 30 | static_assert(FLEXFILE_BLOCK_BITS < 32, "one u32 to track one block"); 31 | static_assert(FLEXFILE_BLOCK_COUNT < (1lu<<48), "no more than 2^48 blocks"); 32 | 33 | // logical logging 34 | #define FLEXFILE_LOG_MEM_CAP (8u << 20) // buffered log size 8M 35 | #define FLEXFILE_LOG_MAX_SIZE (2u << 30) // max on-disk log size 2G 36 | 37 | #define FLEXFILE_IO_URING 38 | // block manager 39 | #ifdef FLEXFILE_IO_URING 40 | #include 41 | #define FLEXFILE_BM_DEPTH (16) // uring 42 | #define FLEXFILE_BM_BATCH_SIZE (FLEXFILE_BM_DEPTH >> 2) // uring 43 | static_assert(FLEXFILE_BM_DEPTH <= 512, "no more than 512 blocks in a ring"); 44 | #endif 45 | #define FLEXFILE_BM_BLKDIST_BITS (16) // stat 46 | #define FLEXFILE_BM_BLKDIST_SIZE ((FLEXFILE_BLOCK_SIZE >> FLEXFILE_BM_BLKDIST_BITS) + 1) // stat 47 | 48 | // garbage collector 49 | #define FLEXFILE_GC_QUEUE_DEPTH (8192) 50 | #define FLEXFILE_GC_THRESHOLD (64) 51 | static_assert(FLEXFILE_GC_THRESHOLD >= (1u << FLEXFILE_MAX_EXTENT_BIT), "need some blocks to do the final gc"); 52 | 53 | #ifdef FLEXFILE_IO_URING 54 | #define FLEXFILE_RRING_DEPTH (32) 55 | #define FLEXFILE_RRING_BATCH_SIZE (FLEXFILE_RRING_DEPTH >> 1) 56 | #endif 57 | 58 | /* 59 | * Note: 60 | * The current implementation of io engine that wraps the flextree structure 61 | * does not allow holes in file, which simplified the usage by applications 62 | * 63 | * One should always call flexfile_sync() manually to guarantee the metadata 64 | * consistency, if one only calls flexfile_write() without a flexfile_sync(), 65 | * the data could be written but it is not logged in the flextree. The space in 66 | * the log can be rewritten. 67 | */ 68 | 69 | struct flexfile_bm; // block manager 70 | struct flexfile_log_entry; 71 | #ifdef FLEXFILE_IO_URING 72 | struct flexfile_rring; 73 | #endif 74 | 75 | struct flexfile { 76 | char *path; 77 | struct flextree *flextree; 78 | int fd; // data file fd 79 | int log_fd; // logical log fd 80 | u8 *log_buf; 81 | u32 log_buf_size; 82 | u32 log_total_size; 83 | struct flexfile_bm* bm; 84 | struct { 85 | u64 loff; 86 | struct { 87 | struct flextree_node *node; 88 | u64 poff; 89 | u32 len; 90 | u32 idx; 91 | u8 *buf; 92 | } *queue; 93 | u32 count; 94 | u8 write_between_stages; 95 | } gc_ctx; 96 | }; 97 | 98 | struct flexfile_handler { 99 | const struct flexfile *file; 100 | struct flextree_pos fp; // cache the flextree position 101 | }; 102 | 103 | /* 104 | * General stateful APIs 105 | */ 106 | 107 | extern struct flexfile *flexfile_open(const char *const path); 108 | 109 | extern void flexfile_close(struct flexfile *const ff); 110 | 111 | extern void flexfile_sync(struct flexfile *const ff); 112 | 113 | extern ssize_t flexfile_read(struct flexfile *const ff, void *const buf, const u64 loff, const u64 len); 114 | 115 | extern ssize_t flexfile_insert(struct flexfile *const ff, const void *const buf, const u64 loff, const u64 len); 116 | 117 | extern int flexfile_collapse(struct flexfile *const ff, const u64 loff, const u64 len); 118 | 119 | extern ssize_t flexfile_write(struct flexfile *const ff, const void *const buf, const u64 loff, const u64 len); 120 | 121 | extern int flexfile_set_tag(struct flexfile *const ff, const u64 loff, const u16 tag); 122 | 123 | extern int flexfile_get_tag(const struct flexfile *const ff, const u64 loff, u16 *const tag); 124 | 125 | extern ssize_t flexfile_update( 126 | struct flexfile *const ff, const void *const buf, const u64 loff, const u64 len, const u64 olen); 127 | 128 | extern ssize_t flexfile_read_fragmentation( 129 | struct flexfile *const ff, void *const buf, const u64 loff, const u64 len, u64 *const frag); 130 | 131 | extern int flexfile_defrag(struct flexfile *const ff, const void *const buf, const u64 loff, const u64 len); 132 | 133 | extern unsigned long flexfile_size(const struct flexfile *const ff); 134 | 135 | extern void flexfile_gc(struct flexfile *const ff); 136 | 137 | extern int flexfile_fallocate(struct flexfile *const ff, const u64 loff, const u64 size); 138 | 139 | extern int flexfile_ftruncate(struct flexfile *const ff, const u64 size); 140 | 141 | #ifdef FLEXFILE_IO_URING 142 | extern struct flexfile_rring *flexfile_rring_create(struct flexfile *const ff, void *const buf, const u64 bufsz); 143 | 144 | extern void flexfile_rring_destroy(struct flexfile_rring *const rring); 145 | 146 | extern ssize_t flexfile_read_rring( 147 | const struct flexfile *const ff, void *const buf, const u64 loff, const u64 len, 148 | struct flexfile_rring *const rring); 149 | 150 | extern ssize_t flexfile_read_fragmentation_rring( 151 | const struct flexfile *const ff, void *const buf, const u64 loff, const u64 len, u64 *const frag, 152 | struct flexfile_rring *const rring); 153 | #endif 154 | 155 | // extern ssize_t flexfile_read_extent( 156 | // struct flexfile *const ff, void *const buf, const u64 loff, const u64 max_len, u64 *const rloff); 157 | 158 | /* 159 | * Handler APIs 160 | */ 161 | 162 | // read-only flexfile handler 163 | 164 | extern struct flexfile_handler flexfile_get_handler(const struct flexfile *const ff, const u64 loff); 165 | 166 | extern ssize_t flexfile_handler_read(const struct flexfile_handler *const fh, void *const buf, const u64 len); 167 | 168 | extern unsigned long flexfile_handler_get_loff(const struct flexfile_handler *const fh); 169 | 170 | extern unsigned long flexfile_handler_get_poff(const struct flexfile_handler *const fh); 171 | 172 | extern void flexfile_handler_forward(struct flexfile_handler *const fh, const u64 step); 173 | 174 | extern void flexfile_handler_forward_extent(struct flexfile_handler *const fh); 175 | 176 | extern void flexfile_handler_backward(struct flexfile_handler *const fh, const u64 step); 177 | 178 | extern int flexfile_handler_valid(const struct flexfile_handler *const fh); 179 | 180 | extern int flexfile_handler_get_tag(const struct flexfile_handler *const fh, u16 *const tag); 181 | 182 | #endif 183 | -------------------------------------------------------------------------------- /flextree.h: -------------------------------------------------------------------------------- 1 | #ifndef _FLEXTREE_H 2 | #define _FLEXTREE_H 3 | 4 | #include 5 | #include 6 | 7 | #include "c/ctypes.h" 8 | #include "c/lib.h" 9 | #include "generic.h" 10 | 11 | 12 | /* 13 | * Tunable parameters 14 | */ 15 | 16 | #define FLEXTREE_LEAF_CAP 60 17 | #define FLEXTREE_INTERNAL_CAP 30 18 | #define FLEXTREE_MAX_EXTENT_SIZE_LIMIT (64u << 20) 19 | 20 | static_assert(FLEXTREE_MAX_EXTENT_SIZE_LIMIT*FLEXTREE_LEAF_CAP < ~(u32)0, "leaf size < 4G"); 21 | 22 | struct flextree_free_list; 23 | struct flextree_path; 24 | 25 | struct flextree_leaf_entry { 26 | struct flextree_extent { 27 | u32 loff; 28 | u32 len; 29 | u64 tag :16; 30 | u64 poff :48; 31 | // 47 bits for address, 1 bit for hole. The max size of a file is 2^47-1 bytes (~128T, enough) 32 | } extents[FLEXTREE_LEAF_CAP] __attribute__((packed)); 33 | struct flextree_node *prev; 34 | struct flextree_node *next; 35 | }; 36 | 37 | static_assert(sizeof(struct flextree_extent) == 16, "16 bytes for extent"); 38 | 39 | struct flextree_internal_entry { 40 | u64 pivots[FLEXTREE_INTERNAL_CAP]; // n pivots 41 | struct { 42 | struct flextree_node *node; 43 | s64 shift; 44 | } children[FLEXTREE_INTERNAL_CAP+1]; // n+1 children 45 | u64 children_ids[FLEXTREE_INTERNAL_CAP+1]; // persistency helper 46 | u32 padding1[3]; 47 | }; 48 | 49 | struct flextree_node { 50 | u32 count; // actually u8 for path 51 | u8 is_leaf; 52 | u8 dirty; 53 | u8 padding1[2]; 54 | struct flextree *tree; 55 | union { 56 | struct flextree_leaf_entry leaf_entry; 57 | struct flextree_internal_entry internal_entry; 58 | }; 59 | u64 id; 60 | }; 61 | 62 | static_assert(sizeof(struct flextree_node) == 1024, "1k for node"); 63 | 64 | struct flextree { 65 | // in-memory 66 | char *path; 67 | u64 max_loff; 68 | u32 max_extent_size; 69 | u64 version; // based on which persistent version 70 | struct flextree_node *root; 71 | struct flextree_node *leaf_head; // linked list head 72 | struct flextree_free_list *free_list; 73 | struct slab *node_slab; 74 | u64 in_memory_mode; // if in-memory, no real files are created 75 | // persistence helper 76 | file_type meta_fd; 77 | file_type node_fd; 78 | u64 node_count; 79 | u64 max_node_id; 80 | u64 root_id; 81 | }; 82 | 83 | // range query result 84 | struct flextree_query_result { 85 | u64 loff; 86 | u64 len; 87 | u64 count; 88 | struct { 89 | u64 poff; 90 | u64 len; 91 | } v[]; 92 | }; 93 | 94 | /* 95 | * A low-level position on leaf nodes 96 | * node: the leaf node pointer 97 | * loff: logical offset of the position 98 | * idx : the index of the entry 99 | * diff: the current position within the entry 100 | */ 101 | 102 | struct flextree_pos { 103 | struct flextree_node *node; 104 | u64 loff; 105 | u32 idx; 106 | u32 diff; 107 | }; 108 | 109 | /* 110 | * Low-level APIs 111 | * 112 | * These APIs should be used with care, as it has strong presumptions 113 | * and the low-level access does not have global view 114 | */ 115 | 116 | extern struct flextree_pos flextree_pos_get_ll(const struct flextree *const tree, const u64 loff); 117 | 118 | extern void flextree_pos_forward_ll(struct flextree_pos *const fp, const u64 step); 119 | 120 | extern void flextree_pos_forward_extent_ll(struct flextree_pos *const fp); 121 | 122 | extern void flextree_pos_backward_ll(struct flextree_pos *const fp, const u64 step); 123 | 124 | extern void flextree_pos_rewind_ll(struct flextree_pos *const fp); 125 | 126 | extern unsigned long flextree_pos_get_loff_ll(const struct flextree_pos *const fp); 127 | 128 | extern unsigned long flextree_pos_get_poff_ll(const struct flextree_pos *const fp); 129 | 130 | extern int flextree_pos_valid_ll(const struct flextree_pos *const fp); 131 | 132 | extern int flextree_pos_get_tag_ll(const struct flextree_pos *const fp, u16 *const tag); 133 | 134 | /* 135 | * High-level APIs 136 | */ 137 | 138 | extern void flextree_print(const struct flextree *const tree); 139 | 140 | extern struct flextree *flextree_open(const char *const path, const u32 max_extent_size); 141 | 142 | extern void flextree_close(struct flextree *const tree); 143 | 144 | extern void flextree_sync(struct flextree *const tree); 145 | 146 | extern int flextree_insert(struct flextree *const tree, const u64 loff, const u64 poff, const u32 len); 147 | 148 | extern int flextree_delete(struct flextree *const tree, const u64 loff, const u64 len); 149 | 150 | extern struct flextree_query_result *flextree_query(const struct flextree *const tree, const u64 loff, const u64 len); 151 | 152 | extern struct flextree_query_result *flextree_query_wbuf( 153 | const struct flextree *const tree, const u64 loff, const u64 len, struct flextree_query_result *const rr); 154 | 155 | extern int flextree_set_tag(struct flextree *const tree, const u64 loff, const u16 tag); 156 | 157 | extern int flextree_get_tag(const struct flextree *const tree, const u64 loff, u16 *const tag); 158 | 159 | extern int flextree_insert_wtag( 160 | struct flextree *const tree, const u64 loff, const u64 poff, const u32 len, const u16 tag); 161 | 162 | // the following two functions are for testing 163 | extern int flextree_pdelete(struct flextree *const tree, const u64 loff); 164 | 165 | extern unsigned long flextree_pquery(const struct flextree *const tree, const u64 loff); 166 | 167 | /* 168 | * Naive array implementation 169 | */ 170 | 171 | struct brute_force_extent { 172 | u64 loff :64; // logical offset (in file address space) 173 | u32 len :32; // segment length 174 | u64 poff :48; // physical offset (in device address space) 175 | u16 tag :16; 176 | } __attribute__((packed)); 177 | 178 | static_assert(sizeof(struct brute_force_extent) == 20, "20 bytes for bf extent"); 179 | 180 | struct brute_force { 181 | u64 count; 182 | u64 cap; 183 | u64 max_loff; 184 | u32 max_extent_size; 185 | struct brute_force_extent *extents; 186 | }; 187 | 188 | extern void brute_force_print(const struct brute_force *const bf); 189 | 190 | extern struct brute_force *brute_force_open(const u32 max_extent_size); 191 | 192 | extern void brute_force_close(struct brute_force *const bf); 193 | 194 | extern int brute_force_insert(struct brute_force *const bf, const u64 loff, const u64 poff, const u32 len); 195 | 196 | extern unsigned long brute_force_pquery(const struct brute_force *const bf, const u64 loff); 197 | 198 | extern struct flextree_query_result *brute_force_query( 199 | const struct brute_force *const bf, const u64 loff, const u64 len); 200 | 201 | extern struct flextree_query_result *brute_force_query_wbuf( 202 | const struct brute_force *const bf, const u64 loff, const u64 len, struct flextree_query_result *const rr); 203 | 204 | extern int brute_force_insert_wtag( 205 | struct brute_force *const bf, const u64 loff, const u64 poff, const u32 len, const u16 tag); 206 | 207 | extern int brute_force_pdelete(struct brute_force *const bf, const u64 loff); 208 | 209 | extern int brute_force_delete(struct brute_force *const bf, const u64 loff, const u64 len); 210 | 211 | extern int brute_force_set_tag(struct brute_force *const bf, const u64 loff, const u16 tag); 212 | 213 | extern int brute_force_get_tag(const struct brute_force*const bf, const u64 loff, u16 *const tag); 214 | 215 | #endif 216 | -------------------------------------------------------------------------------- /generic.c: -------------------------------------------------------------------------------- 1 | #include "generic.h" 2 | 3 | inline void *generic_malloc(size_t size) 4 | { 5 | return malloc(size); 6 | } 7 | 8 | 9 | inline void *generic_realloc(void *ptr, size_t size) 10 | { 11 | return realloc(ptr, size); 12 | }; 13 | 14 | inline void generic_free(void *ptr) 15 | { 16 | return free(ptr); 17 | } 18 | 19 | inline file_type generic_open(const char *pathname, int flags, mode_t mode) 20 | { 21 | return (file_type)open(pathname, flags, mode); 22 | } 23 | 24 | inline file_type generic_mkdir(const char *path, mode_t mode) 25 | { 26 | return mkdir(path, mode); 27 | }; 28 | 29 | inline off_t generic_lseek(file_type fildes, off_t offset, int whence) 30 | { 31 | return lseek(fildes, offset, whence); 32 | } 33 | 34 | inline int generic_close(file_type fd) 35 | { 36 | return close(fd); 37 | } 38 | 39 | inline ssize_t generic_pread(file_type fd, void *buf, size_t nbyte, off_t offset) 40 | { 41 | return pread(fd, buf, nbyte, offset); 42 | } 43 | 44 | inline ssize_t generic_pwrite(file_type fd, const void *buf, size_t count, off_t offset) 45 | { 46 | return pwrite(fd, buf, count, offset); 47 | } 48 | 49 | inline int generic_fdatasync(file_type fildes) 50 | { 51 | return fdatasync(fildes); 52 | } 53 | 54 | inline void generic_exit(int status) 55 | { 56 | fflush(stdout); 57 | return exit(status); 58 | } 59 | 60 | inline int generic_ftruncate(file_type fildes, off_t length) 61 | { 62 | return ftruncate(fildes, length); 63 | } 64 | -------------------------------------------------------------------------------- /generic.h: -------------------------------------------------------------------------------- 1 | #ifndef _GENERIC_H 2 | #define _GENERIC_H 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | typedef int file_type; 11 | 12 | #ifndef static_assert 13 | #ifdef __GNUC__ 14 | #define static_assert(cond, msg) \ 15 | typedef char __static_assertion_failed[(!!(cond))*2-1] 16 | #endif 17 | #endif 18 | 19 | #ifndef debug_assert 20 | #define debug_assert(x) (assert(x)) 21 | #endif 22 | 23 | #define generic_printf(args...) printf(args) 24 | #define generic_sprintf(args...) sprintf(args) 25 | #define generic_fflush(args...) fflush(args) 26 | 27 | extern void *generic_malloc(size_t size); 28 | 29 | extern void *generic_realloc(void *ptr, size_t size); 30 | 31 | extern void generic_free(void * ptr); 32 | 33 | extern file_type generic_open(const char *path, int flags, mode_t mode); 34 | 35 | extern file_type generic_mkdir(const char *path, mode_t mode); 36 | 37 | extern off_t generic_lseek(file_type fildes, off_t offset, int whence); 38 | 39 | extern int generic_close(file_type close); 40 | 41 | extern ssize_t generic_pread(file_type fd, void *buf, size_t nbyte, off_t offset); 42 | 43 | extern ssize_t generic_pwrite(file_type fd, const void *buf, size_t nbyte, off_t offset); 44 | 45 | extern int generic_fdatasync(file_type fildes); 46 | 47 | extern void generic_exit(int status); 48 | 49 | extern int generic_ftruncate(file_type fildes, off_t length); 50 | 51 | #endif 52 | -------------------------------------------------------------------------------- /test_flexdb.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "flexdb.h" 7 | 8 | static void print_flexdb_tree_node_rec(const struct flexdb_tree_node *const node) 9 | { 10 | printf("\n[Node]: %p count %u is_leaf %u\n ", node, node->count, node->is_leaf); 11 | printf("flexdb_tree %p parent %p parent_id %u\n", node->tree, node->parent, node->parent_id); 12 | if (node->is_leaf) { 13 | const struct flexdb_tree_leaf_entry *const le = &node->leaf_entry; 14 | for (u32 i=0; icount; ++i) { 15 | if (le->anchors[i]->key->klen > 0) { 16 | printf(" anchor %u key %s loff %u psize %u\n", 17 | i, le->anchors[i]->key->kv, le->anchors[i]->loff, 18 | le->anchors[i]->psize); 19 | } else { 20 | printf(" anchor %u key nil loff %u psize %u\n", 21 | i, le->anchors[i]->loff, 22 | le->anchors[i]->psize); 23 | } 24 | } 25 | } 26 | else { 27 | const struct flexdb_tree_internal_entry *const ie = &node->internal_entry; 28 | printf("internal_entry\n"); 29 | for (u32 i=0; icount+1; ++i) { 30 | if (i == 0) { 31 | printf(" children %u pointer %p shift %ld\n", i, ie->children[i].node, ie->children[i].shift); 32 | } else { 33 | printf(" base %s\n", ie->pivots[i-1]->kv); 34 | printf(" children %u pointer %p shift %ld\n", i, ie->children[i].node, ie->children[i].shift); 35 | } 36 | } 37 | for (u32 i=0; icount+1; ++i) { 38 | print_flexdb_tree_node_rec(ie->children[i].node); 39 | } 40 | } 41 | } 42 | 43 | static inline void print_flexdb_tree(const struct flexdb_tree *const kt) 44 | { 45 | print_flexdb_tree_node_rec(kt->root); 46 | } 47 | 48 | static void print_flexdb_tree_count(const struct flexdb_tree *const kt) 49 | { 50 | const struct flexdb_tree_node *node = kt->leaf_head; 51 | u32 idx = 0; 52 | u32 count = 0; 53 | const struct kv *key = NULL; 54 | while (node) { 55 | while (idx < node->count) { 56 | if (key) { 57 | if (kv_compare(key, node->leaf_entry.anchors[idx]->key) > 0) { 58 | printf("wrong order!\n"); 59 | exit(1); 60 | } 61 | } 62 | key = node->leaf_entry.anchors[idx]->key; 63 | count++; 64 | idx++; 65 | } 66 | node = node->leaf_entry.next; 67 | idx = 0; 68 | } 69 | printf("%u\n", count); 70 | } 71 | 72 | static void print_flextree_kv_count(struct flexdb_ref *const dbref) 73 | { 74 | flexdb_sync(dbref); 75 | while (!rwlock_trylock_read(&dbref->db->rwlock_flexfile[0].lock)) { 76 | cpu_pause(); 77 | } 78 | struct flexfile_handler ffh = flexfile_get_handler(dbref->db->flexfile, 0); 79 | if (!ffh.file) { 80 | rwlock_unlock_read(&dbref->db->rwlock_flexfile[0].lock); 81 | return; 82 | } 83 | u64 i = 0; 84 | struct kv *pkey = NULL; 85 | u8 *buf = malloc(FLEXDB_MAX_KV_SIZE); 86 | struct kv *const kv = malloc(FLEXDB_MAX_KV_SIZE); 87 | while (ffh.fp.node) { 88 | struct kv *tkv = flexdb_read_kv(&ffh, buf, kv); 89 | if (tkv != kv) { 90 | printf("print_flextree_kv_count error"); 91 | exit(1); 92 | } 93 | i++; 94 | //printf("%s\n", kv->kv); 95 | if (pkey) { 96 | free(pkey); 97 | struct kv *key = kv_dup_key(kv); 98 | if (kv_compare(pkey, key) > 0) { 99 | printf("wrong order!\n"); 100 | exit(1); 101 | } 102 | pkey = key; 103 | } 104 | flexfile_handler_forward(&ffh, kv128_estimate_kv(kv)); 105 | } 106 | free(pkey); 107 | free(buf); 108 | free(kv); 109 | 110 | printf("total kv %lu\n", i); 111 | rwlock_unlock_read(&dbref->db->rwlock_flexfile[0].lock); 112 | } 113 | 114 | static void rand_str(char *const buf, u32 len) 115 | { 116 | const char alphabet[] = "abcdefghijklmnopqrstuvwxyz0123456789"; 117 | for (u32 i=0; iklen = klen; 145 | kvps[i]->vlen = vlen; 146 | rand_str((char *)kvps[i]->kv, klen-1); 147 | rand_str((char *)kvps[i]->kv+klen, vlen-1); 148 | } 149 | //qsort(kvps, c, sizeof(kvps[0]), qsort_cmp); 150 | printf("gen %lu ms\n",time_diff_nsec(ts)/1000/1000); 151 | 152 | ts = time_nsec(); 153 | u64 ts2 = time_nsec(); 154 | for (u64 i=0; ikv); fflush(stdout); 156 | kv_update_hash(kvps[i]); 157 | flexdb_put(dbref, kvps[i]); 158 | if (i > 0 && i % 100000 == 0) { 159 | printf("%lu: w ops %lu \n", i/100000, 100000000 / (time_diff_nsec(ts2) / 1000000)); 160 | ts2 = time_nsec(); 161 | } 162 | } 163 | printf("average w ops %lu \n", c * 1000 * 1000 * 1000 / time_diff_nsec(ts)); 164 | 165 | flexdb_sync(dbref); 166 | 167 | u64 it = 0; 168 | (void)it; 169 | struct flexdb_iterator *iter = flexdb_iterator_create(dbref); 170 | flexdb_iterator_park(iter); 171 | struct kref nkref = kv_kref(kv_null()); 172 | for (u64 i=0; ivlen--; 186 | flexdb_put(dbref, kvps[i]); 187 | if (i > 0 && i % 100000 == 0) { 188 | printf("%lu: u ops %lu \n", i/100000, 100000 * 1000 / (time_diff_nsec(ts2) / 1000 / 1000)); 189 | ts2 = time_nsec(); 190 | } 191 | } 192 | printf("average u ops %lu \n", c * 1000 *1000*1000 / time_diff_nsec(ts)); 193 | fflush(stdout); 194 | 195 | print_flextree_kv_count(dbref); 196 | 197 | it = 0; 198 | iter = flexdb_iterator_create(dbref); 199 | nkref = kv_kref(kv_null()); 200 | flexdb_iterator_seek(iter, &nkref); 201 | while (flexdb_iterator_valid(iter)) { 202 | free(flexdb_iterator_next(iter, NULL)); 203 | it++; 204 | } 205 | flexdb_iterator_destroy(iter); 206 | printf("iterates over %lu\n", it); 207 | 208 | u64 hit = 0; 209 | ts2 = time_nsec(); 210 | for (u64 i=0; ikv); 212 | struct kref kref = kv_kref(kvps[i]); 213 | struct kv *r = flexdb_get(dbref, &kref, NULL); 214 | if (!r) { 215 | continue; 216 | } 217 | hit++; 218 | if (kv_compare(kvps[i], r) != 0) { 219 | printf("diff!\n"); 220 | } 221 | //printf("%s\n", r->kv); 222 | (void)r; 223 | free(r); 224 | if (i > 0 && i % 100000 == 0) { 225 | printf("%lu: r ops %lu \n", i/100000, 100000 * 1000 / (time_diff_nsec(ts2) / 1000 / 1000)); 226 | ts2 = time_nsec(); 227 | } 228 | } 229 | printf("hit %lu total %lu\n", hit, c); 230 | 231 | ts2 = time_nsec(); 232 | for (u64 i=0; ikv); 234 | struct kref kref = kv_kref(kvps[i]); 235 | flexdb_delete(dbref, &kref); 236 | if (i > 0 && i % 100000 == 0) { 237 | printf("%lu: d ops %lu \n", i/100000, 50000 * 1000 / (time_diff_nsec(ts2) / 1000 / 1000)); 238 | ts2 = time_nsec(); 239 | } 240 | } 241 | 242 | it = 0; 243 | iter = flexdb_iterator_create(dbref); 244 | nkref = kv_kref(kv_null()); 245 | flexdb_iterator_seek(iter, &nkref); 246 | while (flexdb_iterator_valid(iter)) { 247 | free(flexdb_iterator_next(iter, NULL)); 248 | it++; 249 | } 250 | flexdb_iterator_destroy(iter); 251 | printf("iterates over %lu\n", it); 252 | 253 | printf("sync\n"); 254 | printf("%u %u\n", dbref->db->memtables[0].hidden, dbref->db->memtables[1].hidden); 255 | fflush(stdout); 256 | flexdb_sync(dbref); 257 | printf("%u %u\n", dbref->db->memtables[0].hidden, dbref->db->memtables[1].hidden); 258 | fflush(stdout); 259 | sleep(15); 260 | printf("1 1: %u %u\n", dbref->db->memtables[0].hidden, dbref->db->memtables[1].hidden); 261 | fflush(stdout); 262 | 263 | print_flexdb_tree(dbref->db->tree); 264 | 265 | qsort(kvps, c, sizeof(kvps[0]), qsort_cmp); 266 | 267 | ts2 = time_nsec(); 268 | for (u64 i=c-1; i>=0; i--) { 269 | //printf("%u: insert k %s\n", i, kvps[i]->kv); fflush(stdout); 270 | kv_update_hash(kvps[i]); 271 | flexdb_put(dbref, kvps[i]); 272 | if (i > 0 && i % 100000 == 0) { 273 | printf("%lu: rev-w ops %lu \n", i/100000, 100000000 / (time_diff_nsec(ts2) / 1000000)); 274 | ts2 = time_nsec(); 275 | } 276 | if (i == 0) break; 277 | } 278 | 279 | hit = 0; 280 | ts2 = time_nsec(); 281 | for (u64 i=0; ikv); 283 | struct kref kref = kv_kref(kvps[i]); 284 | struct kv *r = flexdb_get(dbref, &kref, NULL); 285 | if (!r) { 286 | continue; 287 | } 288 | hit++; 289 | if (kv_compare(kvps[i], r) != 0) { 290 | printf("diff!\n"); 291 | } 292 | if (memcmp(kvps[i]->kv+kvps[i]->klen, r->kv + r->klen, r->vlen) != 0) { 293 | printf("diff\n"); 294 | } 295 | //printf("%s\n", r->kv); 296 | (void)r; 297 | free(r); 298 | if (i > 0 && i % 100000 == 0) { 299 | printf("%lu: r ops %lu \n", i/100000, 100000 * 1000 / (time_diff_nsec(ts2) / 1000 / 1000)); 300 | ts2 = time_nsec(); 301 | } 302 | } 303 | printf("hit %lu total %lu\n", hit, c); 304 | 305 | flexdb_deref(dbref); 306 | flexdb_close(db); 307 | 308 | printf("reopen\n"); 309 | db = flexdb_open("/tmp/flexdb", 32); 310 | dbref = flexdb_ref(db); 311 | 312 | hit = 0; 313 | ts2 = time_nsec(); 314 | for (u64 i=0; ikv+kvps[i]->klen, r->kv + r->klen, r->vlen) != 0) { 325 | printf("diff\n"); 326 | } 327 | //printf("%s\n", r->kv); 328 | (void)r; 329 | free(r); 330 | if (i > 0 && i % 100000 == 0) { 331 | printf("%lu: r ops %lu \n", i/100000, 100000 * 1000 / (time_diff_nsec(ts2) / 1000 / 1000)); 332 | ts2 = time_nsec(); 333 | } 334 | } 335 | printf("hit %lu total %lu\n", hit, c); 336 | 337 | it = 0; 338 | iter = flexdb_iterator_create(dbref); 339 | nkref = kv_kref(kv_null()); 340 | flexdb_iterator_seek(iter, &nkref); 341 | while (flexdb_iterator_valid(iter)) { 342 | struct kv *kv1 = flexdb_iterator_next(iter, NULL); 343 | struct kref kref1 = kv_kref(kv1); 344 | struct kv *kv2 = flexdb_get(dbref, &kref1, NULL); 345 | if (strncmp((char *)kv1->kv, (char *)kv2->kv, kv1->klen + kv1->vlen) != 0) { 346 | printf("integrity broken at %lu\n", it); 347 | exit(0); 348 | } 349 | free(kv1); 350 | free(kv2); 351 | it++; 352 | } 353 | flexdb_iterator_destroy(iter); 354 | printf("iterates over %lu\n", it); 355 | 356 | print_flextree_kv_count(dbref); 357 | flexdb_deref(dbref); 358 | flexdb_close(db); 359 | 360 | for (u64 i=0; iflextree); 32 | flexfile_read(ff, result, 0, 12); 33 | result[12] = 0; 34 | printf("aabcd123efbc: %s\n", result); // should be "aabcd123efbc" 35 | flexfile_close(ff); 36 | return 0; 37 | (void)r; 38 | } 39 | -------------------------------------------------------------------------------- /test_flextree.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "flextree.h" 8 | 9 | #define PATH "/tmp/flextree" 10 | 11 | volatile u64 __global_r = 0; 12 | 13 | u64 seed = 42; 14 | 15 | static void setrand(void) 16 | { 17 | seed = 42; 18 | } 19 | 20 | static u64 rand_int(void) 21 | { 22 | seed = (seed + 100000037); 23 | return seed; 24 | // a very fake function! 25 | } 26 | 27 | static void print_query_result(const struct flextree_query_result *const rr) 28 | { 29 | generic_printf("count %lu loff %lu len %lu\n", rr->count, rr->loff, rr->len); 30 | for (u64 i=0; icount; i+=1) { 31 | generic_printf("%ld %ld\n", rr->v[i].poff, rr->v[i].len); 32 | } 33 | } 34 | 35 | static u8 query_result_equal( 36 | const struct flextree_query_result *const rr1, const struct flextree_query_result *const rr2) 37 | { 38 | if (rr1 == NULL && rr2 == NULL) return 1; 39 | if (rr1 != rr2 && (rr1 == NULL || rr2 == NULL)) return 0; 40 | debug_assert(rr1); 41 | debug_assert(rr2); 42 | if (rr1->count != rr2->count) return 0; 43 | for (u64 i=0; icount; i++) { 44 | if (rr1->v[i].poff != rr2->v[i].poff || rr1->v[i].len != rr2->v[i].len) { 45 | return 0; 46 | } 47 | } 48 | return 1; 49 | } 50 | 51 | static void random_insert(struct flextree *const ft, struct brute_force *const bf, const u64 count) 52 | { 53 | u64 ts = 0; 54 | if (ft) { 55 | setrand(); 56 | ts = time_nsec(); 57 | flextree_insert(ft, 0, 0, 4); 58 | u64 max_loff = 4; 59 | for (u64 i=0; i=0; i--) { 178 | u64 fr = flextree_pquery(ft, i); 179 | u64 br = brute_force_pquery(bf, i); 180 | if (fr != br) { 181 | generic_printf("Error encourted on %ld %ld %ld\n", i, fr, br); 182 | correct = 0; 183 | break; 184 | } 185 | if (i == 0) { 186 | break; 187 | } 188 | } 189 | correct ? generic_printf("\033[0;32m[results correct]\033[0m\n") : 190 | generic_printf("\033[0;31m[results wrong]\033[0m\n"); 191 | } 192 | 193 | static void sequential_query(struct flextree *const ft, struct brute_force *const bf, u64 total_size) 194 | { 195 | return sequential_query_r(ft, bf, total_size, 0); 196 | } 197 | 198 | 199 | static void sequential_query_vo(struct flextree *const ft, struct brute_force *const bf, u64 total_size) 200 | { 201 | return sequential_query_r(ft, bf, total_size, 1); 202 | } 203 | 204 | static void random_range_query( 205 | struct flextree *const ft, struct brute_force *const bf, u64 total_size, const u64 count) 206 | { 207 | if (!ft || !bf) return; 208 | u64 ts = 0; 209 | 210 | setrand(); 211 | ts = time_nsec(); 212 | for (u64 i=0; imax_loff-1, 0xffff); 313 | brute_force_set_tag(bf, bf->max_loff-1, 0xffff); 314 | 315 | u8 correct = 1; 316 | for (u64 i=0; imax_loff == bf->max_loff); 338 | sequential_query(ft, bf, ft->max_loff); 339 | random_query(ft, bf, ft->max_loff); 340 | brute_force_close(bf); 341 | flextree_close(ft); 342 | } 343 | 344 | static void test2(const u64 count) 345 | { 346 | generic_printf("---test2 point deletion and point lookup %lu---\n", count); 347 | struct flextree *ft = flextree_open(NULL, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 348 | struct brute_force *bf = brute_force_open(FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 349 | 350 | random_insert(ft, bf, count); 351 | random_pdelete(ft, bf, ft->max_loff, count); 352 | debug_assert(ft->max_loff == bf->max_loff); 353 | sequential_query(ft, bf, ft->max_loff); 354 | brute_force_close(bf); 355 | flextree_close(ft); 356 | } 357 | 358 | static void test3(const u64 count) 359 | { 360 | generic_printf("---test3 random range deletion %lu---\n", count); 361 | struct flextree *ft = flextree_open(NULL, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 362 | struct brute_force *bf = brute_force_open(FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 363 | random_insert(ft, bf, count); 364 | debug_assert(ft->max_loff == bf->max_loff); 365 | random_delete(ft, bf, ft->max_loff, count); 366 | debug_assert(ft->max_loff == bf->max_loff); 367 | sequential_query(ft, bf, ft->max_loff); 368 | flextree_close(ft); 369 | brute_force_close(bf); 370 | } 371 | 372 | static void test4(const u64 count) 373 | { 374 | generic_printf("---test4 range query %lu---\n", count); 375 | struct flextree *ft = flextree_open(NULL, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 376 | struct brute_force *bf = brute_force_open(FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 377 | random_insert(ft, bf, count); 378 | debug_assert(ft->max_loff == bf->max_loff); 379 | random_delete(ft, bf, ft->max_loff, count); 380 | debug_assert(ft->max_loff == bf->max_loff); 381 | random_range_query(ft, bf, ft->max_loff, count); 382 | flextree_close(ft); 383 | brute_force_close(bf); 384 | } 385 | 386 | static void count_leaf_nodes(struct flextree_node *const node, u64 *const c) 387 | { 388 | if (node->is_leaf) { 389 | (*c)++; 390 | } 391 | else { 392 | const struct flextree_internal_entry *ie = &node->internal_entry; 393 | for (u64 i=0; icount+1; i++) { 394 | count_leaf_nodes(ie->children[i].node, c); 395 | } 396 | } 397 | } 398 | 399 | static void test5(const u64 count) 400 | { 401 | generic_printf("---test5 linked list %lu---\n", count); 402 | struct flextree *ft = flextree_open(PATH, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 403 | struct brute_force *bf = brute_force_open(FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 404 | random_insert(ft, bf, count); 405 | u64 c1 = 0, c2 = 0; 406 | struct flextree_node *node = ft->leaf_head; 407 | while (node) { 408 | // generic_printf("%lu ", node->leaf_entry->id); 409 | node = node->leaf_entry.next; 410 | c1++; 411 | } 412 | count_leaf_nodes(ft->root, &c2); 413 | generic_printf("%ld %ld\n", c1, c2); 414 | flextree_close(ft); 415 | 416 | ft = flextree_open(PATH, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 417 | c1 = 0; 418 | node = ft->leaf_head; 419 | while (node) { 420 | node = node->leaf_entry.next; 421 | c1++; 422 | } 423 | generic_printf("%ld %ld\n", c1, c2); 424 | flextree_delete(ft, ft->max_loff / 4, ft->max_loff / 4 * 3); 425 | brute_force_delete(bf, bf->max_loff / 4, bf->max_loff / 4 * 3); 426 | c2 = 0; 427 | count_leaf_nodes(ft->root, &c2); 428 | flextree_close(ft); 429 | 430 | ft = flextree_open(PATH, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 431 | sequential_query(ft, bf, ft->max_loff); 432 | debug_assert(ft->max_loff == bf->max_loff); 433 | c1 = 0; 434 | node = ft->leaf_head; 435 | while (node) { 436 | node = node->leaf_entry.next; 437 | c1++; 438 | } 439 | generic_printf("%ld %ld\n", c1, c2); 440 | flextree_delete(ft, 0, ft->max_loff); 441 | brute_force_delete(bf, 0, bf->max_loff); 442 | c2 = 0; 443 | count_leaf_nodes(ft->root, &c2); 444 | flextree_close(ft); 445 | 446 | ft = flextree_open(PATH, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 447 | generic_printf("final\n"); 448 | flextree_print(ft); 449 | debug_assert(ft->max_loff == bf->max_loff); 450 | c1 = 0; 451 | node = ft->leaf_head; 452 | while (node) { 453 | node = node->leaf_entry.next; 454 | c1++; 455 | } 456 | generic_printf("%ld %ld\n", c1, c2); 457 | sequential_query(ft, bf, ft->max_loff); 458 | flextree_close(ft); 459 | brute_force_close(bf); 460 | } 461 | 462 | static void flextree_check(struct flextree *ft) 463 | { 464 | u64 total_len = 0; 465 | struct flextree_pos fp = flextree_pos_get_ll(ft, 0); 466 | while (flextree_pos_valid_ll(&fp)) { 467 | struct flextree_extent *ext = &fp.node->leaf_entry.extents[fp.idx]; 468 | total_len += ext->len; 469 | flextree_pos_forward_extent_ll(&fp); 470 | } 471 | printf("ft check max_loff %lu, total_len %lu\n", ft->max_loff, total_len); 472 | u8 correct = 0; 473 | if (ft->max_loff == total_len) { 474 | correct = 1; 475 | } 476 | correct ? generic_printf("\033[0;32m[results correct]\033[0m\n") : 477 | generic_printf("\033[0;31m[results wrong]\033[0m\n"); 478 | } 479 | 480 | static void test0(const u64 count) 481 | { 482 | generic_printf("---test0 insertion and point lookup %lu---\n", count); 483 | struct flextree *ft = flextree_open(NULL, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 484 | 485 | random_insert(ft, NULL, count); 486 | random_delete(ft, NULL, ft->max_loff, count); 487 | sequential_query(ft, NULL, ft->max_loff); 488 | 489 | u64 loff = 0; 490 | while (ft->max_loff > 100) { 491 | loff = (loff + 0xabcd12) % (ft->max_loff - 100); 492 | flextree_delete(ft, loff, 100); 493 | } 494 | flextree_delete(ft, 0, ft->max_loff-10); 495 | generic_printf("final 10\n"); 496 | generic_printf("slab %lu\n", *(u64 *)((char *)ft->node_slab+72)); 497 | flextree_print(ft); 498 | flextree_delete(ft, 0, ft->max_loff); 499 | generic_printf("final\n"); 500 | generic_printf("slab %lu\n", *(u64 *)((char *)ft->node_slab+72)); 501 | flextree_print(ft); 502 | 503 | flextree_close(ft); 504 | } 505 | 506 | static void test6(const u64 count) 507 | { 508 | generic_printf("---test6 address hole handling %lu---\n", count); 509 | struct flextree *ft = flextree_open(NULL, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 510 | struct brute_force *bf = brute_force_open(FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 511 | random_insert(ft, bf, count); 512 | // insert holes 513 | generic_printf("%lu\n", ft->max_loff); 514 | int r = flextree_insert(ft, 1lu<<34, 1lu<<40, 50); 515 | debug_assert(r == 0); 516 | r = brute_force_insert(bf, 1lu<<34, 1lu<<40, 50); 517 | debug_assert(r == 0); 518 | generic_printf("%lu\n", ft->max_loff); 519 | random_range_query(ft, bf, ft->max_loff, count); 520 | brute_force_close(bf); 521 | flextree_close(ft); 522 | (void)r; 523 | } 524 | 525 | static void test7(const u64 count) 526 | { 527 | generic_printf("---test7 range deletion %lu---\n", count); 528 | struct flextree *ft = flextree_open(NULL, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 529 | struct brute_force *bf = brute_force_open(FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 530 | random_insert(ft, bf, count); 531 | debug_assert(ft->max_loff == bf->max_loff); 532 | flextree_delete(ft, ft->max_loff / 4, ft->max_loff / 4 * 3); 533 | brute_force_delete(bf, bf->max_loff / 4, bf->max_loff / 4 * 3); 534 | debug_assert(ft->max_loff == bf->max_loff); 535 | sequential_query(ft, bf, ft->max_loff); 536 | flextree_close(ft); 537 | brute_force_close(bf); 538 | } 539 | 540 | static void test8(const u64 count) 541 | { 542 | generic_printf("---test8 tags %lu---\n", count); 543 | struct flextree *ft = flextree_open(NULL, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 544 | struct brute_force *bf = brute_force_open(FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 545 | random_insert(ft, bf, count); 546 | debug_assert(ft->max_loff == bf->max_loff); 547 | random_delete(ft, bf, ft->max_loff, count); 548 | debug_assert(ft->max_loff == bf->max_loff); 549 | sequential_tag_query(ft, bf, ft->max_loff); 550 | flextree_close(ft); 551 | brute_force_close(bf); 552 | } 553 | 554 | static void test9(const u64 count) 555 | { 556 | generic_printf("---test9 large persistent tree %lu---\n", count); 557 | struct flextree *ft = flextree_open(PATH, 128lu<<10); 558 | for (u64 i=0; i<2600000000; i++) { 559 | if (i % 100000000 == 0) { 560 | printf("%lu ", i); 561 | flextree_sync(ft); 562 | fflush(stdout); 563 | } 564 | flextree_insert(ft, i*156, i*156, 156); 565 | } 566 | printf("\n"); 567 | fflush(stdout); 568 | flextree_close(ft); 569 | ft = flextree_open(PATH, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 570 | for (u32 i=0; i<10000; i++) { 571 | printf("round %u\n", i); 572 | random_append(ft, NULL, count); 573 | flextree_close(ft); 574 | ft = flextree_open(PATH, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 575 | random_insert(ft, NULL, count); 576 | flextree_close(ft); 577 | ft = flextree_open(PATH, FLEXTREE_MAX_EXTENT_SIZE_LIMIT); 578 | flextree_check(ft); 579 | } 580 | flextree_close(ft); 581 | (void)sequential_query_vo; 582 | } 583 | 584 | int main(int argc, char ** argv) { 585 | typeof(test0) *tests [11] = {test0, test1, test2, test3, test4, test5, test6, test7, test8, test9, NULL}; 586 | char * cmd = argc > 1 ? argv[1] : "0123456789"; 587 | for (u32 i = 0; i < 10; i++) { 588 | if (strchr(cmd, (char)((u8)'0'+i)) && tests[i]) 589 | tests[i](50000); 590 | } 591 | return 0; 592 | } 593 | -------------------------------------------------------------------------------- /test_flextree_check.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | 7 | #include "flextree.h" 8 | 9 | #define FLEXTREE_HOLE (1lu << 47) // highest bit set to indicate a hole 10 | 11 | static u8 flextree_pos_consistent(struct flextree_pos *const fp1, struct flextree_pos *const fp2) 12 | { 13 | if (memcmp(fp1, fp2, sizeof(*fp1)) != 0) { 14 | return 0; 15 | } 16 | return 1; 17 | } 18 | 19 | void flextree_check(struct flextree *ft) 20 | { 21 | struct flextree_pos fp = flextree_pos_get_ll(ft, 0); 22 | while (flextree_pos_valid_ll(&fp)) { 23 | struct flextree_extent *ext = &fp.node->leaf_entry.extents[fp.idx]; 24 | struct flextree_pos fp2 = flextree_pos_get_ll(ft, fp.loff); 25 | printf("extent loff %lu poff %lu len %u is_hole %u consistent %u\n", 26 | fp.loff, ext->poff, ext->len, (ext->poff & FLEXTREE_HOLE) ? 1 : 0, flextree_pos_consistent(&fp, &fp2)); 27 | flextree_pos_forward_extent_ll(&fp); 28 | } 29 | printf("ft check max_loff %lu\n", ft->max_loff); 30 | } 31 | 32 | int main(int argc, char **argv) 33 | { 34 | if (argc != 2) { 35 | return 0; 36 | } 37 | struct flextree *const ft = flextree_open(argv[1], 128u<<10); 38 | flextree_check(ft); 39 | flextree_close(ft); 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /wrapper.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | 3 | #include 4 | #include "wrapper.h" 5 | 6 | static struct flexfile *files[65536]; // int fd -> struct flexfile *, mapping of open files per process 7 | static FILE *tmpfiles[65536]; // int fd -> FILE *, to avoid memory leaks 8 | 9 | #define O_FLEXFILE 0xffffffff 10 | #define FIXED_PATH "/tmp/flexfile" 11 | 12 | static int (*real_open)(const char *pathname, int flags, ...) = NULL; 13 | 14 | int open(const char *pathname, int flags, ...) 15 | { 16 | if ((flags & O_FLEXFILE) != O_FLEXFILE && 17 | strncmp(pathname, FIXED_PATH, strlen(pathname)) != 0) { 18 | va_list args; 19 | va_start(args, flags); 20 | mode_t mode = va_arg(args, mode_t); 21 | va_end(args); 22 | if (!real_open) { 23 | real_open = (typeof(real_open))dlsym(RTLD_NEXT, "open"); 24 | } 25 | return real_open(pathname, flags, mode); 26 | } 27 | printf("open %s\n", pathname); 28 | fflush(stdout); 29 | struct flexfile *const ff = flexfile_open(pathname); 30 | if (ff) { 31 | FILE *const fp = tmpfile(); 32 | const int fd = fileno(fp); 33 | printf("open succeed, fake fd %d\n", fd); 34 | fflush(stdout); 35 | files[fd] = ff; 36 | tmpfiles[fd] = fp; 37 | return fd; 38 | } 39 | return 0; 40 | (void)flags; 41 | } 42 | 43 | static int (*real_close)(int fd) = NULL; 44 | 45 | int close(int fd) 46 | { 47 | if (!files[fd]) { 48 | if (!real_close) { 49 | real_close = (typeof(real_close))dlsym(RTLD_NEXT, "close"); 50 | } 51 | return real_close(fd); 52 | } 53 | printf("close %d\n", fd); 54 | fflush(stdout); 55 | struct flexfile *const ff = files[fd]; 56 | FILE *const fp = tmpfiles[fd]; 57 | fclose(fp); 58 | flexfile_close(ff); 59 | files[fd] = NULL; 60 | tmpfiles[fd] = NULL; 61 | return 0; 62 | } 63 | 64 | static ssize_t (*real_read)(int fd, void *buf, size_t count) = NULL; 65 | 66 | ssize_t read(int fd, void *buf, size_t count) 67 | { 68 | if (!files[fd]) { 69 | if (!real_read) { 70 | real_read= (typeof(real_read))dlsym(RTLD_NEXT, "read"); 71 | } 72 | return real_read(fd, buf, count); 73 | } 74 | const off_t off = lseek(fd, 0, SEEK_CUR); 75 | return pread(fd, buf, count, off); 76 | } 77 | 78 | static ssize_t (*real_pread)(int fd, void *buf, size_t count, off_t offset) = NULL; 79 | 80 | ssize_t pread(int fd, void *buf, size_t count, off_t offset) 81 | { 82 | if (!files[fd]) { 83 | if (!real_pread) { 84 | real_pread= (typeof(real_pread))dlsym(RTLD_NEXT, "pread"); 85 | } 86 | return real_pread(fd, buf, count, offset); 87 | } 88 | struct flexfile *const ff = files[fd]; 89 | return flexfile_read(ff, buf, offset, count); 90 | } 91 | 92 | static ssize_t (*real_write)(int fd, const void *buf, size_t count) = NULL; 93 | 94 | ssize_t write(int fd, const void *buf, size_t count) 95 | { 96 | if (!files[fd]) { 97 | if (!real_write) { 98 | real_write = (typeof(real_write))dlsym(RTLD_NEXT, "write"); 99 | } 100 | return real_write(fd, buf, count); 101 | } 102 | const off_t off = lseek(fd, 0, SEEK_CUR); 103 | return pwrite(fd, buf, count, off); 104 | } 105 | 106 | static ssize_t (*real_pwrite)(int fd, const void *buf, size_t count, off_t offset) = NULL; 107 | 108 | ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset) 109 | { 110 | if (!files[fd]) { 111 | if (!real_pwrite) { 112 | real_pwrite = (typeof(real_pwrite))dlsym(RTLD_NEXT, "pwrite"); 113 | } 114 | return real_pwrite(fd, buf, count, offset); 115 | } 116 | struct flexfile *const ff = files[fd]; 117 | return flexfile_write(ff, buf, offset, count); 118 | } 119 | 120 | static int (*real_fsync)(int fd) = NULL; 121 | 122 | int fsync(int fd) 123 | { 124 | if (!files[fd]) { 125 | if (!real_fsync) { 126 | real_fsync = (typeof(real_fsync))dlsym(RTLD_NEXT, "fsync"); 127 | } 128 | return real_fsync(fd); 129 | } 130 | struct flexfile *const ff = files[fd]; 131 | flexfile_sync(ff); 132 | return 0; 133 | } 134 | 135 | static int (*real_ftruncate)(int fd, off_t length) = NULL; 136 | 137 | int ftruncate(int fd, off_t length) 138 | { 139 | if (!files[fd]) { 140 | if (!real_ftruncate) { 141 | real_ftruncate = (typeof(real_ftruncate))dlsym(RTLD_NEXT, "ftruncate"); 142 | } 143 | return real_ftruncate(fd, length); 144 | } 145 | 146 | struct flexfile *const ff = files[fd]; 147 | return flexfile_ftruncate(ff, length); 148 | } 149 | 150 | static int (*real_dup)(int oldfd) = NULL; 151 | 152 | int dup(int oldfd) 153 | { 154 | if (!files[oldfd]) { 155 | if (!real_dup) { 156 | real_dup = (typeof(real_dup))dlsym(RTLD_NEXT, "dup"); 157 | } 158 | return real_dup(oldfd); 159 | } 160 | printf("dup is not implemented for flexfile\n"); 161 | fflush(stdout); 162 | exit(1); 163 | } 164 | 165 | static int (*real_dup2)(int oldfd, int newfd) = NULL; 166 | 167 | int dup2(int oldfd, int newfd) 168 | { 169 | if (!files[oldfd]) { 170 | if (!real_dup2) { 171 | real_dup2 = (typeof(real_dup2))dlsym(RTLD_NEXT, "dup2"); 172 | } 173 | return real_dup2(oldfd, newfd); 174 | } 175 | printf("dup2 is not implemented for flexfile\n"); 176 | fflush(stdout); 177 | exit(1); 178 | } 179 | -------------------------------------------------------------------------------- /wrapper.h: -------------------------------------------------------------------------------- 1 | #ifndef _WRAPPER_H 2 | #define _WRAPPER_H 3 | 4 | #include "flexfile.h" 5 | 6 | extern int open(const char *pathname, int flags, ...); 7 | 8 | extern int close(int fd); 9 | 10 | extern ssize_t read(int fd, void *buf, size_t count); 11 | 12 | extern ssize_t pread(int fd, void *buf, size_t count, off_t offset); 13 | 14 | extern ssize_t write(int fd, const void *buf, size_t count); 15 | 16 | extern ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset); 17 | 18 | extern int fsync(int fd); 19 | 20 | extern off_t lseek(int fd, off_t offset, int whence); 21 | 22 | extern int ftruncate(int fd, off_t length); 23 | 24 | extern int dup(int oldfd); 25 | 26 | extern int dup2(int oldfd, int newfd); 27 | 28 | #endif 29 | --------------------------------------------------------------------------------