├── README.md ├── fst-serialized ├── src │ └── CMakeLists.txt ├── CMakeLists.txt ├── test │ ├── CMakeLists.txt │ └── TestFST.cpp └── include │ ├── common.h │ ├── popcount.h │ └── FST.hpp ├── fst ├── src │ ├── CMakeLists.txt │ ├── bitmap-rank.cc │ ├── bitmap-rankF.cc │ ├── bitmap-select.cc │ └── FST.cpp ├── CMakeLists.txt ├── test │ ├── CMakeLists.txt │ └── TestFST.cpp └── include │ ├── shared.h │ ├── common.h │ ├── bitmap-select.h │ ├── bitmap-rank.h │ ├── bitmap-rankF.h │ ├── FST.hpp │ └── popcount.h ├── third-party └── art │ ├── src │ ├── CMakeLists.txt │ └── ART.cpp │ ├── CMakeLists.txt │ └── include │ └── ART.hpp ├── benchmark ├── ycsb_download.sh ├── include │ ├── microbench.h │ ├── allocatortracker.h │ └── index.hpp ├── CMakeLists.txt ├── workload_spec │ ├── workloada │ ├── workloadb │ ├── workloadc │ ├── workloadf │ ├── workloadd │ ├── workloade │ └── workload_template ├── workload_email.cpp ├── gen_workload.py └── workload.cpp ├── CMakeLists.txt └── .gitmodules /README.md: -------------------------------------------------------------------------------- 1 | # fast-succinct-trie -------------------------------------------------------------------------------- /fst-serialized/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(FST SHARED FST.cpp) 2 | -------------------------------------------------------------------------------- /fst/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(FST SHARED FST.cpp bitmap-rank.cc bitmap-rankF.cc bitmap-select.cc) 2 | -------------------------------------------------------------------------------- /third-party/art/src/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(ART SHARED ART.cpp) 2 | add_library(CART SHARED CART.cpp) 3 | 4 | 5 | -------------------------------------------------------------------------------- /benchmark/ycsb_download.sh: -------------------------------------------------------------------------------- 1 | curl -O --location https://github.com/brianfrankcooper/YCSB/releases/download/0.12.0/ycsb-0.12.0.tar.gz 2 | tar xfvz ycsb-0.12.0.tar.gz 3 | rm ycsb-0.12.0.tar.gz 4 | mv ycsb-0.12.0 YCSB 5 | -------------------------------------------------------------------------------- /third-party/art/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project (art) 3 | 4 | message(STATUS "Configuring..." ${CMAKE_PROJECT_NAME}) 5 | 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O9 -Werror -mpopcnt -pthread -std=c++11") 7 | 8 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include") 9 | 10 | add_subdirectory(src) 11 | 12 | -------------------------------------------------------------------------------- /fst/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project (fst) 3 | 4 | message(STATUS "Configuring..." ${CMAKE_PROJECT_NAME}) 5 | 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O9 -Werror -mpopcnt -pthread -std=c++11") 7 | 8 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include") 9 | 10 | add_subdirectory(src) 11 | add_subdirectory(test) 12 | -------------------------------------------------------------------------------- /fst-serialized/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project (fst) 3 | 4 | message(STATUS "Configuring..." ${CMAKE_PROJECT_NAME}) 5 | 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O9 -Werror -mpopcnt -pthread -std=c++11") 7 | 8 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include") 9 | 10 | add_subdirectory(src) 11 | add_subdirectory(test) 12 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.6) 2 | project(fast-succinct-trie) 3 | 4 | message(STATUS "Configuring..." ${CMAKE_PROJECT_NAME}) 5 | 6 | add_subdirectory(fst) 7 | #add_subdirectory(fst-serialized) 8 | add_subdirectory(benchmark) 9 | add_subdirectory(third-party/art) 10 | #add_subdirectory(third-party/tx-trie) 11 | #add_subdirectory(third-party/path_decomposed_tries) 12 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "third-party/stx-btree"] 2 | path = third-party/stx-btree 3 | url = git@github.com:bingmann/stx-btree.git 4 | [submodule "third-party/path_decomposed_tries"] 5 | path = third-party/path_decomposed_tries 6 | url = git@github.com:ot/path_decomposed_tries.git 7 | [submodule "third-party/tx-trie"] 8 | path = third-party/tx-trie 9 | url = git@github.com:hillbig/tx-trie.git 10 | -------------------------------------------------------------------------------- /fst/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${PROJECT_SOURCE_DIR}/src) 2 | find_package(GTest REQUIRED) 3 | include_directories(${GTEST_INCLUDE_DIR}) 4 | 5 | function (add_fst_test file_name ) 6 | add_executable(${file_name} ${file_name}.cpp) 7 | target_link_libraries(${file_name} FST gtest) 8 | add_test(NAME ${file_name} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${file_name}) 9 | endfunction() 10 | 11 | add_fst_test(TestFST) 12 | -------------------------------------------------------------------------------- /fst-serialized/test/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories(${PROJECT_SOURCE_DIR}/src) 2 | find_package(GTest REQUIRED) 3 | include_directories(${GTEST_INCLUDE_DIR}) 4 | 5 | function (add_fst_test file_name ) 6 | add_executable(${file_name} ${file_name}.cpp) 7 | target_link_libraries(${file_name} FST gtest) 8 | add_test(NAME ${file_name} COMMAND ${CMAKE_CURRENT_BINARY_DIR}/${file_name}) 9 | endfunction() 10 | 11 | add_fst_test(TestFST) 12 | -------------------------------------------------------------------------------- /benchmark/include/microbench.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | 11 | //#include "allocatortracker.h" 12 | 13 | #include "index.hpp" 14 | 15 | #define LIMIT 10000000 16 | #define RANGE_PLUS 0 17 | 18 | //============================================================== 19 | inline double get_now() { 20 | struct timeval tv; 21 | gettimeofday(&tv, 0); 22 | return tv.tv_sec + tv.tv_usec / 1000000.0; 23 | } 24 | 25 | -------------------------------------------------------------------------------- /fst/include/shared.h: -------------------------------------------------------------------------------- 1 | #ifndef _SHARED_H_ 2 | #define _SHARED_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | typedef uint16_t uint16; 10 | typedef uint32_t uint32; 11 | typedef uint64_t uint64; 12 | 13 | const int kCacheLineSize = 64; 14 | 15 | inline double 16 | timeval_diff(const struct timeval *start, const struct timeval *end) 17 | { 18 | double r = (end->tv_sec - start->tv_sec)* 1000000; 19 | 20 | if (end->tv_usec > start->tv_usec) 21 | r += (end->tv_usec - start->tv_usec); 22 | else if (end->tv_usec < start->tv_usec) 23 | r -= (start->tv_usec - end->tv_usec); 24 | 25 | return (double) r / 1000000; 26 | } 27 | 28 | #endif /* _SHARED_H_ */ 29 | 30 | -------------------------------------------------------------------------------- /benchmark/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 2.6) 2 | project (benchmark) 3 | 4 | message(STATUS "Configuring..." ${CMAKE_PROJECT_NAME}) 5 | 6 | set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g -O9 -Werror -mpopcnt -pthread -std=c++11") 7 | 8 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/include") 9 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../fst/include") 10 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../third-party/stx-btree/include") 11 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../third-party/stx-btree/include/stx") 12 | include_directories("${CMAKE_CURRENT_SOURCE_DIR}/../third-party/art/include") 13 | 14 | set(COMMON_DEPENDENCIES tcmalloc_minimal pthread) 15 | 16 | add_executable(workload workload.cpp) 17 | target_link_libraries(workload ${COMMON_DEPENDENCIES} ART CART FST) 18 | 19 | add_executable(workload_email workload_email.cpp) 20 | target_link_libraries(workload_email ${COMMON_DEPENDENCIES} ART CART FST) -------------------------------------------------------------------------------- /fst/include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_H_ 2 | #define _COMMON_H_ 3 | 4 | #include 5 | 6 | #define MSB_MASK 0x8000000000000000 7 | 8 | #define likely(x) __builtin_expect(!!(x), 1) 9 | #define unlikely(x) __builtin_expect(!!(x), 0) 10 | 11 | inline void setBit(uint64_t &byte, int pos) { 12 | byte = byte | (MSB_MASK >> pos); 13 | } 14 | 15 | inline bool readBit(uint64_t &byte, int pos) { 16 | return byte & (MSB_MASK >> pos); 17 | } 18 | 19 | inline bool isLabelExist(uint64_t *bits, uint8_t c) { 20 | int group = c >> 6; 21 | int idx = c & 63; 22 | return bits[group] & (MSB_MASK >> idx); 23 | } 24 | 25 | inline bool isLabelExist_lowerBound(uint64_t *bits, uint8_t c, uint8_t &pos) { 26 | int group = c >> 6; 27 | int idx = c & 63; 28 | uint64_t b64 = bits[group]; 29 | if (b64 & (MSB_MASK >> idx)) { 30 | pos = c; 31 | return true; 32 | } 33 | else { 34 | b64 <<= idx; 35 | if (b64) { 36 | pos = c + __builtin_clzll(b64); 37 | return true; 38 | } 39 | for (int i = (group + 1); i < 4; i++) { 40 | b64 = bits[i]; 41 | if (b64) { 42 | pos = (i << 6) + __builtin_clzll(b64); 43 | return true; 44 | } 45 | } 46 | } 47 | return false; 48 | } 49 | 50 | inline void setLabel(uint64_t *bits, uint8_t c) { 51 | int group = c >> 6; 52 | int idx = c & 63; 53 | bits[group] |= ((uint64_t)1 << (63 - idx)); 54 | } 55 | 56 | inline int commonPrefixLen(std::string &a, std::string &b) { 57 | int len = a.length(); 58 | if (b.length() < len) 59 | len = b.length(); 60 | for (int i = 0; i < len; i++) { 61 | if (a[i] != b[i]) 62 | return i; 63 | } 64 | return len; 65 | } 66 | 67 | #endif /* _COMMON_H_ */ 68 | 69 | -------------------------------------------------------------------------------- /benchmark/include/allocatortracker.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | 10 | template 11 | /** 12 | * Custom allocator for all the indexes except array index. 13 | */ 14 | class AllocatorTracker : public std::allocator { 15 | public: 16 | typedef typename std::allocator BaseAllocator; 17 | typedef typename BaseAllocator::pointer pointer; 18 | typedef typename BaseAllocator::size_type size_type; 19 | 20 | int64_t *memory_size; 21 | 22 | AllocatorTracker() throw() : BaseAllocator() {} 23 | 24 | AllocatorTracker(int64_t* m_ptr) throw() : BaseAllocator() { 25 | memory_size = m_ptr; 26 | } 27 | AllocatorTracker(const AllocatorTracker& allocator) throw() : BaseAllocator(allocator) { 28 | memory_size = allocator.memory_size; 29 | } 30 | template AllocatorTracker(const AllocatorTracker& allocator) throw(): BaseAllocator(allocator) { 31 | memory_size = allocator.memory_size; 32 | } 33 | 34 | ~AllocatorTracker() {} 35 | 36 | template struct rebind { 37 | typedef AllocatorTracker other; 38 | }; 39 | 40 | pointer allocate(size_type size) { 41 | pointer dataPtr = BaseAllocator::allocate(size); 42 | *memory_size += size * sizeof(ValueType); 43 | return dataPtr; 44 | } 45 | 46 | pointer allocate(size_type size, void* ptr) { 47 | pointer dataPtr = BaseAllocator::allocate(size, ptr); 48 | *memory_size += size * sizeof(ValueType); 49 | return dataPtr; 50 | } 51 | 52 | pointer allocate(size_type size, pointer ptr) { 53 | pointer dataPtr = BaseAllocator::allocate(size, ptr); 54 | *memory_size += size * sizeof(ValueType); 55 | return dataPtr; 56 | } 57 | 58 | void deallocate(pointer ptr, size_type size) throw() { 59 | BaseAllocator::deallocate(ptr, size); 60 | *memory_size -= size * sizeof(ValueType); 61 | } 62 | }; 63 | -------------------------------------------------------------------------------- /fst/include/bitmap-select.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2013 Carnegie Mellon University 3 | 4 | Authors: Dong Zhou, David G. Andersen and Michale Kaminsky 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | Modified by Huanchen Zhang 19 | */ 20 | 21 | #ifndef _BITMAPSELECT_H_ 22 | #define _BITMAPSELECT_H_ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #define __STDC_FORMAT_MACROS 31 | #include 32 | 33 | #include "shared.h" 34 | 35 | class BitmapSelect { 36 | public: 37 | const int kWordSize = 64; 38 | const int kWordBits = 6; 39 | const uint32 kWordMask = ((uint32)1 << kWordBits) - 1; 40 | 41 | const int skip = 64; 42 | const int kSkipBits = 6; 43 | const uint32 kSkipMask = ((uint32)1 << kSkipBits) - 1; 44 | 45 | BitmapSelect() { } 46 | virtual uint32 select(uint32 rank) = 0; 47 | }; 48 | 49 | class BitmapSelectPoppy: public BitmapSelect { 50 | public: 51 | BitmapSelectPoppy(uint64* bits, uint32 nbits); 52 | ~BitmapSelectPoppy() {} 53 | 54 | uint32 select(uint32 rank); 55 | 56 | uint64* getBits(); 57 | uint32 getNbits(); 58 | uint32 getMem(); 59 | 60 | friend class FST; 61 | friend class FSTIter; 62 | 63 | private: 64 | uint64* bits_; 65 | uint32 nbits_; 66 | uint32 mem_; 67 | 68 | uint32 wordCount_; 69 | uint32 pCount_; 70 | uint32* selectLUT_; 71 | uint32 selectLUTCount_; 72 | }; 73 | 74 | #endif /* _BITMAPSELECT_H_ */ 75 | -------------------------------------------------------------------------------- /fst/include/bitmap-rank.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2013 Carnegie Mellon University 3 | 4 | Authors: Dong Zhou, David G. Andersen and Michale Kaminsky 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | Modified by Huanchen Zhang 19 | */ 20 | 21 | #ifndef _BITMAPRANK_H_ 22 | #define _BITMAPRANK_H_ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #define __STDC_FORMAT_MACROS 31 | #include 32 | 33 | #include "shared.h" 34 | 35 | class BitmapRank { 36 | public: 37 | const int kWordSize = 64; 38 | const int kBasicBlockSize = 512; 39 | const int kBasicBlockBits = 9; 40 | const int kBasicBlockMask = kBasicBlockSize - 1; 41 | const int kWordCountPerBasicBlock = kBasicBlockSize / kWordSize; 42 | 43 | BitmapRank() { pCount_ = 0; } 44 | virtual uint32 rank(uint32 pos) = 0; 45 | uint64 pCount() { return pCount_; } 46 | 47 | protected: 48 | uint64 pCount_; 49 | }; 50 | 51 | class BitmapRankPoppy: public BitmapRank { 52 | public: 53 | BitmapRankPoppy(uint64* bits, uint32 nbits); 54 | ~BitmapRankPoppy() {} 55 | 56 | uint32 rank(uint32 pos); 57 | 58 | uint64* getBits(); 59 | uint32 getNbits(); 60 | uint32 getMem(); 61 | 62 | friend class FST; 63 | friend class FSTIter; 64 | 65 | private: 66 | uint64* bits_; 67 | uint32 nbits_; 68 | uint32 mem_; 69 | 70 | uint32* rankLUT_; 71 | uint32 basicBlockCount_; 72 | }; 73 | 74 | #endif /* _BITMAPRANK_H_ */ 75 | -------------------------------------------------------------------------------- /fst/src/bitmap-rank.cc: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2013 Carnegie Mellon University 3 | 4 | Authors: Dong Zhou, David G. Andersen and Michale Kaminsky 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | Modified by Huanchen Zhang 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "bitmap-rank.h" 26 | #include "popcount.h" 27 | #include "shared.h" 28 | 29 | #include 30 | 31 | BitmapRankPoppy::BitmapRankPoppy(uint64 *bits, uint32 nbits) 32 | { 33 | bits_ = bits; 34 | nbits_ = nbits; 35 | basicBlockCount_ = nbits_ / kBasicBlockSize; 36 | 37 | assert(posix_memalign((void **) &rankLUT_, kCacheLineSize, basicBlockCount_ * sizeof(uint32)) >= 0); 38 | 39 | uint32 rankCum = 0; 40 | for (uint32 i = 0; i < basicBlockCount_; i++) { 41 | rankLUT_[i] = rankCum; 42 | rankCum += popcountLinear(bits_, 43 | i * kWordCountPerBasicBlock, 44 | kBasicBlockSize); 45 | } 46 | rankLUT_[basicBlockCount_-1] = rankCum; 47 | 48 | pCount_ = rankCum; 49 | mem_ = nbits / 8 + basicBlockCount_ * sizeof(uint32); 50 | } 51 | 52 | uint32 BitmapRankPoppy::rank(uint32 pos) 53 | { 54 | assert(pos <= nbits_); 55 | uint32 blockId = pos >> kBasicBlockBits; 56 | return rankLUT_[blockId] + popcountLinear(bits_, (blockId << 3), (pos & 511)); 57 | } 58 | 59 | uint64* BitmapRankPoppy::getBits() { 60 | return bits_; 61 | } 62 | 63 | uint32 BitmapRankPoppy::getNbits() { 64 | return nbits_; 65 | } 66 | 67 | uint32 BitmapRankPoppy::getMem() { 68 | return mem_; 69 | } 70 | -------------------------------------------------------------------------------- /fst/include/bitmap-rankF.h: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2013 Carnegie Mellon University 3 | 4 | Authors: Dong Zhou, David G. Andersen and Michale Kaminsky 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | Modified by Huanchen Zhang 19 | */ 20 | 21 | #ifndef _BITMAPRANKF_H_ 22 | #define _BITMAPRANKF_H_ 23 | 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include 29 | #include 30 | #define __STDC_FORMAT_MACROS 31 | #include 32 | 33 | #include "shared.h" 34 | 35 | class BitmapRankF { 36 | public: 37 | const int kWordSize = 64; 38 | const int kBasicBlockSize = 64; 39 | const int kBasicBlockBits = 6; 40 | const int kBasicBlockMask = kBasicBlockSize - 1; 41 | const int kWordCountPerBasicBlock = kBasicBlockSize / kWordSize; 42 | 43 | BitmapRankF() { pCount_ = 0; } 44 | virtual uint32 rank(uint32 pos) = 0; 45 | uint64 pCount() { return pCount_; } 46 | 47 | protected: 48 | uint64 pCount_; 49 | }; 50 | 51 | class BitmapRankFPoppy: public BitmapRankF { 52 | public: 53 | BitmapRankFPoppy(uint64* bits, uint32 nbits); 54 | ~BitmapRankFPoppy() {} 55 | 56 | uint32 rank(uint32 pos); 57 | 58 | uint64* getBits(); 59 | uint32 getNbits(); 60 | uint32 getMem(); 61 | 62 | friend class FST; 63 | friend class FSTIter; 64 | 65 | private: 66 | uint64* bits_; 67 | uint32 nbits_; 68 | uint32 mem_; 69 | 70 | uint32* rankLUT_; 71 | uint32 basicBlockCount_; 72 | }; 73 | 74 | #endif /* _BITMAPRANKF_H_ */ 75 | -------------------------------------------------------------------------------- /fst/src/bitmap-rankF.cc: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2013 Carnegie Mellon University 3 | 4 | Authors: Dong Zhou, David G. Andersen and Michale Kaminsky 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | Modified by Huanchen Zhang 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "bitmap-rankF.h" 26 | #include "popcount.h" 27 | #include "shared.h" 28 | 29 | #include 30 | 31 | BitmapRankFPoppy::BitmapRankFPoppy(uint64 *bits, uint32 nbits) 32 | { 33 | bits_ = bits; 34 | nbits_ = nbits; 35 | basicBlockCount_ = nbits_ / kBasicBlockSize; 36 | 37 | assert(posix_memalign((void **) &rankLUT_, kCacheLineSize, basicBlockCount_ * sizeof(uint32)) >= 0); 38 | 39 | uint32 rankCum = 0; 40 | for (uint32 i = 0; i < basicBlockCount_; i++) { 41 | rankLUT_[i] = rankCum; 42 | rankCum += popcountLinear(bits_, 43 | i * kWordCountPerBasicBlock, 44 | kBasicBlockSize); 45 | } 46 | rankLUT_[basicBlockCount_-1] = rankCum; 47 | 48 | pCount_ = rankCum; 49 | mem_ = nbits / 8 + basicBlockCount_ * sizeof(uint32); 50 | } 51 | 52 | uint32 BitmapRankFPoppy::rank(uint32 pos) 53 | { 54 | assert(pos <= nbits_); 55 | uint32 blockId = pos >> kBasicBlockBits; 56 | uint32 offset = pos & (uint32)63; 57 | if (offset) 58 | return rankLUT_[blockId] + popcount(bits_[blockId] >> (64 - offset)); 59 | else 60 | return rankLUT_[blockId]; 61 | } 62 | 63 | uint64* BitmapRankFPoppy::getBits() { 64 | return bits_; 65 | } 66 | 67 | uint32 BitmapRankFPoppy::getNbits() { 68 | return nbits_; 69 | } 70 | 71 | uint32 BitmapRankFPoppy::getMem() { 72 | return mem_; 73 | } 74 | -------------------------------------------------------------------------------- /fst-serialized/include/common.h: -------------------------------------------------------------------------------- 1 | #ifndef _COMMON_H_ 2 | #define _COMMON_H_ 3 | 4 | #include 5 | 6 | #define MSB_MASK 0x8000000000000000 7 | 8 | #define likely(x) __builtin_expect(!!(x), 1) 9 | #define unlikely(x) __builtin_expect(!!(x), 0) 10 | 11 | inline void setBit(uint64_t &byte, int pos) { 12 | byte = byte | (MSB_MASK >> pos); 13 | } 14 | 15 | inline bool readBit(uint64_t &byte, int pos) { 16 | return byte & (MSB_MASK >> pos); 17 | } 18 | 19 | inline bool isLabelExist(uint64_t *bits, uint8_t c) { 20 | int group = c >> 6; 21 | int idx = c & 63; 22 | return bits[group] & (MSB_MASK >> idx); 23 | } 24 | 25 | inline bool isLabelExist_lowerBound(uint64_t *bits, uint8_t c, uint8_t &pos) { 26 | int group = c >> 6; 27 | int idx = c & 63; 28 | uint64_t b64 = bits[group]; 29 | if (b64 & (MSB_MASK >> idx)) { 30 | pos = c; 31 | return true; 32 | } 33 | else { 34 | b64 <<= idx; 35 | if (b64) { 36 | pos = c + __builtin_clzll(b64); 37 | return true; 38 | } 39 | for (int i = (group + 1); i < 4; i++) { 40 | b64 = bits[i]; 41 | if (b64) { 42 | pos = (i << 6) + __builtin_clzll(b64); 43 | return true; 44 | } 45 | } 46 | } 47 | return false; 48 | } 49 | 50 | inline bool isLabelExist_upperBound(uint64_t *bits, uint8_t c, uint8_t &pos) { 51 | int group = c >> 6; 52 | int idx = c & 63; 53 | uint64_t b64 = bits[group]; 54 | if (b64 & (MSB_MASK >> idx)) { 55 | pos = c; 56 | return true; 57 | } 58 | else { 59 | b64 >>= (63 - idx); 60 | if (b64) { 61 | pos = c - __builtin_ctzll(b64); 62 | return true; 63 | } 64 | for (int i = (group - 1); i >= 0; i--) { 65 | b64 = bits[i]; 66 | if (b64) { 67 | pos = ((i + 1) << 6) - 1 - __builtin_ctzll(b64); 68 | return true; 69 | } 70 | } 71 | } 72 | return false; 73 | } 74 | 75 | inline void setLabel(uint64_t *bits, uint8_t c) { 76 | int group = c >> 6; 77 | int idx = c & 63; 78 | bits[group] |= ((uint64_t)1 << (63 - idx)); 79 | } 80 | 81 | inline int commonPrefixLen(std::string &a, std::string &b) { 82 | int len = a.length(); 83 | if (b.length() < len) 84 | len = b.length(); 85 | for (int i = 0; i < len; i++) { 86 | if (a[i] != b[i]) 87 | return i; 88 | } 89 | return len; 90 | } 91 | 92 | #endif /* _COMMON_H_ */ 93 | 94 | -------------------------------------------------------------------------------- /fst/src/bitmap-select.cc: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright 2013 Carnegie Mellon University 3 | 4 | Authors: Dong Zhou, David G. Andersen and Michale Kaminsky 5 | 6 | Licensed under the Apache License, Version 2.0 (the "License"); 7 | you may not use this file except in compliance with the License. 8 | You may obtain a copy of the License at 9 | 10 | http://www.apache.org/licenses/LICENSE-2.0 11 | 12 | Unless required by applicable law or agreed to in writing, software 13 | distributed under the License is distributed on an "AS IS" BASIS, 14 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | See the License for the specific language governing permissions and 16 | limitations under the License. 17 | 18 | Modified by Huanchen Zhang 19 | */ 20 | 21 | #include 22 | #include 23 | #include 24 | 25 | #include "bitmap-select.h" 26 | #include "popcount.h" 27 | #include "shared.h" 28 | 29 | #include 30 | 31 | BitmapSelectPoppy::BitmapSelectPoppy(uint64 *bits, uint32 nbits) 32 | { 33 | bits_ = bits; 34 | nbits_ = nbits; 35 | 36 | uint32 wordCount_ = nbits_ / kWordSize; 37 | 38 | uint32* rankLUT; 39 | assert(posix_memalign((void **) &rankLUT, kCacheLineSize, (wordCount_ + 1) * sizeof(uint32)) >= 0); 40 | 41 | uint32 rankCum = 0; 42 | for (uint32 i = 0; i < wordCount_; i++) { 43 | rankLUT[i] = rankCum; 44 | rankCum += popcount(bits_[i]); 45 | } 46 | rankLUT[wordCount_] = rankCum; 47 | pCount_ = rankCum; 48 | 49 | selectLUTCount_ = pCount_ / skip + 1; 50 | assert(posix_memalign((void **) &selectLUT_, kCacheLineSize, (selectLUTCount_ + 1) * sizeof(uint32)) >= 0); 51 | 52 | selectLUT_[0] = 0; 53 | uint32 idx = 1; 54 | for (uint32 i = 1; i <= wordCount_; i++) { 55 | while (idx * skip <= rankLUT[i]) { 56 | int rankR = idx * skip - rankLUT[i-1]; 57 | selectLUT_[idx] = (i - 1) * kWordSize + select64_popcount_search(bits_[i-1], rankR) + 1; 58 | idx++; 59 | } 60 | } 61 | 62 | free(rankLUT); 63 | 64 | mem_ = nbits_ / 8 + (selectLUTCount_ + 1) * sizeof(uint32); 65 | } 66 | 67 | uint32 BitmapSelectPoppy::select(uint32 rank) { 68 | assert(rank <= pCount_); 69 | 70 | uint32 s = selectLUT_[rank >> kSkipBits]; 71 | uint32 rankR = rank & kSkipMask; 72 | 73 | if (rankR == 0) 74 | return s - 1; 75 | 76 | int idx = s >> kWordBits; 77 | int startWordBit = s & kWordMask; 78 | uint64 word = bits_[idx] << startWordBit >> startWordBit; 79 | 80 | int pop = 0; 81 | while ((pop = popcount(word)) < rankR) { 82 | idx++; 83 | word = bits_[idx]; 84 | rankR -= pop; 85 | } 86 | 87 | return (idx << kWordBits) + select64_popcount_search(word, rankR); 88 | } 89 | 90 | uint64* BitmapSelectPoppy::getBits() { 91 | return bits_; 92 | } 93 | 94 | uint32 BitmapSelectPoppy::getNbits() { 95 | return nbits_; 96 | } 97 | 98 | uint32 BitmapSelectPoppy::getMem() { 99 | return mem_; 100 | } 101 | -------------------------------------------------------------------------------- /benchmark/workload_spec/workloada: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010 Yahoo! Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you 4 | # may not use this file except in compliance with the License. You 5 | # may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. See the License for the specific language governing 13 | # permissions and limitations under the License. See accompanying 14 | # LICENSE file. 15 | 16 | 17 | # Yahoo! Cloud System Benchmark 18 | # Workload A: Update heavy workload 19 | # Application example: Session store recording recent actions 20 | # 21 | # Read/update ratio: 50/50 22 | # Default data size: 1 KB records (10 fields, 100 bytes each, plus key) 23 | # Request distribution: zipfian 24 | 25 | recordcount=1000 26 | operationcount=1000 27 | workload=com.yahoo.ycsb.workloads.CoreWorkload 28 | 29 | readallfields=true 30 | 31 | readproportion=0.5 32 | updateproportion=0.5 33 | scanproportion=0 34 | insertproportion=0 35 | 36 | requestdistribution=zipfian 37 | 38 | -------------------------------------------------------------------------------- /benchmark/workload_spec/workloadb: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010 Yahoo! Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you 4 | # may not use this file except in compliance with the License. You 5 | # may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. See the License for the specific language governing 13 | # permissions and limitations under the License. See accompanying 14 | # LICENSE file. 15 | 16 | # Yahoo! Cloud System Benchmark 17 | # Workload B: Read mostly workload 18 | # Application example: photo tagging; add a tag is an update, but most operations are to read tags 19 | # 20 | # Read/update ratio: 95/5 21 | # Default data size: 1 KB records (10 fields, 100 bytes each, plus key) 22 | # Request distribution: zipfian 23 | 24 | recordcount=1000 25 | operationcount=1000 26 | workload=com.yahoo.ycsb.workloads.CoreWorkload 27 | 28 | readallfields=true 29 | 30 | readproportion=0.95 31 | updateproportion=0.05 32 | scanproportion=0 33 | insertproportion=0 34 | 35 | requestdistribution=zipfian 36 | 37 | -------------------------------------------------------------------------------- /benchmark/workload_spec/workloadc: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010 Yahoo! Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you 4 | # may not use this file except in compliance with the License. You 5 | # may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. See the License for the specific language governing 13 | # permissions and limitations under the License. See accompanying 14 | # LICENSE file. 15 | 16 | # Yahoo! Cloud System Benchmark 17 | # Workload C: Read only 18 | # Application example: user profile cache, where profiles are constructed elsewhere (e.g., Hadoop) 19 | # 20 | # Read/update ratio: 100/0 21 | # Default data size: 1 KB records (10 fields, 100 bytes each, plus key) 22 | # Request distribution: zipfian 23 | 24 | recordcount=50000000 25 | operationcount=10000000 26 | workload=com.yahoo.ycsb.workloads.CoreWorkload 27 | 28 | readallfields=true 29 | 30 | readproportion=1 31 | updateproportion=0 32 | scanproportion=0 33 | insertproportion=0 34 | 35 | requestdistribution=zipfian 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /benchmark/workload_spec/workloadf: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010 Yahoo! Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you 4 | # may not use this file except in compliance with the License. You 5 | # may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. See the License for the specific language governing 13 | # permissions and limitations under the License. See accompanying 14 | # LICENSE file. 15 | 16 | # Yahoo! Cloud System Benchmark 17 | # Workload F: Read-modify-write workload 18 | # Application example: user database, where user records are read and modified by the user or to record user activity. 19 | # 20 | # Read/read-modify-write ratio: 50/50 21 | # Default data size: 1 KB records (10 fields, 100 bytes each, plus key) 22 | # Request distribution: zipfian 23 | 24 | recordcount=1000 25 | operationcount=1000 26 | workload=com.yahoo.ycsb.workloads.CoreWorkload 27 | 28 | readallfields=true 29 | 30 | readproportion=0.5 31 | updateproportion=0 32 | scanproportion=0 33 | insertproportion=0 34 | readmodifywriteproportion=0.5 35 | 36 | requestdistribution=zipfian 37 | 38 | -------------------------------------------------------------------------------- /benchmark/workload_spec/workloadd: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010 Yahoo! Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you 4 | # may not use this file except in compliance with the License. You 5 | # may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. See the License for the specific language governing 13 | # permissions and limitations under the License. See accompanying 14 | # LICENSE file. 15 | 16 | # Yahoo! Cloud System Benchmark 17 | # Workload D: Read latest workload 18 | # Application example: user status updates; people want to read the latest 19 | # 20 | # Read/update/insert ratio: 95/0/5 21 | # Default data size: 1 KB records (10 fields, 100 bytes each, plus key) 22 | # Request distribution: latest 23 | 24 | # The insert order for this is hashed, not ordered. The "latest" items may be 25 | # scattered around the keyspace if they are keyed by userid.timestamp. A workload 26 | # which orders items purely by time, and demands the latest, is very different than 27 | # workload here (which we believe is more typical of how people build systems.) 28 | 29 | recordcount=1000 30 | operationcount=1000 31 | workload=com.yahoo.ycsb.workloads.CoreWorkload 32 | 33 | readallfields=true 34 | 35 | readproportion=0.95 36 | updateproportion=0 37 | scanproportion=0 38 | insertproportion=0.05 39 | 40 | requestdistribution=latest 41 | 42 | -------------------------------------------------------------------------------- /benchmark/workload_spec/workloade: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2010 Yahoo! Inc. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you 4 | # may not use this file except in compliance with the License. You 5 | # may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. See the License for the specific language governing 13 | # permissions and limitations under the License. See accompanying 14 | # LICENSE file. 15 | 16 | # Yahoo! Cloud System Benchmark 17 | # Workload E: Short ranges 18 | # Application example: threaded conversations, where each scan is for the posts in a given thread (assumed to be clustered by thread id) 19 | # 20 | # Scan/insert ratio: 95/5 21 | # Default data size: 1 KB records (10 fields, 100 bytes each, plus key) 22 | # Request distribution: zipfian 23 | 24 | # The insert order is hashed, not ordered. Although the scans are ordered, it does not necessarily 25 | # follow that the data is inserted in order. For example, posts for thread 342 may not be inserted contiguously, but 26 | # instead interspersed with posts from lots of other threads. The way the YCSB client works is that it will pick a start 27 | # key, and then request a number of records; this works fine even for hashed insertion. 28 | 29 | recordcount=50000000 30 | operationcount=10000000 31 | workload=com.yahoo.ycsb.workloads.CoreWorkload 32 | 33 | readallfields=true 34 | 35 | readproportion=0 36 | updateproportion=0 37 | scanproportion=1 38 | insertproportion=0 39 | 40 | requestdistribution=zipfian 41 | 42 | maxscanlength=100 43 | 44 | scanlengthdistribution=uniform 45 | 46 | 47 | -------------------------------------------------------------------------------- /fst/include/FST.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | using namespace std; 15 | 16 | class FSTIter; 17 | class FST; 18 | 19 | class FST { 20 | public: 21 | static const uint8_t TERM = 36; //$ 22 | static const int CUTOFF_RATIO = 64; 23 | 24 | FST(); 25 | virtual ~FST(); 26 | 27 | void load(vector &keys, vector &values, int longestKeyLen); 28 | void load(vector &keys, vector &values); 29 | 30 | bool lookup(const uint8_t* key, const int keylen, uint64_t &value); 31 | bool lookup(const uint64_t key, uint64_t &value); 32 | 33 | bool lowerBound(const uint8_t* key, const int keylen, FSTIter &iter); 34 | bool lowerBound(const uint64_t key, FSTIter &iter); 35 | 36 | uint32_t cMemU(); 37 | uint32_t tMemU(); 38 | uint32_t oMemU(); 39 | uint32_t keyMemU(); 40 | uint32_t valueMemU(); 41 | 42 | uint64_t cMem(); 43 | uint32_t tMem(); 44 | uint32_t sMem(); 45 | uint64_t keyMem(); 46 | uint64_t valueMem(); 47 | 48 | uint64_t mem(); 49 | 50 | uint32_t numT(); 51 | 52 | void printU(); 53 | void print(); 54 | 55 | private: 56 | inline bool insertChar_cond(const uint8_t ch, vector &c, vector &t, vector &s, int &pos, int &nc); 57 | inline bool insertChar(const uint8_t ch, bool isTerm, vector &c, vector &t, vector &s, int &pos, int &nc); 58 | 59 | inline bool isCbitSetU(uint64_t nodeNum, uint8_t kc); 60 | inline bool isTbitSetU(uint64_t nodeNum, uint8_t kc); 61 | inline bool isObitSetU(uint64_t nodeNum); 62 | inline bool isSbitSet(uint64_t pos); 63 | inline bool isTbitSet(uint64_t pos); 64 | inline uint64_t valuePosU(uint64_t nodeNum, uint64_t pos); 65 | inline uint64_t valuePos(uint64_t pos); 66 | 67 | inline uint64_t childNodeNumU(uint64_t pos); 68 | inline uint64_t childNodeNum(uint64_t pos); 69 | inline uint64_t childpos(uint64_t nodeNum); 70 | 71 | inline int nodeSize(uint64_t pos); 72 | inline bool simdSearch(uint64_t &pos, uint64_t size, uint8_t target); 73 | inline bool binarySearch(uint64_t &pos, uint64_t size, uint8_t target); 74 | inline bool linearSearch(uint64_t &pos, uint64_t size, uint8_t target); 75 | inline bool nodeSearch(uint64_t &pos, int size, uint8_t target); 76 | inline bool nodeSearch_lowerBound(uint64_t &pos, int size, uint8_t target); 77 | 78 | inline bool binarySearch_lowerBound(uint64_t &pos, uint64_t size, uint8_t target); 79 | inline bool linearSearch_lowerBound(uint64_t &pos, uint64_t size, uint8_t target); 80 | 81 | inline bool nextItemU(uint64_t nodeNum, uint8_t kc, uint8_t &cc); 82 | 83 | inline bool nextLeftU(int keypos, uint64_t pos, FSTIter* iter); 84 | inline bool nextLeft(int keypos, uint64_t pos, FSTIter* iter); 85 | 86 | inline bool nextNodeU(int keypos, uint64_t nodeNum, FSTIter* iter); 87 | inline bool nextNode(int keypos, uint64_t pos, FSTIter* iter); 88 | 89 | int cutoff_level_; 90 | uint64_t nodeCountU_; 91 | uint64_t childCountU_; 92 | 93 | BitmapRankFPoppy* cbitsU_; 94 | BitmapRankFPoppy* tbitsU_; 95 | BitmapRankFPoppy* obitsU_; 96 | uint64_t* valuesU_; 97 | 98 | uint8_t* cbytes_; 99 | BitmapRankPoppy* tbits_; 100 | BitmapSelectPoppy* sbits_; 101 | uint64_t* values_; 102 | 103 | //stats 104 | uint32_t tree_height_; 105 | int32_t last_value_pos_; // negative means in valuesU_ 106 | 107 | uint32_t c_lenU_; 108 | uint32_t o_lenU_; 109 | 110 | uint32_t c_memU_; 111 | uint32_t t_memU_; 112 | uint32_t o_memU_; 113 | uint32_t val_memU_; 114 | 115 | uint64_t c_mem_; 116 | uint32_t t_mem_; 117 | uint32_t s_mem_; 118 | uint64_t val_mem_; 119 | 120 | uint32_t num_t_; 121 | 122 | friend class FSTIter; 123 | }; 124 | 125 | typedef struct { 126 | int32_t keyPos; 127 | int32_t valPos; 128 | bool isO; 129 | } Cursor; 130 | 131 | 132 | class FSTIter { 133 | public: 134 | FSTIter(); 135 | FSTIter(FST* idx); 136 | 137 | void clear (); 138 | 139 | inline void setVU (int keypos, uint64_t nodeNum, uint64_t pos); 140 | inline void setKVU (int keypos, uint64_t nodeNum, uint64_t pos, bool o); 141 | inline void setV (int keypos, uint64_t pos); 142 | inline void setKV (int keypos, uint64_t pos); 143 | 144 | uint64_t value (); 145 | bool operator ++ (int); 146 | bool operator -- (int); 147 | 148 | private: 149 | FST* index; 150 | vector positions; 151 | 152 | uint32_t len; 153 | bool isEnd; 154 | 155 | uint32_t cBoundU; 156 | uint64_t cBound; 157 | int cutoff_level; 158 | uint32_t tree_height; 159 | uint32_t last_value_pos; 160 | 161 | friend class FST; 162 | }; 163 | 164 | -------------------------------------------------------------------------------- /benchmark/workload_email.cpp: -------------------------------------------------------------------------------- 1 | #include "microbench.h" 2 | 3 | #define INIT_LIMIT 25000000 4 | 5 | typedef std::string keytype; 6 | typedef std::less keycomp; 7 | //typedef GenericComparator<31> keycomp; 8 | 9 | static const uint64_t key_type=0; 10 | static const uint64_t value_type=1; // 0 = random pointers, 1 = pointers to keys 11 | 12 | //============================================================== 13 | // GET INSTANCE 14 | //============================================================== 15 | template 16 | Index *getInstance(const int type) { 17 | if (type == 0) 18 | return new ArtIndex_Email(); 19 | else if (type == 1) 20 | return new CArtIndex_Email(); 21 | else if (type == 2) 22 | return new FSTIndex_Email(); 23 | else 24 | return new ArtIndex_Email(); 25 | } 26 | 27 | //============================================================== 28 | // LOAD 29 | //============================================================== 30 | inline void load(int wl, int index_type, std::vector &init_keys, std::vector &keys, std::vector &values, std::vector &ranges, std::vector &ops) { 31 | std::string init_file; 32 | std::string txn_file; 33 | // 0 = c, 1 = e 34 | if (wl == 0) { 35 | init_file = "../../benchmark/workloads/load_email_workloadc"; 36 | txn_file = "../../benchmark/workloads/txn_email_workloadc"; 37 | } 38 | else if (wl == 1) { 39 | init_file = "../../benchmark/workloads/load_email_workloade"; 40 | txn_file = "../../benchmark/workloads/txn_email_workloade"; 41 | } 42 | else { 43 | init_file = "../../benchmark/workloads/load_email_workloadc"; 44 | txn_file = "../../benchmark/workloads/txn_email_workloadc"; 45 | } 46 | 47 | std::ifstream infile_load(init_file); 48 | std::ifstream infile_txn(txn_file); 49 | 50 | std::string op; 51 | keytype key; 52 | int range; 53 | 54 | std::string insert("INSERT"); 55 | std::string read("READ"); 56 | std::string scan("SCAN"); 57 | 58 | int count = 0; 59 | while ((count < INIT_LIMIT) && infile_load.good()) { 60 | infile_load >> op >> key; 61 | if (op.compare(insert) != 0) { 62 | std::cout << "READING LOAD FILE FAIL!\n"; 63 | return; 64 | } 65 | init_keys.push_back(key); 66 | count++; 67 | } 68 | 69 | count = 0; 70 | uint64_t value = 0; 71 | void *base_ptr = malloc(8); 72 | uint64_t base = (uint64_t)(base_ptr); 73 | free(base_ptr); 74 | 75 | if (value_type == 0) { 76 | while (count < INIT_LIMIT) { 77 | value = base + rand(); 78 | values.push_back(value); 79 | count++; 80 | } 81 | } 82 | else { 83 | while (count < INIT_LIMIT) { 84 | values.push_back((uint64_t)(init_keys[count].data())); 85 | count++; 86 | } 87 | } 88 | 89 | count = 0; 90 | while ((count < LIMIT) && infile_txn.good()) { 91 | infile_txn >> op >> key; 92 | if (op.compare(read) == 0) { 93 | ops.push_back(1); 94 | keys.push_back(key); 95 | } 96 | else if (op.compare(scan) == 0) { 97 | infile_txn >> range; 98 | ops.push_back(2); 99 | keys.push_back(key); 100 | ranges.push_back(range); 101 | } 102 | else { 103 | std::cout << "UNRECOGNIZED CMD!\n"; 104 | return; 105 | } 106 | count++; 107 | } 108 | 109 | } 110 | 111 | //============================================================== 112 | // EXEC 113 | //============================================================== 114 | inline void exec(int wl, int index_type, std::vector &init_keys, std::vector &keys, std::vector &values, std::vector &ranges, std::vector &ops) { 115 | Index *idx = getInstance(index_type); 116 | 117 | //WRITE ONLY TEST----------------- 118 | double start_time = get_now(); 119 | idx->load(init_keys, values); 120 | double end_time = get_now(); 121 | double tput = init_keys.size() / (end_time - start_time) / 1000000; //Mops/sec 122 | 123 | std::cout << "insert " << tput << "\n"; 124 | std::cout << "memory " << (idx->getMemory() / 1000000) << "\n"; 125 | 126 | //READ/SCAN TEST---------------- 127 | start_time = get_now(); 128 | int txn_num = 0; 129 | 130 | while ((txn_num < LIMIT) && (txn_num < (int)ops.size())) { 131 | if (ops[txn_num] == 1) { //READ 132 | idx->find(keys[txn_num]); 133 | } 134 | else if (ops[txn_num] == 2) { //SCAN 135 | idx->scan(keys[txn_num], ranges[txn_num] + RANGE_PLUS); 136 | } 137 | else { 138 | std::cout << "UNRECOGNIZED CMD!\n"; 139 | return; 140 | } 141 | txn_num++; 142 | } 143 | 144 | end_time = get_now(); 145 | tput = txn_num / (end_time - start_time) / 1000000; //Mops/sec 146 | 147 | if (wl == 0) 148 | std::cout << "read " << tput << "\n"; 149 | else if (wl == 1) 150 | std::cout << "scan " << tput << "\n"; 151 | else 152 | std::cout << "read " << tput << "\n"; 153 | } 154 | 155 | int main(int argc, char *argv[]) { 156 | if (argc != 3) { 157 | std::cout << "Usage:\n"; 158 | std::cout << "1. workload type: c, e\n"; 159 | std::cout << "2. index type: art, cart, hrt\n"; 160 | return 1; 161 | } 162 | 163 | int wl = 0; 164 | if (strcmp(argv[1], "c") == 0) 165 | wl = 0; 166 | else if (strcmp(argv[1], "e") == 0) 167 | wl = 1; 168 | 169 | int index_type = 0; 170 | if (strcmp(argv[2], "art") == 0) 171 | index_type = 0; 172 | else if (strcmp(argv[2], "cart") == 0) 173 | index_type = 1; 174 | else if (strcmp(argv[2], "hrt") == 0) 175 | index_type = 2; 176 | 177 | std::vector init_keys; 178 | std::vector keys; 179 | std::vector values; 180 | std::vector ranges; 181 | std::vector ops; 182 | 183 | load(wl, index_type, init_keys, keys, values, ranges, ops); 184 | exec(wl, index_type, init_keys, keys, values, ranges, ops); 185 | 186 | return 0; 187 | } 188 | -------------------------------------------------------------------------------- /benchmark/gen_workload.py: -------------------------------------------------------------------------------- 1 | import sys 2 | import os 3 | 4 | class bcolors: 5 | HEADER = '\033[95m' 6 | OKBLUE = '\033[94m' 7 | OKGREEN = '\033[92m' 8 | WARNING = '\033[93m' 9 | FAIL = '\033[91m' 10 | ENDC = '\033[0m' 11 | BOLD = '\033[1m' 12 | UNDERLINE = '\033[4m' 13 | 14 | ##################################################################################### 15 | 16 | def reverseHostName ( email ) : 17 | name, sep, host = email.partition('@') 18 | hostparts = host[:-1].split('.') 19 | r_host = '' 20 | for part in hostparts : 21 | r_host = part + '.' + r_host 22 | return r_host + sep + name 23 | 24 | ##################################################################################### 25 | 26 | if (len(sys.argv) < 3) : 27 | print bcolors.FAIL + 'Usage:' 28 | print 'arg 1, workload type: workloadc, workloade' 29 | print 'arg 2, key type: randint, email' 30 | print 'optional arg 3, email list' 31 | print 'optional arg 4, number of email records' + bcolors.ENDC 32 | sys.exit() 33 | 34 | workload = sys.argv[1] 35 | key_type = sys.argv[2] 36 | 37 | print bcolors.OKGREEN + 'workload type = ' + workload 38 | print 'key type = ' + key_type + bcolors.ENDC 39 | 40 | ycsb_dir = 'YCSB/bin/' 41 | workload_dir = 'workload_spec/' 42 | output_dir='workloads/' 43 | 44 | email_list = '' 45 | email_list_size = 0 46 | 47 | if (len(sys.argv) > 4) : 48 | email_list = sys.argv[3] 49 | email_list_size = int(sys.argv[4]) 50 | 51 | ''' 52 | email_list = 'list.txt' 53 | email_list_size = 27549660 54 | ''' 55 | 56 | if workload != 'workloadc' and workload != 'workloade' : 57 | print bcolors.FAIL + 'Incorrect workload: please pick from workloadc and workloade' + bcolors.ENDC 58 | sys.exit() 59 | 60 | if key_type != 'randint' and key_type != 'email' : 61 | print bcolors.FAIL + 'Incorrect key_type: please pick from randint and email' + bcolors.ENDC 62 | sys.exit() 63 | 64 | if key_type == 'email' and email_list_size == 0 : 65 | print bcolors.FAIL + 'please provide your email list and the total number of email records' + bcolors.ENDC 66 | sys.exit() 67 | 68 | out_ycsb_load = output_dir + 'ycsb_load_' + key_type + '_' + workload 69 | out_ycsb_txn = output_dir + 'ycsb_txn_' + key_type + '_' + workload 70 | out_load_ycsbkey = output_dir + 'load_' + 'ycsbkey' + '_' + workload 71 | out_txn_ycsbkey = output_dir + 'txn_' + 'ycsbkey' + '_' + workload 72 | out_load = output_dir + 'load_' + key_type + '_' + workload 73 | out_txn = output_dir + 'txn_' + key_type + '_' + workload 74 | 75 | cmd_ycsb_load = ycsb_dir + 'ycsb load basic -P ' + workload_dir + workload + ' -s > ' + out_ycsb_load 76 | cmd_ycsb_txn = ycsb_dir + 'ycsb run basic -P ' + workload_dir + workload + ' -s > ' + out_ycsb_txn 77 | 78 | os.system(cmd_ycsb_load) 79 | os.system(cmd_ycsb_txn) 80 | 81 | ##################################################################################### 82 | 83 | f_load = open (out_ycsb_load, 'r') 84 | f_load_out = open (out_load_ycsbkey, 'w') 85 | for line in f_load : 86 | cols = line.split() 87 | if len(cols) > 0 and cols[0] == "INSERT": 88 | f_load_out.write (cols[0] + " " + cols[2][4:] + "\n") 89 | f_load.close() 90 | f_load_out.close() 91 | 92 | f_txn = open (out_ycsb_txn, 'r') 93 | f_txn_out = open (out_txn_ycsbkey, 'w') 94 | for line in f_txn : 95 | cols = line.split() 96 | if (cols[0] == 'SCAN') or (cols[0] == 'INSERT') or (cols[0] == 'READ') or (cols[0] == 'UPDATE'): 97 | startkey = cols[2][4:] 98 | if cols[0] == 'SCAN' : 99 | numkeys = cols[3] 100 | f_txn_out.write (cols[0] + ' ' + startkey + ' ' + numkeys + '\n') 101 | else : 102 | f_txn_out.write (cols[0] + ' ' + startkey + '\n') 103 | f_txn.close() 104 | f_txn_out.close() 105 | 106 | cmd = 'rm -f ' + out_ycsb_load 107 | os.system(cmd) 108 | cmd = 'rm -f ' + out_ycsb_txn 109 | os.system(cmd) 110 | 111 | ##################################################################################### 112 | 113 | if key_type == 'randint' : 114 | f_load = open (out_load_ycsbkey, 'r') 115 | f_load_out = open (out_load, 'w') 116 | for line in f_load : 117 | f_load_out.write (line) 118 | 119 | f_txn = open (out_txn_ycsbkey, 'r') 120 | f_txn_out = open (out_txn, 'w') 121 | for line in f_txn : 122 | f_txn_out.write (line) 123 | 124 | elif key_type == 'email' : 125 | if email_list_size == 0 : 126 | print bcolors.FAIL + 'please provide your email list and the total number of email records' + bcolors.ENDC 127 | sys.exit() 128 | 129 | keymap = {} 130 | f_email = open (email_list, 'r') 131 | emails = f_email.readlines() 132 | 133 | f_load = open (out_load_ycsbkey, 'r') 134 | f_load_out = open (out_load, 'w') 135 | 136 | sample_size = len(f_load.readlines()) 137 | gap = email_list_size / sample_size 138 | 139 | f_load.close() 140 | f_load = open (out_load_ycsbkey, 'r') 141 | count = 0 142 | for line in f_load : 143 | cols = line.split() 144 | email = reverseHostName(emails[count * gap]) 145 | keymap[int(cols[1])] = email 146 | f_load_out.write (cols[0] + ' ' + email + '\n') 147 | count += 1 148 | 149 | count = 0 150 | f_txn = open (out_txn_ycsbkey, 'r') 151 | f_txn_out = open (out_txn, 'w') 152 | for line in f_txn : 153 | cols = line.split() 154 | if cols[0] == 'SCAN' : 155 | f_txn_out.write (cols[0] + ' ' + keymap[int(cols[1])] + ' ' + cols[2] + '\n') 156 | elif cols[0] == 'INSERT' : 157 | email = reverseHostName(emails[count * gap + 1]) 158 | keymap[int(cols[1])] = email 159 | f_txn_out.write (cols[0] + ' ' + email + '\n') 160 | count += 1 161 | else : 162 | f_txn_out.write (cols[0] + ' ' + keymap[int(cols[1])] + '\n') 163 | 164 | f_load.close() 165 | f_load_out.close() 166 | f_txn.close() 167 | f_txn_out.close() 168 | 169 | cmd = 'rm -f ' + out_load_ycsbkey 170 | os.system(cmd) 171 | cmd = 'rm -f ' + out_txn_ycsbkey 172 | os.system(cmd) 173 | -------------------------------------------------------------------------------- /fst/test/TestFST.cpp: -------------------------------------------------------------------------------- 1 | //************************************************ 2 | // FST unit tests 3 | //************************************************ 4 | #include "gtest/gtest.h" 5 | #include 6 | #include 7 | #include 8 | 9 | #include "FST.hpp" 10 | 11 | #define TEST_SIZE 234369 12 | #define RANGE_SIZE 10 13 | 14 | using namespace std; 15 | 16 | const string testFilePath = "../../test/bulkload_sort"; 17 | 18 | class UnitTest : public ::testing::Test { 19 | public: 20 | virtual void SetUp () { } 21 | virtual void TearDown () { } 22 | }; 23 | 24 | inline void printStatFST(FST* index) { 25 | cout << "mem = " << index->mem() << "\n"; 26 | 27 | cout << "cMemU = " << index->cMemU() << "\n"; 28 | cout << "tMemU = " << index->tMemU() << "\n"; 29 | cout << "oMemU = " << index->oMemU() << "\n"; 30 | cout << "keyMemU = " << index->keyMemU() << "\n"; 31 | cout << "valueMemU = " << index->valueMemU() << "\n"; 32 | 33 | cout << "cMem = " << index->cMem() << "\n"; 34 | cout << "tMem = " << index->tMem() << "\n"; 35 | cout << "sMem = " << index->sMem() << "\n"; 36 | cout << "keyMem = " << index->keyMem() << "\n"; 37 | cout << "valueMem = " << index->valueMem() << "\n"; 38 | } 39 | 40 | inline int loadFile (string filePath, vector &keys, vector &values) { 41 | ifstream infile(filePath); 42 | string op; 43 | string key; 44 | uint64_t count = 0; 45 | int longestKeyLen = 0; 46 | while (count < TEST_SIZE && infile.good()) { 47 | infile >> key; //subject to change 48 | keys.push_back(key); 49 | values.push_back(count); 50 | if (key.length() > longestKeyLen) 51 | longestKeyLen = key.length(); 52 | count++; 53 | } 54 | 55 | return longestKeyLen; 56 | } 57 | 58 | inline int loadFile_ptrValue (string filePath, vector &keys, vector &values) { 59 | ifstream infile(filePath); 60 | string op; 61 | string key; 62 | uint64_t count = 0; 63 | int longestKeyLen = 0; 64 | while (count < TEST_SIZE && infile.good()) { 65 | infile >> key; //subject to change 66 | keys.push_back(key); 67 | //values.push_back(count); 68 | values.push_back((uint64_t)(const_cast(keys[count].c_str()))); 69 | if (key.length() > longestKeyLen) 70 | longestKeyLen = key.length(); 71 | count++; 72 | } 73 | 74 | return longestKeyLen; 75 | } 76 | 77 | inline int loadMonoInt (vector &keys) { 78 | for (uint64_t i = 0; i < TEST_SIZE; i++) 79 | keys.push_back(i); 80 | return sizeof(uint64_t); 81 | } 82 | 83 | inline int loadRandInt (vector &keys) { 84 | srand(0); 85 | for (uint64_t i = 0; i < TEST_SIZE; i++) { 86 | uint64_t r = rand(); 87 | keys.push_back(r); 88 | } 89 | sort(keys.begin(), keys.end()); 90 | return sizeof(uint64_t); 91 | } 92 | 93 | 94 | //***************************************************************** 95 | // FST TESTS 96 | //***************************************************************** 97 | 98 | TEST_F(UnitTest, LookupTest) { 99 | vector keys; 100 | vector values; 101 | int longestKeyLen = loadFile(testFilePath, keys, values); 102 | 103 | FST *index = new FST(); 104 | index->load(keys, values, longestKeyLen); 105 | 106 | printStatFST(index); 107 | 108 | uint64_t fetchedValue; 109 | for (int i = 0; i < TEST_SIZE; i++) { 110 | if (i > 0 && keys[i].compare(keys[i-1]) == 0) 111 | continue; 112 | ASSERT_TRUE(index->lookup((uint8_t*)keys[i].c_str(), keys[i].length(), fetchedValue)); 113 | ASSERT_EQ(values[i], fetchedValue); 114 | } 115 | } 116 | 117 | 118 | TEST_F(UnitTest, LookupMonoIntTest) { 119 | vector keys; 120 | int longestKeyLen = loadMonoInt(keys); 121 | 122 | FST *index = new FST(); 123 | index->load(keys, keys); 124 | 125 | printStatFST(index); 126 | 127 | uint64_t fetchedValue; 128 | for (uint64_t i = 0; i < TEST_SIZE; i++) { 129 | ASSERT_TRUE(index->lookup(keys[i], fetchedValue)); 130 | ASSERT_EQ(keys[i], fetchedValue); 131 | } 132 | } 133 | 134 | 135 | TEST_F(UnitTest, LookupRandIntTest) { 136 | vector keys; 137 | int longestKeyLen = loadRandInt(keys); 138 | 139 | FST *index = new FST(); 140 | index->load(keys, keys); 141 | 142 | printStatFST(index); 143 | 144 | random_shuffle(keys.begin(), keys.end()); 145 | 146 | uint64_t fetchedValue; 147 | 148 | for (uint64_t i = 0; i < TEST_SIZE; i++) { 149 | ASSERT_TRUE(index->lookup(keys[i], fetchedValue)); 150 | ASSERT_EQ(keys[i], fetchedValue); 151 | } 152 | } 153 | 154 | 155 | 156 | TEST_F(UnitTest, ScanTest) { 157 | vector keys; 158 | vector values; 159 | int longestKeyLen = loadFile(testFilePath, keys, values); 160 | 161 | FST *index = new FST(); 162 | index->load(keys, values, longestKeyLen); 163 | 164 | printStatFST(index); 165 | 166 | FSTIter iter(index); 167 | for (int i = 0; i < TEST_SIZE - 1; i++) { 168 | if (i > 0 && keys[i].compare(keys[i-1]) == 0) 169 | continue; 170 | ASSERT_TRUE(index->lowerBound((uint8_t*)keys[i].c_str(), keys[i].length(), iter)); 171 | ASSERT_EQ(values[i], iter.value()); 172 | 173 | for (int j = 0; j < RANGE_SIZE; j++) { 174 | if (i+j+1 < TEST_SIZE) { 175 | ASSERT_TRUE(iter++); 176 | ASSERT_EQ(values[i+j+1], iter.value()); 177 | } 178 | else { 179 | ASSERT_FALSE(iter++); 180 | ASSERT_EQ(values[TEST_SIZE-1], iter.value()); 181 | } 182 | } 183 | } 184 | } 185 | 186 | 187 | TEST_F(UnitTest, ScanMonoIntTest) { 188 | vector keys; 189 | int longestKeyLen = loadMonoInt(keys); 190 | 191 | FST *index = new FST(); 192 | index->load(keys, keys); 193 | 194 | printStatFST(index); 195 | 196 | FSTIter iter(index); 197 | for (int i = 0; i < TEST_SIZE - 1; i++) { 198 | uint64_t fetchedValue; 199 | index->lookup(keys[i], fetchedValue); 200 | ASSERT_TRUE(index->lowerBound(keys[i], iter)); 201 | ASSERT_EQ(keys[i], iter.value()); 202 | 203 | for (int j = 0; j < RANGE_SIZE; j++) { 204 | if (i+j+1 < TEST_SIZE) { 205 | ASSERT_TRUE(iter++); 206 | ASSERT_EQ(keys[i+j+1], iter.value()); 207 | } 208 | else { 209 | ASSERT_FALSE(iter++); 210 | ASSERT_EQ(keys[TEST_SIZE-1], iter.value()); 211 | } 212 | } 213 | 214 | } 215 | } 216 | 217 | int main (int argc, char** argv) { 218 | ::testing::InitGoogleTest(&argc, argv); 219 | return RUN_ALL_TESTS(); 220 | } 221 | -------------------------------------------------------------------------------- /benchmark/workload.cpp: -------------------------------------------------------------------------------- 1 | #include "microbench.h" 2 | 3 | #define INIT_LIMIT 50000000 4 | 5 | typedef uint64_t keytype; 6 | typedef std::less keycomp; 7 | 8 | static const uint64_t key_type=0; 9 | static const uint64_t value_type=1; // 0 = random pointers, 1 = pointers to keys 10 | 11 | static const char* GREEN="\033[0;32m"; 12 | static const char* RED="\033[0;31m"; 13 | static const char* NC="\033[0;0m"; 14 | 15 | static Index *idx = NULL; 16 | 17 | //============================================================== 18 | // GET INSTANCE 19 | //============================================================== 20 | template 21 | Index *getInstance(const int type) { 22 | if (type == 0) 23 | return new BtreeIndex(); 24 | else if (type == 1) 25 | return new ArtIndex(); 26 | else if (type == 2) 27 | return new CArtIndex(); 28 | else if (type == 3) 29 | return new FSTIndex(); 30 | else 31 | return new BtreeIndex(); 32 | } 33 | 34 | //============================================================== 35 | // LOAD 36 | //============================================================== 37 | inline void load(int wl, std::vector &init_keys, std::vector &keys, std::vector &values, std::vector &ranges, std::vector &ops) { 38 | std::string init_file; 39 | std::string txn_file; 40 | // 0 = c, 1 = e 41 | if (wl == 0) { 42 | init_file = "../../benchmark/workloads/load_randint_workloadc"; 43 | txn_file = "../../benchmark/workloads/txn_randint_workloadc"; 44 | } 45 | else if (wl == 1) { 46 | init_file = "../../benchmark/workloads/load_randint_workloade"; 47 | txn_file = "../../benchmark/workloads/txn_randint_workloade"; 48 | } 49 | else { 50 | init_file = "../../benchmark/workloads/load_randint_workloadc"; 51 | txn_file = "../../benchmark/workloads/txn_randint_workloadc"; 52 | } 53 | 54 | std::ifstream infile_load(init_file); 55 | std::ifstream infile_txn(txn_file); 56 | 57 | std::string op; 58 | keytype key; 59 | int range; 60 | 61 | std::string insert("INSERT"); 62 | std::string read("READ"); 63 | std::string scan("SCAN"); 64 | 65 | int count = 0; 66 | while ((count < INIT_LIMIT) && infile_load.good()) { 67 | infile_load >> op >> key; 68 | if (op.compare(insert) != 0) { 69 | std::cout << "READING LOAD FILE FAIL!\n"; 70 | return; 71 | } 72 | init_keys.push_back(key); 73 | count++; 74 | } 75 | 76 | count = 0; 77 | uint64_t value = 0; 78 | void *base_ptr = malloc(8); 79 | uint64_t base = (uint64_t)(base_ptr); 80 | free(base_ptr); 81 | 82 | keytype *init_keys_data = init_keys.data(); 83 | 84 | if (value_type == 0) { 85 | while (count < INIT_LIMIT) { 86 | value = base + rand(); 87 | values.push_back(value); 88 | count++; 89 | } 90 | } 91 | else { 92 | while (count < INIT_LIMIT) { 93 | values.push_back(init_keys_data[count]); 94 | count++; 95 | } 96 | } 97 | 98 | count = 0; 99 | while ((count < LIMIT) && infile_txn.good()) { 100 | infile_txn >> op >> key; 101 | if (op.compare(read) == 0) { 102 | ops.push_back(1); 103 | keys.push_back(key); 104 | } 105 | else if (op.compare(scan) == 0) { 106 | infile_txn >> range; 107 | ops.push_back(2); 108 | keys.push_back(key); 109 | ranges.push_back(range); 110 | } 111 | else { 112 | std::cout << "UNRECOGNIZED CMD!\n"; 113 | return; 114 | } 115 | count++; 116 | } 117 | 118 | } 119 | 120 | //============================================================== 121 | // EXEC 122 | //============================================================== 123 | inline void exec_load(int index_type, std::vector &init_keys, std::vector &values) { 124 | idx = getInstance(index_type); 125 | 126 | //WRITE ONLY TEST----------------- 127 | int count = (int)init_keys.size(); 128 | double start_time = get_now(); 129 | if (!idx->load(init_keys, values)) 130 | return; 131 | double end_time = get_now(); 132 | double tput = count / (end_time - start_time) / 1000000; //Mops/sec 133 | 134 | std::cout << "insert " << tput << "\n"; 135 | std::cout << "memory " << (idx->getMemory() / 1000000) << "\n"; 136 | } 137 | 138 | inline void exec_txn(int wl, std::vector &keys, std::vector &values, std::vector &ranges, std::vector &ops) { 139 | //READ/SCAN TEST---------------- 140 | double start_time = get_now(); 141 | int txn_num = 0; 142 | uint64_t s = 0; 143 | 144 | while ((txn_num < LIMIT) && (txn_num < (int)ops.size())) { 145 | if (ops[txn_num] == 1) { //READ 146 | idx->find(keys[txn_num]); 147 | } 148 | else if (ops[txn_num] == 2) { //SCAN 149 | idx->scan(keys[txn_num], ranges[txn_num] + RANGE_PLUS); 150 | } 151 | else { 152 | std::cout << "UNRECOGNIZED CMD!\n"; 153 | return; 154 | } 155 | txn_num++; 156 | } 157 | 158 | double end_time = get_now(); 159 | double tput = txn_num / (end_time - start_time) / 1000000; //Mops/sec 160 | 161 | if (wl == 0) 162 | std::cout << "read " << tput << "\n"; 163 | else if (wl == 1) 164 | std::cout << "scan " << tput << "\n"; 165 | else 166 | std::cout << "read " << tput << "\n"; 167 | } 168 | 169 | int main(int argc, char *argv[]) { 170 | if (argc != 3) { 171 | std::cout << "Usage:\n"; 172 | std::cout << "1. workload type: c, e\n"; 173 | std::cout << "2. index type: btree, art, cart, hrt\n"; 174 | return 1; 175 | } 176 | 177 | int wl = 0; 178 | if (strcmp(argv[1], "c") == 0) 179 | wl = 0; 180 | else if (strcmp(argv[1], "e") == 0) 181 | wl = 1; 182 | 183 | int index_type = 0; 184 | if (strcmp(argv[2], "btree") == 0) 185 | index_type = 0; 186 | else if (strcmp(argv[2], "art") == 0) 187 | index_type = 1; 188 | else if (strcmp(argv[2], "cart") == 0) 189 | index_type = 2; 190 | else if (strcmp(argv[2], "hrt") == 0) 191 | index_type = 3; 192 | 193 | std::vector init_keys; 194 | std::vector keys; 195 | std::vector values; 196 | std::vector ranges; 197 | std::vector ops; 198 | 199 | load(wl, init_keys, keys, values, ranges, ops); 200 | 201 | exec_load(index_type, init_keys, values); 202 | exec_txn(wl, keys, values, ranges, ops); 203 | 204 | return 0; 205 | } 206 | -------------------------------------------------------------------------------- /fst/include/popcount.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | #ifndef _FASTRANK_POPCOUNT_H_ 3 | #define _FASTRANK_POPCOUNT_H_ 4 | 5 | #include 6 | #include 7 | 8 | #define L8 0x0101010101010101ULL // Every lowest 8th bit set: 00000001... 9 | #define G2 0xAAAAAAAAAAAAAAAAULL // Every highest 2nd bit: 101010... 10 | #define G4 0x3333333333333333ULL // 00110011 ... used to group the sum of 4 bits. 11 | #define G8 0x0F0F0F0F0F0F0F0FULL 12 | #define H8 0x8080808080808080ULL 13 | #define L9 0x0040201008040201ULL 14 | #define H9 (L9 << 8) 15 | #define L16 0x0001000100010001ULL 16 | #define H16 0x8000800080008000ULL 17 | 18 | #define ONES_STEP_4 ( 0x1111111111111111ULL ) 19 | #define ONES_STEP_8 ( 0x0101010101010101ULL ) 20 | #define ONES_STEP_9 ( 1ULL << 0 | 1ULL << 9 | 1ULL << 18 | 1ULL << 27 | 1ULL << 36 | 1ULL << 45 | 1ULL << 54 ) 21 | #define ONES_STEP_16 ( 1ULL << 0 | 1ULL << 16 | 1ULL << 32 | 1ULL << 48 ) 22 | #define MSBS_STEP_4 ( 0x8ULL * ONES_STEP_4 ) 23 | #define MSBS_STEP_8 ( 0x80ULL * ONES_STEP_8 ) 24 | #define MSBS_STEP_9 ( 0x100ULL * ONES_STEP_9 ) 25 | #define MSBS_STEP_16 ( 0x8000ULL * ONES_STEP_16 ) 26 | #define INCR_STEP_8 ( 0x80ULL << 56 | 0x40ULL << 48 | 0x20ULL << 40 | 0x10ULL << 32 | 0x8ULL << 24 | 0x4ULL << 16 | 0x2ULL << 8 | 0x1 ) 27 | 28 | #define ONES_STEP_32 ( 0x0000000100000001ULL ) 29 | #define MSBS_STEP_32 ( 0x8000000080000000ULL ) 30 | 31 | #define COMPARE_STEP_8(x,y) ( ( ( ( ( (x) | MSBS_STEP_8 ) - ( (y) & ~MSBS_STEP_8 ) ) ^ (x) ^ ~(y) ) & MSBS_STEP_8 ) >> 7 ) 32 | #define LEQ_STEP_8(x,y) ( ( ( ( ( (y) | MSBS_STEP_8 ) - ( (x) & ~MSBS_STEP_8 ) ) ^ (x) ^ (y) ) & MSBS_STEP_8 ) >> 7 ) 33 | 34 | #define UCOMPARE_STEP_9(x,y) ( ( ( ( ( ( (x) | MSBS_STEP_9 ) - ( (y) & ~MSBS_STEP_9 ) ) | ( x ^ y ) ) ^ ( x | ~y ) ) & MSBS_STEP_9 ) >> 8 ) 35 | #define UCOMPARE_STEP_16(x,y) ( ( ( ( ( ( (x) | MSBS_STEP_16 ) - ( (y) & ~MSBS_STEP_16 ) ) | ( x ^ y ) ) ^ ( x | ~y ) ) & MSBS_STEP_16 ) >> 15 ) 36 | #define ULEQ_STEP_9(x,y) ( ( ( ( ( ( (y) | MSBS_STEP_9 ) - ( (x) & ~MSBS_STEP_9 ) ) | ( x ^ y ) ) ^ ( x & ~y ) ) & MSBS_STEP_9 ) >> 8 ) 37 | #define ULEQ_STEP_16(x,y) ( ( ( ( ( ( (y) | MSBS_STEP_16 ) - ( (x) & ~MSBS_STEP_16 ) ) | ( x ^ y ) ) ^ ( x & ~y ) ) & MSBS_STEP_16 ) >> 15 ) 38 | #define ZCOMPARE_STEP_8(x) ( ( ( x | ( ( x | MSBS_STEP_8 ) - ONES_STEP_8 ) ) & MSBS_STEP_8 ) >> 7 ) 39 | 40 | // Population count of a 64 bit integer in SWAR (SIMD within a register) style 41 | // From Sebastiano Vigna, "Broadword Implementation of Rank/Select Queries" 42 | // http://sux.dsi.unimi.it/paper.pdf p4 43 | // This variant uses multiplication for the last summation instead of 44 | // continuing the shift/mask/addition chain. 45 | inline int suxpopcount(uint64 x) { 46 | // Step 1: 00 - 00 = 0; 01 - 00 = 01; 10 - 01 = 01; 11 - 01 = 10; 47 | x = x - ((x & G2) >> 1); 48 | // step 2: add 2 groups of 2. 49 | x = (x & G4) + ((x >> 2) & G4); 50 | // 2 groups of 4. 51 | x = (x + (x >> 4)) & G8; 52 | // Using a multiply to collect the 8 groups of 8 together. 53 | x = x * L8 >> 56; 54 | return x; 55 | } 56 | 57 | // Default to using the GCC builtin popcount. On architectures 58 | // with -march popcnt, this compiles to a single popcnt instruction. 59 | #ifndef popcount 60 | #define popcount __builtin_popcountll 61 | //#define popcount suxpopcount 62 | #endif 63 | 64 | #define popcountsize 64ULL 65 | #define popcountmask (popcountsize - 1) 66 | 67 | inline uint64 popcountLinear(uint64 *bits, uint64 x, uint64 nbits) { 68 | if (nbits == 0) { return 0; } 69 | uint64 lastword = (nbits - 1) / popcountsize; 70 | uint64 p = 0; 71 | 72 | __builtin_prefetch(bits + x + 7, 0); //huanchen 73 | for (int i = 0; i < lastword; i++) { /* tested; manually unrolling doesn't help, at least in C */ 74 | //__builtin_prefetch(bits + x + i + 3, 0); 75 | p += popcount(bits[x+i]); // note that use binds us to 64 bit popcount impls 76 | } 77 | 78 | // 'nbits' may or may not fall on a multiple of 64 boundary, 79 | // so we may need to zero out the right side of the last word 80 | // (accomplished by shifting it right, since we're just popcounting) 81 | uint64 lastshifted = bits[x+lastword] >> (63 - ((nbits - 1) & popcountmask)); 82 | p += popcount(lastshifted); 83 | return p; 84 | } 85 | 86 | // Return the index of the kth bit set in x 87 | inline int select64_naive(uint64 x, int k) { 88 | int count = -1; 89 | for (int i = 63; i >= 0; i--) { 90 | count++; 91 | if (x & (1ULL << i)) { 92 | k--; 93 | if (k == 0) { 94 | return count; 95 | } 96 | } 97 | } 98 | return -1; 99 | } 100 | 101 | inline int select64_popcount_search(uint64 x, int k) { 102 | int loc = -1; 103 | // if (k > popcount(x)) { return -1; } 104 | 105 | for (int testbits = 32; testbits > 0; testbits >>= 1) { 106 | int lcount = popcount(x >> testbits); 107 | if (k > lcount) { 108 | x &= ((1ULL << testbits)-1); 109 | loc += testbits; 110 | k -= lcount; 111 | } else { 112 | x >>= testbits; 113 | } 114 | } 115 | return loc+k; 116 | } 117 | 118 | inline int select64_broadword(uint64 x, int k) { 119 | uint64 word = x; 120 | int residual = k; 121 | register uint64 byte_sums; 122 | 123 | byte_sums = word - ( ( word & 0xa * ONES_STEP_4 ) >> 1 ); 124 | byte_sums = ( byte_sums & 3 * ONES_STEP_4 ) + ( ( byte_sums >> 2 ) & 3 * ONES_STEP_4 ); 125 | byte_sums = ( byte_sums + ( byte_sums >> 4 ) ) & 0x0f * ONES_STEP_8; 126 | byte_sums *= ONES_STEP_8; 127 | 128 | // Phase 2: compare each byte sum with the residual 129 | const uint64 residual_step_8 = residual * ONES_STEP_8; 130 | const int place = ( LEQ_STEP_8( byte_sums, residual_step_8 ) * ONES_STEP_8 >> 53 ) & ~0x7; 131 | 132 | // Phase 3: Locate the relevant byte and make 8 copies with incremental masks 133 | const int byte_rank = residual - ( ( ( byte_sums << 8 ) >> place ) & 0xFF ); 134 | 135 | const uint64 spread_bits = ( word >> place & 0xFF ) * ONES_STEP_8 & INCR_STEP_8; 136 | const uint64 bit_sums = ZCOMPARE_STEP_8( spread_bits ) * ONES_STEP_8; 137 | 138 | // Compute the inside-byte location and return the sum 139 | const uint64 byte_rank_step_8 = byte_rank * ONES_STEP_8; 140 | 141 | return place + ( LEQ_STEP_8( bit_sums, byte_rank_step_8 ) * ONES_STEP_8 >> 56 ); 142 | } 143 | 144 | inline int select64(uint64 x, int k) { 145 | return select64_popcount_search(x, k); 146 | } 147 | 148 | // x is the starting offset of the 512 bits; 149 | // k is the thing we're selecting for. 150 | inline int select512(uint64 *bits, int x, int k) { 151 | __asm__ __volatile__ ( 152 | "prefetchnta (%0)\n" 153 | : : "r" (&bits[x]) ); 154 | int i = 0; 155 | int pop = popcount(bits[x+i]); 156 | while (k > pop && i < 7) { 157 | k -= pop; 158 | i++; 159 | pop = popcount(bits[x+i]); 160 | } 161 | if (i == 7 && popcount(bits[x+i]) < k) { 162 | return -1; 163 | } 164 | // We're now certain that the bit we want is stored in bv[x+i] 165 | return i*64 + select64(bits[x+i], k); 166 | } 167 | 168 | // brute-force linear select 169 | // x is the starting offset of the bits in bv; 170 | // k is the thing we're selecting for (starting from bv[x]). 171 | // bvlen is the total length of bv 172 | inline uint64 selectLinear(uint64* bits, uint64 length, uint64 x, uint64 k) { 173 | if (k > (length - x) * 64) 174 | return -1; 175 | uint64 i = 0; 176 | uint64 pop = popcount(bits[x+i]); 177 | while (k > pop && i < (length - 1)) { 178 | k -= pop; 179 | i++; 180 | pop = popcount(bits[x+i]); 181 | } 182 | if ((i == length - 1) && (pop < k)) { 183 | return -1; 184 | } 185 | // We're now certain that the bit we want is stored in bits[x+i] 186 | return i*64 + select64(bits[x+i], k); 187 | } 188 | 189 | #endif /* _FASTRANK_POPCOUNT_H_ */ 190 | -------------------------------------------------------------------------------- /fst-serialized/include/popcount.h: -------------------------------------------------------------------------------- 1 | /* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil -*- */ 2 | #ifndef _FASTRANK_POPCOUNT_H_ 3 | #define _FASTRANK_POPCOUNT_H_ 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #define L8 0x0101010101010101ULL // Every lowest 8th bit set: 00000001... 10 | #define G2 0xAAAAAAAAAAAAAAAAULL // Every highest 2nd bit: 101010... 11 | #define G4 0x3333333333333333ULL // 00110011 ... used to group the sum of 4 bits. 12 | #define G8 0x0F0F0F0F0F0F0F0FULL 13 | #define H8 0x8080808080808080ULL 14 | #define L9 0x0040201008040201ULL 15 | #define H9 (L9 << 8) 16 | #define L16 0x0001000100010001ULL 17 | #define H16 0x8000800080008000ULL 18 | 19 | #define ONES_STEP_4 ( 0x1111111111111111ULL ) 20 | #define ONES_STEP_8 ( 0x0101010101010101ULL ) 21 | #define ONES_STEP_9 ( 1ULL << 0 | 1ULL << 9 | 1ULL << 18 | 1ULL << 27 | 1ULL << 36 | 1ULL << 45 | 1ULL << 54 ) 22 | #define ONES_STEP_16 ( 1ULL << 0 | 1ULL << 16 | 1ULL << 32 | 1ULL << 48 ) 23 | #define MSBS_STEP_4 ( 0x8ULL * ONES_STEP_4 ) 24 | #define MSBS_STEP_8 ( 0x80ULL * ONES_STEP_8 ) 25 | #define MSBS_STEP_9 ( 0x100ULL * ONES_STEP_9 ) 26 | #define MSBS_STEP_16 ( 0x8000ULL * ONES_STEP_16 ) 27 | #define INCR_STEP_8 ( 0x80ULL << 56 | 0x40ULL << 48 | 0x20ULL << 40 | 0x10ULL << 32 | 0x8ULL << 24 | 0x4ULL << 16 | 0x2ULL << 8 | 0x1 ) 28 | 29 | #define ONES_STEP_32 ( 0x0000000100000001ULL ) 30 | #define MSBS_STEP_32 ( 0x8000000080000000ULL ) 31 | 32 | #define COMPARE_STEP_8(x,y) ( ( ( ( ( (x) | MSBS_STEP_8 ) - ( (y) & ~MSBS_STEP_8 ) ) ^ (x) ^ ~(y) ) & MSBS_STEP_8 ) >> 7 ) 33 | #define LEQ_STEP_8(x,y) ( ( ( ( ( (y) | MSBS_STEP_8 ) - ( (x) & ~MSBS_STEP_8 ) ) ^ (x) ^ (y) ) & MSBS_STEP_8 ) >> 7 ) 34 | 35 | #define UCOMPARE_STEP_9(x,y) ( ( ( ( ( ( (x) | MSBS_STEP_9 ) - ( (y) & ~MSBS_STEP_9 ) ) | ( x ^ y ) ) ^ ( x | ~y ) ) & MSBS_STEP_9 ) >> 8 ) 36 | #define UCOMPARE_STEP_16(x,y) ( ( ( ( ( ( (x) | MSBS_STEP_16 ) - ( (y) & ~MSBS_STEP_16 ) ) | ( x ^ y ) ) ^ ( x | ~y ) ) & MSBS_STEP_16 ) >> 15 ) 37 | #define ULEQ_STEP_9(x,y) ( ( ( ( ( ( (y) | MSBS_STEP_9 ) - ( (x) & ~MSBS_STEP_9 ) ) | ( x ^ y ) ) ^ ( x & ~y ) ) & MSBS_STEP_9 ) >> 8 ) 38 | #define ULEQ_STEP_16(x,y) ( ( ( ( ( ( (y) | MSBS_STEP_16 ) - ( (x) & ~MSBS_STEP_16 ) ) | ( x ^ y ) ) ^ ( x & ~y ) ) & MSBS_STEP_16 ) >> 15 ) 39 | #define ZCOMPARE_STEP_8(x) ( ( ( x | ( ( x | MSBS_STEP_8 ) - ONES_STEP_8 ) ) & MSBS_STEP_8 ) >> 7 ) 40 | 41 | // Population count of a 64 bit integer in SWAR (SIMD within a register) style 42 | // From Sebastiano Vigna, "Broadword Implementation of Rank/Select Queries" 43 | // http://sux.dsi.unimi.it/paper.pdf p4 44 | // This variant uses multiplication for the last summation instead of 45 | // continuing the shift/mask/addition chain. 46 | inline int suxpopcount(uint64_t x) { 47 | // Step 1: 00 - 00 = 0; 01 - 00 = 01; 10 - 01 = 01; 11 - 01 = 10; 48 | x = x - ((x & G2) >> 1); 49 | // step 2: add 2 groups of 2. 50 | x = (x & G4) + ((x >> 2) & G4); 51 | // 2 groups of 4. 52 | x = (x + (x >> 4)) & G8; 53 | // Using a multiply to collect the 8 groups of 8 together. 54 | x = x * L8 >> 56; 55 | return x; 56 | } 57 | 58 | // Default to using the GCC builtin popcount. On architectures 59 | // with -march popcnt, this compiles to a single popcnt instruction. 60 | #ifndef popcount 61 | #define popcount __builtin_popcountll 62 | //#define popcount suxpopcount 63 | #endif 64 | 65 | #define popcountsize 64ULL 66 | #define popcountmask (popcountsize - 1) 67 | 68 | inline uint64_t popcountLinear(uint64_t *bits, uint64_t x, uint64_t nbits) { 69 | if (nbits == 0) { return 0; } 70 | uint64_t lastword = (nbits - 1) / popcountsize; 71 | uint64_t p = 0; 72 | 73 | __builtin_prefetch(bits + x + 7, 0); //huanchen 74 | for (int i = 0; i < lastword; i++) { /* tested; manually unrolling doesn't help, at least in C */ 75 | //__builtin_prefetch(bits + x + i + 3, 0); 76 | p += popcount(bits[x+i]); // note that use binds us to 64 bit popcount impls 77 | } 78 | 79 | // 'nbits' may or may not fall on a multiple of 64 boundary, 80 | // so we may need to zero out the right side of the last word 81 | // (accomplished by shifting it right, since we're just popcounting) 82 | uint64_t lastshifted = bits[x+lastword] >> (63 - ((nbits - 1) & popcountmask)); 83 | p += popcount(lastshifted); 84 | return p; 85 | } 86 | 87 | // Return the index of the kth bit set in x 88 | inline int select64_naive(uint64_t x, int k) { 89 | int count = -1; 90 | for (int i = 63; i >= 0; i--) { 91 | count++; 92 | if (x & (1ULL << i)) { 93 | k--; 94 | if (k == 0) { 95 | return count; 96 | } 97 | } 98 | } 99 | return -1; 100 | } 101 | 102 | inline int select64_popcount_search(uint64_t x, int k) { 103 | int loc = -1; 104 | // if (k > popcount(x)) { return -1; } 105 | 106 | for (int testbits = 32; testbits > 0; testbits >>= 1) { 107 | int lcount = popcount(x >> testbits); 108 | if (k > lcount) { 109 | x &= ((1ULL << testbits)-1); 110 | loc += testbits; 111 | k -= lcount; 112 | } else { 113 | x >>= testbits; 114 | } 115 | } 116 | return loc+k; 117 | } 118 | 119 | inline int select64_broadword(uint64_t x, int k) { 120 | uint64_t word = x; 121 | int residual = k; 122 | register uint64_t byte_sums; 123 | 124 | byte_sums = word - ( ( word & 0xa * ONES_STEP_4 ) >> 1 ); 125 | byte_sums = ( byte_sums & 3 * ONES_STEP_4 ) + ( ( byte_sums >> 2 ) & 3 * ONES_STEP_4 ); 126 | byte_sums = ( byte_sums + ( byte_sums >> 4 ) ) & 0x0f * ONES_STEP_8; 127 | byte_sums *= ONES_STEP_8; 128 | 129 | // Phase 2: compare each byte sum with the residual 130 | const uint64_t residual_step_8 = residual * ONES_STEP_8; 131 | const int place = ( LEQ_STEP_8( byte_sums, residual_step_8 ) * ONES_STEP_8 >> 53 ) & ~0x7; 132 | 133 | // Phase 3: Locate the relevant byte and make 8 copies with incremental masks 134 | const int byte_rank = residual - ( ( ( byte_sums << 8 ) >> place ) & 0xFF ); 135 | 136 | const uint64_t spread_bits = ( word >> place & 0xFF ) * ONES_STEP_8 & INCR_STEP_8; 137 | const uint64_t bit_sums = ZCOMPARE_STEP_8( spread_bits ) * ONES_STEP_8; 138 | 139 | // Compute the inside-byte location and return the sum 140 | const uint64_t byte_rank_step_8 = byte_rank * ONES_STEP_8; 141 | 142 | return place + ( LEQ_STEP_8( bit_sums, byte_rank_step_8 ) * ONES_STEP_8 >> 56 ); 143 | } 144 | 145 | inline int select64(uint64_t x, int k) { 146 | return select64_popcount_search(x, k); 147 | } 148 | 149 | // x is the starting offset of the 512 bits; 150 | // k is the thing we're selecting for. 151 | inline int select512(uint64_t *bits, int x, int k) { 152 | __asm__ __volatile__ ( 153 | "prefetchnta (%0)\n" 154 | : : "r" (&bits[x]) ); 155 | int i = 0; 156 | int pop = popcount(bits[x+i]); 157 | while (k > pop && i < 7) { 158 | k -= pop; 159 | i++; 160 | pop = popcount(bits[x+i]); 161 | } 162 | if (i == 7 && popcount(bits[x+i]) < k) { 163 | return -1; 164 | } 165 | // We're now certain that the bit we want is stored in bv[x+i] 166 | return i*64 + select64(bits[x+i], k); 167 | } 168 | 169 | // brute-force linear select 170 | // x is the starting offset of the bits in bv; 171 | // k is the thing we're selecting for (starting from bv[x]). 172 | // bvlen is the total length of bv 173 | inline uint64_t selectLinear(uint64_t* bits, uint64_t length, uint64_t x, uint64_t k) { 174 | if (k > (length - x) * 64) 175 | return -1; 176 | uint64_t i = 0; 177 | uint64_t pop = popcount(bits[x+i]); 178 | while (k > pop && i < (length - 1)) { 179 | k -= pop; 180 | i++; 181 | pop = popcount(bits[x+i]); 182 | } 183 | if ((i == length - 1) && (pop < k)) { 184 | return -1; 185 | } 186 | // We're now certain that the bit we want is stored in bits[x+i] 187 | return i*64 + select64(bits[x+i], k); 188 | } 189 | 190 | #endif /* _FASTRANK_POPCOUNT_H_ */ 191 | -------------------------------------------------------------------------------- /benchmark/workload_spec/workload_template: -------------------------------------------------------------------------------- 1 | # Copyright (c) 2012-2016 YCSB contributors. All rights reserved. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); you 4 | # may not use this file except in compliance with the License. You 5 | # may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 12 | # implied. See the License for the specific language governing 13 | # permissions and limitations under the License. See accompanying 14 | # LICENSE file. 15 | 16 | # Yahoo! Cloud System Benchmark 17 | # Workload Template: Default Values 18 | # 19 | # File contains all properties that can be set to define a 20 | # YCSB session. All properties are set to their default 21 | # value if one exists. If not, the property is commented 22 | # out. When a property has a finite number of settings, 23 | # the default is enabled and the alternates are shown in 24 | # comments below it. 25 | # 26 | # Use of most explained through comments in Client.java or 27 | # CoreWorkload.java or on the YCSB wiki page: 28 | # https://github.com/brianfrankcooper/YCSB/wiki/Core-Properties 29 | 30 | # The name of the workload class to use 31 | workload=com.yahoo.ycsb.workloads.CoreWorkload 32 | 33 | # There is no default setting for recordcount but it is 34 | # required to be set. 35 | # The number of records in the table to be inserted in 36 | # the load phase or the number of records already in the 37 | # table before the run phase. 38 | recordcount=1000000 39 | 40 | # There is no default setting for operationcount but it is 41 | # required to be set. 42 | # The number of operations to use during the run phase. 43 | operationcount=3000000 44 | 45 | # The number of insertions to do, if different from recordcount. 46 | # Used with insertstart to grow an existing table. 47 | #insertcount= 48 | 49 | # The offset of the first insertion 50 | insertstart=0 51 | 52 | # The number of fields in a record 53 | fieldcount=10 54 | 55 | # The size of each field (in bytes) 56 | fieldlength=100 57 | 58 | # Should read all fields 59 | readallfields=true 60 | 61 | # Should write all fields on update 62 | writeallfields=false 63 | 64 | # The distribution used to choose the length of a field 65 | fieldlengthdistribution=constant 66 | #fieldlengthdistribution=uniform 67 | #fieldlengthdistribution=zipfian 68 | 69 | # What proportion of operations are reads 70 | readproportion=0.95 71 | 72 | # What proportion of operations are updates 73 | updateproportion=0.05 74 | 75 | # What proportion of operations are inserts 76 | insertproportion=0 77 | 78 | # What proportion of operations read then modify a record 79 | readmodifywriteproportion=0 80 | 81 | # What proportion of operations are scans 82 | scanproportion=0 83 | 84 | # On a single scan, the maximum number of records to access 85 | maxscanlength=1000 86 | 87 | # The distribution used to choose the number of records to access on a scan 88 | scanlengthdistribution=uniform 89 | #scanlengthdistribution=zipfian 90 | 91 | # Should records be inserted in order or pseudo-randomly 92 | insertorder=hashed 93 | #insertorder=ordered 94 | 95 | # The distribution of requests across the keyspace 96 | requestdistribution=zipfian 97 | #requestdistribution=uniform 98 | #requestdistribution=latest 99 | 100 | # Percentage of data items that constitute the hot set 101 | hotspotdatafraction=0.2 102 | 103 | # Percentage of operations that access the hot set 104 | hotspotopnfraction=0.8 105 | 106 | # Maximum execution time in seconds 107 | #maxexecutiontime= 108 | 109 | # The name of the database table to run queries against 110 | table=usertable 111 | 112 | # The column family of fields (required by some databases) 113 | #columnfamily= 114 | 115 | # How the latency measurements are presented 116 | measurementtype=histogram 117 | #measurementtype=timeseries 118 | #measurementtype=raw 119 | # When measurementtype is set to raw, measurements will be output 120 | # as RAW datapoints in the following csv format: 121 | # "operation, timestamp of the measurement, latency in us" 122 | # 123 | # Raw datapoints are collected in-memory while the test is running. Each 124 | # data point consumes about 50 bytes (including java object overhead). 125 | # For a typical run of 1 million to 10 million operations, this should 126 | # fit into memory most of the time. If you plan to do 100s of millions of 127 | # operations per run, consider provisioning a machine with larger RAM when using 128 | # the RAW measurement type, or split the run into multiple runs. 129 | # 130 | # Optionally, you can specify an output file to save raw datapoints. 131 | # Otherwise, raw datapoints will be written to stdout. 132 | # The output file will be appended to if it already exists, otherwise 133 | # a new output file will be created. 134 | #measurement.raw.output_file = /tmp/your_output_file_for_this_run 135 | 136 | # JVM Reporting. 137 | # 138 | # Measure JVM information over time including GC counts, max and min memory 139 | # used, max and min thread counts, max and min system load and others. This 140 | # setting must be enabled in conjunction with the "-s" flag to run the status 141 | # thread. Every "status.interval", the status thread will capture JVM 142 | # statistics and record the results. At the end of the run, max and mins will 143 | # be recorded. 144 | # measurement.trackjvm = false 145 | 146 | # The range of latencies to track in the histogram (milliseconds) 147 | histogram.buckets=1000 148 | 149 | # Granularity for time series (in milliseconds) 150 | timeseries.granularity=1000 151 | 152 | # Latency reporting. 153 | # 154 | # YCSB records latency of failed operations separately from successful ones. 155 | # Latency of all OK operations will be reported under their operation name, 156 | # such as [READ], [UPDATE], etc. 157 | # 158 | # For failed operations: 159 | # By default we don't track latency numbers of specific error status. 160 | # We just report latency of all failed operation under one measurement name 161 | # such as [READ-FAILED]. But optionally, user can configure to have either: 162 | # 1. Record and report latency for each and every error status code by 163 | # setting reportLatencyForEachError to true, or 164 | # 2. Record and report latency for a select set of error status codes by 165 | # providing a CSV list of Status codes via the "latencytrackederrors" 166 | # property. 167 | # reportlatencyforeacherror=false 168 | # latencytrackederrors="" 169 | 170 | # Insertion error retry for the core workload. 171 | # 172 | # By default, the YCSB core workload does not retry any operations. 173 | # However, during the load process, if any insertion fails, the entire 174 | # load process is terminated. 175 | # If a user desires to have more robust behavior during this phase, they can 176 | # enable retry for insertion by setting the following property to a positive 177 | # number. 178 | # core_workload_insertion_retry_limit = 0 179 | # 180 | # the following number controls the interval between retries (in seconds): 181 | # core_workload_insertion_retry_interval = 3 182 | 183 | # Distributed Tracing via Apache HTrace (http://htrace.incubator.apache.org/) 184 | # 185 | # Defaults to blank / no tracing 186 | # Below sends to a local file, sampling at 0.1% 187 | # 188 | # htrace.sampler.classes=ProbabilitySampler 189 | # htrace.sampler.fraction=0.001 190 | # htrace.span.receiver.classes=org.apache.htrace.core.LocalFileSpanReceiver 191 | # htrace.local.file.span.receiver.path=/some/path/to/local/file 192 | # 193 | # To capture all spans, use the AlwaysSampler 194 | # 195 | # htrace.sampler.classes=AlwaysSampler 196 | # 197 | # To send spans to an HTraced receiver, use the below and ensure 198 | # your classpath contains the htrace-htraced jar (i.e. when invoking the ycsb 199 | # command add -cp /path/to/htrace-htraced.jar) 200 | # 201 | # htrace.span.receiver.classes=org.apache.htrace.impl.HTracedSpanReceiver 202 | # htrace.htraced.receiver.address=example.com:9075 203 | # htrace.htraced.error.log.period.ms=10000 204 | -------------------------------------------------------------------------------- /fst-serialized/include/FST.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include "popcount.h" 11 | 12 | using namespace std; 13 | 14 | class FSTIter; 15 | class FST; 16 | 17 | //****************************************************** 18 | // Constants for FST 19 | //****************************************************** 20 | const uint8_t TERM = 36; //$ 21 | const int CUTOFF_RATIO = 64; 22 | 23 | //****************************************************** 24 | //Constants for rank and select 25 | //****************************************************** 26 | const int kCacheLineSize = 64; 27 | const int kWordSize = 64; 28 | 29 | const int kBasicBlockSizeU = 64; 30 | const int kBasicBlockBitsU = 6; 31 | const int kBasicBlockMaskU = kBasicBlockSizeU - 1; 32 | const int kWordCountPerBasicBlockU = kBasicBlockSizeU / kWordSize; 33 | 34 | const int kBasicBlockSize = 512; 35 | const int kBasicBlockBits = 9; 36 | const int kBasicBlockMask = kBasicBlockSize - 1; 37 | const int kWordCountPerBasicBlock = kBasicBlockSize / kWordSize; 38 | 39 | const int kWordBits = 6; 40 | const uint32_t kWordMask = ((uint32_t)1 << kWordBits) - 1; 41 | 42 | const int skip = 64; 43 | const int kSkipBits = 6; 44 | const uint32_t kSkipMask = ((uint32_t)1 << kSkipBits) - 1; 45 | 46 | 47 | //****************************************************** 48 | // Initilization functions for FST 49 | //****************************************************** 50 | FST* load(vector &keys, vector &values, int longestKeyLen); 51 | FST* load(vector &keys, vector &values); 52 | 53 | //helpers 54 | inline bool insertChar_cond(const uint8_t ch, vector &c, vector &t, vector &s, int &pos, int &nc); 55 | inline bool insertChar(const uint8_t ch, bool isTerm, vector &c, vector &t, vector &s, int &pos, int &nc); 56 | 57 | 58 | //****************************************************** 59 | // Fast Succinct Trie Class 60 | //****************************************************** 61 | class FST { 62 | public: 63 | FST(); 64 | FST(int16_t cl, uint16_t th, int8_t fvp, int32_t lvp, uint32_t ncu, uint32_t ccu, 65 | uint32_t cUnb, uint32_t cUmem, uint32_t tUnb, uint32_t tUmem, 66 | uint32_t oUnb, uint32_t oUmem, uint32_t vUm, uint32_t cmem, 67 | uint32_t tnb, uint32_t tmem, uint32_t smem, uint32_t sbbc, uint32_t vm); 68 | virtual ~FST(); 69 | 70 | friend inline bool insertChar_cond(const uint8_t ch, vector &c, vector &t, vector &s, int &pos, int &nc); 71 | friend inline bool insertChar(const uint8_t ch, bool isTerm, vector &c, vector &t, vector &s, int &pos, int &nc); 72 | 73 | friend FST* load(vector &keys, vector &values, int longestKeyLen); 74 | friend FST* load(vector &keys, vector &values); 75 | 76 | //point query 77 | bool lookup(const uint8_t* key, const int keylen, uint64_t &value); 78 | bool lookup(const uint64_t key, uint64_t &value); 79 | 80 | //range query 81 | bool lowerBound(const uint8_t* key, const int keylen, FSTIter &iter); 82 | bool lowerBound(const uint64_t key, FSTIter &iter); 83 | bool upperBound(const uint8_t* key, const int keylen, FSTIter &iter); 84 | bool upperBound(const uint64_t key, FSTIter &iter); 85 | 86 | 87 | //mem stats 88 | //------------------------------------------------- 89 | uint32_t cMemU(); 90 | uint32_t cRankMemU(); 91 | uint32_t tMemU(); 92 | uint32_t tRankMemU(); 93 | uint32_t oMemU(); 94 | uint32_t oRankMemU(); 95 | uint32_t valueMemU(); 96 | 97 | uint64_t cMem(); 98 | uint32_t tMem(); 99 | uint32_t tRankMem(); 100 | uint32_t sMem(); 101 | uint32_t sSelectMem(); 102 | uint64_t valueMem(); 103 | 104 | uint64_t mem(); 105 | 106 | //debug 107 | void printU(); 108 | void print(); 109 | 110 | private: 111 | //bit/byte vector accessors 112 | //------------------------------------------------- 113 | inline uint64_t* cUbits_(); 114 | inline uint32_t* cUrankLUT_(); 115 | inline uint64_t* tUbits_(); 116 | inline uint32_t* tUrankLUT_(); 117 | inline uint64_t* oUbits_(); 118 | inline uint32_t* oUrankLUT_(); 119 | inline uint8_t* cbytes_(); 120 | inline uint64_t* tbits_(); 121 | inline uint32_t* trankLUT_(); 122 | inline uint64_t* sbits_(); 123 | inline uint32_t* sselectLUT_(); 124 | inline uint64_t* valuesU_(); 125 | inline uint64_t* values_(); 126 | 127 | //rank and select init 128 | //------------------------------------------------- 129 | inline void cUinit(uint64_t* bits, uint32_t nbits); 130 | inline void tUinit(uint64_t* bits, uint32_t nbits); 131 | inline void oUinit(uint64_t* bits, uint32_t nbits); 132 | inline void tinit(uint64_t* bits, uint32_t nbits); 133 | inline void sinit(uint64_t* bits, uint32_t nbits); 134 | 135 | //rank and select support 136 | //------------------------------------------------- 137 | inline uint32_t cUrank(uint32_t pos); 138 | inline uint32_t tUrank(uint32_t pos); 139 | inline uint32_t oUrank(uint32_t pos); 140 | inline uint32_t trank(uint32_t pos); 141 | inline uint32_t sselect(uint32_t pos); 142 | 143 | //helpers 144 | //------------------------------------------------- 145 | inline bool isCbitSetU(uint64_t nodeNum, uint8_t kc); 146 | inline bool isTbitSetU(uint64_t nodeNum, uint8_t kc); 147 | inline bool isObitSetU(uint64_t nodeNum); 148 | inline bool isSbitSet(uint64_t pos); 149 | inline bool isTbitSet(uint64_t pos); 150 | inline uint64_t valuePosU(uint64_t nodeNum, uint64_t pos); 151 | inline uint64_t valuePos(uint64_t pos); 152 | 153 | inline uint64_t childNodeNumU(uint64_t pos); 154 | inline uint64_t childNodeNum(uint64_t pos); 155 | inline uint64_t childpos(uint64_t nodeNum); 156 | 157 | inline int nodeSize(uint64_t pos); 158 | inline bool simdSearch(uint64_t &pos, uint64_t size, uint8_t target); 159 | inline bool binarySearch(uint64_t &pos, uint64_t size, uint8_t target); 160 | inline bool linearSearch(uint64_t &pos, uint64_t size, uint8_t target); 161 | inline bool nodeSearch(uint64_t &pos, int size, uint8_t target); 162 | inline bool nodeSearch_lowerBound(uint64_t &pos, int size, uint8_t target); 163 | inline bool nodeSearch_upperBound(uint64_t &pos, int size, uint8_t target); 164 | 165 | inline bool binarySearch_lowerBound(uint64_t &pos, uint64_t size, uint8_t target); 166 | inline bool binarySearch_upperBound(uint64_t &pos, uint64_t size, uint8_t target); 167 | inline bool linearSearch_lowerBound(uint64_t &pos, uint64_t size, uint8_t target); 168 | inline bool linearSearch_upperBound(uint64_t &pos, uint64_t size, uint8_t target); 169 | 170 | inline bool nextItemU(uint64_t nodeNum, uint8_t kc, uint8_t &cc); 171 | inline bool prevItemU(uint64_t nodeNum, uint8_t kc, uint8_t &cc); 172 | 173 | inline bool nextLeftU(int keypos, uint64_t pos, FSTIter* iter); 174 | inline bool nextLeft(int keypos, uint64_t pos, FSTIter* iter); 175 | inline bool nextRightU(int keypos, uint64_t pos, FSTIter* iter); 176 | inline bool nextRight(int keypos, uint64_t pos, FSTIter* iter); 177 | 178 | inline bool nextNodeU(int keypos, uint64_t nodeNum, FSTIter* iter); 179 | inline bool nextNode(int keypos, uint64_t pos, FSTIter* iter); 180 | inline bool prevNodeU(int keypos, uint64_t nodeNum, FSTIter* iter); 181 | inline bool prevNode(int keypos, uint64_t pos, FSTIter* iter); 182 | 183 | //members 184 | //------------------------------------------------- 185 | int16_t cutoff_level_; 186 | uint16_t tree_height_; 187 | int8_t first_value_pos_; // negative means in valuesU_ 188 | int32_t last_value_pos_; // negative means in valuesU_ 189 | uint32_t nodeCountU_; 190 | uint32_t childCountU_; 191 | 192 | //D-Labels 193 | uint32_t cUnbits_; 194 | uint32_t cUpCount_; 195 | uint32_t cUmem_; 196 | 197 | //D-HasChild 198 | uint32_t tUnbits_; 199 | uint32_t tUpCount_; 200 | uint32_t tUmem_; 201 | 202 | //D-IsPrefixKey 203 | uint32_t oUnbits_; 204 | uint32_t oUpCount_; 205 | uint32_t oUmem_; 206 | 207 | //D-values 208 | uint32_t valUmem_; 209 | 210 | //S-Labels 211 | uint32_t cmem_; 212 | 213 | //S-HasChild 214 | uint32_t tnbits_; 215 | uint32_t tpCount_; 216 | uint32_t tmem_; 217 | 218 | //S-LOUDS 219 | uint32_t snbits_; 220 | uint32_t spCount_; 221 | uint32_t smem_; 222 | uint32_t sselectLUTCount_; 223 | 224 | //S-values 225 | uint64_t valmem_; 226 | 227 | //data 228 | char data_[0]; 229 | 230 | friend class FSTIter; 231 | }; 232 | 233 | typedef struct { 234 | int32_t keyPos; 235 | int32_t valPos; 236 | bool isO; 237 | } Cursor; 238 | 239 | 240 | class FSTIter { 241 | public: 242 | FSTIter(); 243 | FSTIter(FST* idx); 244 | 245 | void clear (); 246 | 247 | inline void setVU (int keypos, uint64_t nodeNum, uint64_t pos); 248 | inline void setVU_R (int keypos, uint64_t nodeNum, uint64_t pos); 249 | inline void setKVU (int keypos, uint64_t nodeNum, uint64_t pos, bool o); 250 | inline void setKVU_R (int keypos, uint64_t nodeNum, uint64_t pos, bool o); 251 | inline void setV (int keypos, uint64_t pos); 252 | inline void setV_R (int keypos, uint64_t pos); 253 | inline void setKV (int keypos, uint64_t pos); 254 | inline void setKV_R (int keypos, uint64_t pos); 255 | 256 | string key (); 257 | uint64_t value (); 258 | bool operator ++ (int); 259 | bool operator -- (int); 260 | 261 | private: 262 | FST* index; 263 | vector positions; 264 | 265 | uint32_t len; 266 | bool isBegin; 267 | bool isEnd; 268 | 269 | uint32_t cBoundU; 270 | uint64_t cBound; 271 | int cutoff_level; 272 | uint32_t tree_height; 273 | int8_t first_value_pos; 274 | uint32_t last_value_pos; 275 | 276 | friend class FST; 277 | }; 278 | 279 | -------------------------------------------------------------------------------- /fst-serialized/test/TestFST.cpp: -------------------------------------------------------------------------------- 1 | //************************************************ 2 | // FST unit tests 3 | //************************************************ 4 | #include "gtest/gtest.h" 5 | #include 6 | #include 7 | #include 8 | 9 | #include "FST.hpp" 10 | 11 | #define TEST_SIZE 234369 12 | #define TEST_SIZE_INT 1000000 13 | #define RANGE_SIZE 10 14 | 15 | using namespace std; 16 | 17 | const string testFilePath = "../../test/testStringKeys.txt"; 18 | 19 | class UnitTest : public ::testing::Test { 20 | public: 21 | virtual void SetUp () { } 22 | virtual void TearDown () { } 23 | }; 24 | 25 | inline void printStatFST(FST* index) { 26 | cout << "mem = " << index->mem() << "\n"; 27 | 28 | cout << "cMemU = " << index->cMemU() << "\n"; 29 | cout << "tMemU = " << index->tMemU() << "\n"; 30 | cout << "oMemU = " << index->oMemU() << "\n"; 31 | cout << "valueMemU = " << index->valueMemU() << "\n"; 32 | 33 | cout << "cMem = " << index->cMem() << "\n"; 34 | cout << "tMem = " << index->tMem() << "\n"; 35 | cout << "sMem = " << index->sMem() << "\n"; 36 | cout << "valueMem = " << index->valueMem() << "\n"; 37 | } 38 | 39 | inline int loadFile (string filePath, vector &keys, vector &values) { 40 | ifstream infile(filePath); 41 | string op; 42 | string key; 43 | uint64_t count = 0; 44 | int longestKeyLen = 0; 45 | while (count < TEST_SIZE && infile.good()) { 46 | infile >> key; //subject to change 47 | keys.push_back(key); 48 | values.push_back(count); 49 | if (key.length() > longestKeyLen) 50 | longestKeyLen = key.length(); 51 | count++; 52 | } 53 | 54 | return longestKeyLen; 55 | } 56 | 57 | inline int loadFile_ptrValue (string filePath, vector &keys, vector &values) { 58 | ifstream infile(filePath); 59 | string op; 60 | string key; 61 | uint64_t count = 0; 62 | int longestKeyLen = 0; 63 | while (count < TEST_SIZE && infile.good()) { 64 | infile >> key; //subject to change 65 | keys.push_back(key); 66 | //values.push_back(count); 67 | values.push_back((uint64_t)(const_cast(keys[count].c_str()))); 68 | if (key.length() > longestKeyLen) 69 | longestKeyLen = key.length(); 70 | count++; 71 | } 72 | 73 | return longestKeyLen; 74 | } 75 | 76 | inline int loadMonoInt (vector &keys) { 77 | for (uint64_t i = 0; i < TEST_SIZE_INT; i++) 78 | keys.push_back(i); 79 | return sizeof(uint64_t); 80 | } 81 | 82 | inline int loadMonoSkipInt (vector &keys) { 83 | uint64_t skip = 10; 84 | for (uint64_t i = skip - 1; i < (TEST_SIZE_INT * skip); i += skip) 85 | keys.push_back(i); 86 | return sizeof(uint64_t); 87 | } 88 | 89 | inline int loadRandInt (vector &keys) { 90 | srand(0); 91 | for (uint64_t i = 0; i < TEST_SIZE_INT; i++) { 92 | uint64_t r = rand(); 93 | keys.push_back(r); 94 | } 95 | sort(keys.begin(), keys.end()); 96 | return sizeof(uint64_t); 97 | } 98 | 99 | 100 | //***************************************************************** 101 | // FST TESTS 102 | //***************************************************************** 103 | 104 | TEST_F(UnitTest, LookupTest) { 105 | vector keys; 106 | vector values; 107 | int longestKeyLen = loadFile(testFilePath, keys, values); 108 | 109 | FST *index = load(keys, values, longestKeyLen); 110 | printStatFST(index); 111 | 112 | //index->printU(); 113 | //index->print(); 114 | 115 | uint64_t fetchedValue; 116 | for (int i = 0; i < TEST_SIZE; i++) { 117 | if (i > 0 && keys[i].compare(keys[i-1]) == 0) 118 | continue; 119 | ASSERT_TRUE(index->lookup((uint8_t*)keys[i].c_str(), keys[i].length(), fetchedValue)); 120 | ASSERT_EQ(values[i], fetchedValue); 121 | } 122 | 123 | delete index; 124 | } 125 | 126 | 127 | TEST_F(UnitTest, LookupMonoIntTest) { 128 | vector keys; 129 | int longestKeyLen = loadMonoInt(keys); 130 | 131 | FST *index = load(keys, keys); 132 | 133 | printStatFST(index); 134 | 135 | uint64_t fetchedValue; 136 | for (uint64_t i = 0; i < TEST_SIZE_INT; i++) { 137 | ASSERT_TRUE(index->lookup(keys[i], fetchedValue)); 138 | ASSERT_EQ(keys[i], fetchedValue); 139 | } 140 | } 141 | 142 | 143 | TEST_F(UnitTest, LookupRandIntTest) { 144 | vector keys; 145 | int longestKeyLen = loadRandInt(keys); 146 | 147 | FST *index = load(keys, keys); 148 | 149 | printStatFST(index); 150 | 151 | random_shuffle(keys.begin(), keys.end()); 152 | 153 | uint64_t fetchedValue; 154 | 155 | for (uint64_t i = 0; i < TEST_SIZE_INT; i++) { 156 | ASSERT_TRUE(index->lookup(keys[i], fetchedValue)); 157 | ASSERT_EQ(keys[i], fetchedValue); 158 | } 159 | } 160 | 161 | TEST_F(UnitTest, LowerBoundTest) { 162 | vector keys; 163 | int longestKeyLen = loadMonoSkipInt(keys); 164 | 165 | FST *index = load(keys, keys); 166 | 167 | printStatFST(index); 168 | 169 | string curkey; 170 | char* key_str = new char[8]; 171 | string keyString; 172 | FSTIter iter(index); 173 | for (int i = 0; i < TEST_SIZE_INT - 1; i++) { 174 | ASSERT_TRUE(index->lowerBound(keys[i] - 1, iter)); 175 | curkey = iter.key(); 176 | reinterpret_cast(key_str)[0]=__builtin_bswap64(keys[i]); 177 | keyString = string(key_str, 8); 178 | auto res = mismatch(curkey.begin(), curkey.end(), keyString.begin()); 179 | ASSERT_TRUE(res.first == curkey.end()); 180 | ASSERT_EQ(keys[i], iter.value()); 181 | 182 | for (int j = 0; j < RANGE_SIZE; j++) { 183 | if (i+j+1 < TEST_SIZE_INT) { 184 | ASSERT_TRUE(iter++); 185 | curkey = iter.key(); 186 | reinterpret_cast(key_str)[0]=__builtin_bswap64(keys[i+j+1]); 187 | keyString = string(key_str, 8); 188 | res = mismatch(curkey.begin(), curkey.end(), keyString.begin()); 189 | ASSERT_TRUE(res.first == curkey.end()); 190 | ASSERT_EQ(keys[i+j+1], iter.value()); 191 | } 192 | else { 193 | ASSERT_FALSE(iter++); 194 | curkey = iter.key(); 195 | reinterpret_cast(key_str)[0]=__builtin_bswap64(keys[TEST_SIZE_INT-1]); 196 | keyString = string(key_str, 8); 197 | res = mismatch(curkey.begin(), curkey.end(), keyString.begin()); 198 | ASSERT_TRUE(res.first == curkey.end()); 199 | ASSERT_EQ(keys[TEST_SIZE_INT-1], iter.value()); 200 | } 201 | } 202 | } 203 | } 204 | 205 | TEST_F(UnitTest, UpperBoundTest) { 206 | vector keys; 207 | int longestKeyLen = loadMonoSkipInt(keys); 208 | 209 | FST *index = load(keys, keys); 210 | 211 | printStatFST(index); 212 | 213 | string curkey; 214 | char* key_str = new char[8]; 215 | string keyString; 216 | FSTIter iter(index); 217 | for (int i = 0; i < TEST_SIZE_INT - 1; i++) { 218 | ASSERT_TRUE(index->upperBound(keys[i] + 1, iter)); 219 | curkey = iter.key(); 220 | reinterpret_cast(key_str)[0]=__builtin_bswap64(keys[i]); 221 | keyString = string(key_str, 8); 222 | auto res = mismatch(curkey.begin(), curkey.end(), keyString.begin()); 223 | ASSERT_TRUE(res.first == curkey.end()); 224 | ASSERT_EQ(keys[i], iter.value()); 225 | 226 | for (int j = 0; j < RANGE_SIZE; j++) { 227 | if (i+j+1 < TEST_SIZE_INT) { 228 | ASSERT_TRUE(iter++); 229 | curkey = iter.key(); 230 | reinterpret_cast(key_str)[0]=__builtin_bswap64(keys[i+j+1]); 231 | keyString = string(key_str, 8); 232 | res = mismatch(curkey.begin(), curkey.end(), keyString.begin()); 233 | ASSERT_TRUE(res.first == curkey.end()); 234 | ASSERT_EQ(keys[i+j+1], iter.value()); 235 | } 236 | else { 237 | ASSERT_FALSE(iter++); 238 | curkey = iter.key(); 239 | reinterpret_cast(key_str)[0]=__builtin_bswap64(keys[TEST_SIZE_INT-1]); 240 | keyString = string(key_str, 8); 241 | res = mismatch(curkey.begin(), curkey.end(), keyString.begin()); 242 | ASSERT_TRUE(res.first == curkey.end()); 243 | ASSERT_EQ(keys[TEST_SIZE_INT-1], iter.value()); 244 | } 245 | } 246 | 247 | } 248 | } 249 | 250 | TEST_F(UnitTest, ScanTest) { 251 | vector keys; 252 | vector values; 253 | int longestKeyLen = loadFile(testFilePath, keys, values); 254 | 255 | FST *index = load(keys, values, longestKeyLen); 256 | 257 | printStatFST(index); 258 | 259 | string curkey; 260 | FSTIter iter(index); 261 | for (int i = 0; i < TEST_SIZE - 1; i++) { 262 | if (i > 0 && keys[i].compare(keys[i-1]) == 0) 263 | continue; 264 | ASSERT_TRUE(index->lowerBound((uint8_t*)keys[i].c_str(), keys[i].length(), iter)); 265 | 266 | curkey = iter.key(); 267 | 268 | auto res = mismatch(curkey.begin(), curkey.end(), keys[i].begin()); 269 | ASSERT_TRUE(res.first == curkey.end()); 270 | ASSERT_EQ(values[i], iter.value()); 271 | 272 | for (int j = 0; j < RANGE_SIZE; j++) { 273 | if (i+j+1 < TEST_SIZE) { 274 | ASSERT_TRUE(iter++); 275 | curkey = iter.key(); 276 | res = mismatch(curkey.begin(), curkey.end(), keys[i+j+1].begin()); 277 | ASSERT_TRUE(res.first == curkey.end()); 278 | ASSERT_EQ(values[i+j+1], iter.value()); 279 | } 280 | else { 281 | ASSERT_FALSE(iter++); 282 | curkey = iter.key(); 283 | res = mismatch(curkey.begin(), curkey.end(), keys[TEST_SIZE-1].begin()); 284 | ASSERT_TRUE(res.first == curkey.end()); 285 | ASSERT_EQ(values[TEST_SIZE-1], iter.value()); 286 | } 287 | } 288 | } 289 | } 290 | 291 | 292 | TEST_F(UnitTest, ScanMonoIntTest) { 293 | vector keys; 294 | int longestKeyLen = loadMonoInt(keys); 295 | 296 | FST *index = load(keys, keys); 297 | 298 | printStatFST(index); 299 | 300 | FSTIter iter(index); 301 | for (int i = 0; i < TEST_SIZE_INT - 1; i++) { 302 | ASSERT_TRUE(index->lowerBound(keys[i], iter)); 303 | ASSERT_EQ(keys[i], iter.value()); 304 | 305 | for (int j = 0; j < RANGE_SIZE; j++) { 306 | if (i+j+1 < TEST_SIZE_INT) { 307 | ASSERT_TRUE(iter++); 308 | ASSERT_EQ(keys[i+j+1], iter.value()); 309 | } 310 | else { 311 | ASSERT_FALSE(iter++); 312 | ASSERT_EQ(keys[TEST_SIZE_INT-1], iter.value()); 313 | } 314 | } 315 | } 316 | } 317 | 318 | TEST_F(UnitTest, ScanReverseTest) { 319 | vector keys; 320 | vector values; 321 | int longestKeyLen = loadFile(testFilePath, keys, values); 322 | 323 | FST *index = load(keys, values, longestKeyLen); 324 | 325 | printStatFST(index); 326 | 327 | string curkey; 328 | FSTIter iter(index); 329 | for (int i = 0; i < TEST_SIZE - 1; i++) { 330 | if (i > 0 && keys[i].compare(keys[i-1]) == 0) 331 | continue; 332 | 333 | ASSERT_TRUE(index->upperBound((uint8_t*)keys[i].c_str(), keys[i].length(), iter)); 334 | curkey = iter.key(); 335 | auto res = mismatch(curkey.begin(), curkey.end(), keys[i].begin()); 336 | ASSERT_TRUE(res.first == curkey.end()); 337 | ASSERT_EQ(values[i], iter.value()); 338 | 339 | for (int j = 0; j < RANGE_SIZE; j++) { 340 | if (i-j-1 >= 0) { 341 | ASSERT_TRUE(iter--); 342 | curkey = iter.key(); 343 | res = mismatch(curkey.begin(), curkey.end(), keys[i-j-1].begin()); 344 | ASSERT_TRUE(res.first == curkey.end()); 345 | ASSERT_EQ(values[i-j-1], iter.value()); 346 | } 347 | else { 348 | ASSERT_FALSE(iter--); 349 | curkey = iter.key(); 350 | res = mismatch(curkey.begin(), curkey.end(), keys[0].begin()); 351 | ASSERT_TRUE(res.first == curkey.end()); 352 | ASSERT_EQ(values[0], iter.value()); 353 | } 354 | } 355 | } 356 | } 357 | 358 | TEST_F(UnitTest, ScanMonoIntReverseTest) { 359 | vector keys; 360 | int longestKeyLen = loadMonoInt(keys); 361 | 362 | FST *index = load(keys, keys); 363 | 364 | printStatFST(index); 365 | 366 | FSTIter iter(index); 367 | for (int i = 0; i < TEST_SIZE_INT - 1; i++) { 368 | ASSERT_TRUE(index->upperBound(keys[i], iter)); 369 | ASSERT_EQ(keys[i], iter.value()); 370 | 371 | for (int j = 0; j < RANGE_SIZE; j++) { 372 | if (i-j-1 >= 0) { 373 | ASSERT_TRUE(iter--); 374 | ASSERT_EQ(keys[i-j-1], iter.value()); 375 | } 376 | else { 377 | ASSERT_FALSE(iter--); 378 | ASSERT_EQ(keys[0], iter.value()); 379 | } 380 | } 381 | } 382 | } 383 | 384 | 385 | int main (int argc, char** argv) { 386 | ::testing::InitGoogleTest(&argc, argv); 387 | return RUN_ALL_TESTS(); 388 | } 389 | -------------------------------------------------------------------------------- /benchmark/include/index.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | //#include "indexkey.h" 5 | #include "btree_map.h" 6 | #include "btree.h" 7 | #include "ART.hpp" 8 | #include "FST.hpp" 9 | #include "allocatortracker.h" 10 | 11 | template 12 | class Index 13 | { 14 | public: 15 | virtual bool load(std::vector &keys, std::vector &values) = 0; 16 | 17 | virtual bool insert(KeyType key, uint64_t value) = 0; 18 | 19 | virtual uint64_t find(KeyType key) = 0; 20 | 21 | virtual uint64_t scan(KeyType key, int range) = 0; 22 | 23 | virtual int64_t getMemory() const = 0; 24 | }; 25 | 26 | //*********************************************************** 27 | // B+tree 28 | //*********************************************************** 29 | 30 | template 31 | class BtreeIndex : public Index 32 | { 33 | public: 34 | typedef AllocatorTracker > AllocatorType; 35 | typedef stx::btree_map, AllocatorType> MapType; 36 | 37 | ~BtreeIndex() { 38 | delete idx; 39 | delete alloc; 40 | } 41 | 42 | bool load(std::vector &keys, std::vector &values) { 43 | int count = 0; 44 | while (count < (int)keys.size()) { 45 | insert(keys[count], values[count]); 46 | count++; 47 | } 48 | return true; 49 | } 50 | 51 | bool insert(KeyType key, uint64_t value) { 52 | std::pair retval = idx->insert(key, value); 53 | return retval.second; 54 | } 55 | 56 | uint64_t find(KeyType key) { 57 | iter = idx->find(key); 58 | if (iter == idx->end()) { 59 | std::cout << "READ FAIL\n"; 60 | return 0; 61 | } 62 | return iter->second; 63 | } 64 | 65 | uint64_t scan(KeyType key, int range) { 66 | iter = idx->lower_bound(key); 67 | if (iter == idx->end()) { 68 | std::cout << "SCAN FIRST READ FAIL\n"; 69 | return 0; 70 | } 71 | 72 | uint64_t sum = 0; 73 | sum += iter->second; 74 | for (int i = 0; i < range; i++) { 75 | ++iter; 76 | if (iter == idx->end()) { 77 | break; 78 | } 79 | sum += iter->second; 80 | } 81 | return sum; 82 | } 83 | 84 | int64_t getMemory() const { 85 | return memory; 86 | } 87 | 88 | BtreeIndex() { 89 | memory = 0; 90 | alloc = new AllocatorType(&memory); 91 | idx = new MapType(KeyComparator(), (*alloc)); 92 | } 93 | 94 | MapType *idx; 95 | int64_t memory; 96 | AllocatorType *alloc; 97 | typename MapType::const_iterator iter; 98 | }; 99 | 100 | //*********************************************************** 101 | // ART 102 | //*********************************************************** 103 | 104 | template 105 | class ArtIndex : public Index 106 | { 107 | public: 108 | 109 | ~ArtIndex() { 110 | delete idx; 111 | delete key_bytes; 112 | } 113 | 114 | bool load(std::vector &keys, std::vector &values) { 115 | idx->load(keys, values); 116 | iter = ARTIter(idx); 117 | return true; 118 | } 119 | 120 | bool insert(KeyType key, uint64_t value) { 121 | loadKey(key); 122 | idx->insert(key_bytes, value, 8); 123 | return true; 124 | } 125 | 126 | uint64_t find(KeyType key) { 127 | return idx->lookup(key); 128 | } 129 | 130 | uint64_t scan(KeyType key, int range) { 131 | uint64_t sum = 0; 132 | idx->lower_bound(key, &iter); 133 | sum += iter.value(); 134 | for (int i = 0; i < range - 1; i++) { 135 | if (!iter++) break; 136 | sum += iter.value(); 137 | } 138 | return sum; 139 | } 140 | 141 | int64_t getMemory() const { 142 | return idx->getMemory(); 143 | } 144 | 145 | ArtIndex() { 146 | idx = new ART(8); 147 | key_bytes = new uint8_t [8]; 148 | } 149 | 150 | private: 151 | inline void loadKey(KeyType key) { 152 | reinterpret_cast(key_bytes)[0]=__builtin_bswap64(key); 153 | } 154 | 155 | ART *idx; 156 | uint8_t* key_bytes; 157 | ARTIter iter; 158 | }; 159 | 160 | //*********************************************************** 161 | // ART EMAIL 162 | //*********************************************************** 163 | 164 | template 165 | class ArtIndex_Email : public Index 166 | { 167 | public: 168 | 169 | ~ArtIndex_Email() { 170 | delete idx; 171 | } 172 | 173 | bool load(std::vector &keys, std::vector &values) { 174 | idx->load(keys, values, maxKeyLength); 175 | iter = ARTIter(idx); 176 | return true; 177 | } 178 | 179 | bool insert(KeyType key, uint64_t value) { 180 | idx->insert((uint8_t*)(const_cast(key.c_str())), value, maxKeyLength); 181 | return true; 182 | } 183 | 184 | uint64_t find(KeyType key) { 185 | return idx->lookup((uint8_t*)(const_cast(key.c_str())), key.length(), maxKeyLength); 186 | } 187 | 188 | uint64_t scan(KeyType key, int range) { 189 | uint64_t sum = 0; 190 | idx->lower_bound((uint8_t*)(const_cast(key.c_str())), key.length(), maxKeyLength, &iter); 191 | sum += iter.value(); 192 | for (int i = 0; i < range - 1; i++) { 193 | if (!iter++) break; 194 | sum += iter.value(); 195 | } 196 | return sum; 197 | } 198 | 199 | int64_t getMemory() const { 200 | return idx->getMemory(); 201 | } 202 | 203 | ArtIndex_Email() { 204 | maxKeyLength = 80; 205 | idx = new ART(maxKeyLength); 206 | } 207 | 208 | private: 209 | ART *idx; 210 | ARTIter iter; 211 | unsigned maxKeyLength; 212 | }; 213 | 214 | 215 | //*********************************************************** 216 | // CART 217 | //*********************************************************** 218 | 219 | template 220 | class CArtIndex : public Index 221 | { 222 | public: 223 | 224 | ~CArtIndex() { 225 | delete idx; 226 | delete key_bytes; 227 | } 228 | 229 | bool load(std::vector &keys, std::vector &values) { 230 | idx->load(keys, values); 231 | idx->convert(); 232 | iter = CARTIter(idx); 233 | return true; 234 | } 235 | 236 | bool insert(KeyType key, uint64_t value) { 237 | loadKey(key); 238 | idx->insert(key_bytes, value, 8); 239 | return true; 240 | } 241 | 242 | uint64_t find(KeyType key) { 243 | return idx->lookup(key); 244 | } 245 | 246 | uint64_t scan(KeyType key, int range) { 247 | uint64_t sum = 0; 248 | idx->lower_bound(key, &iter); 249 | sum += iter.value(); 250 | for (int i = 0; i < range - 1; i++) { 251 | if (!iter++) break; 252 | sum += iter.value(); 253 | } 254 | return sum; 255 | } 256 | 257 | int64_t getMemory() const { 258 | return idx->getMemory(); 259 | } 260 | 261 | CArtIndex() { 262 | idx = new CART(8); 263 | key_bytes = new uint8_t [8]; 264 | } 265 | 266 | private: 267 | inline void loadKey(KeyType key) { 268 | reinterpret_cast(key_bytes)[0]=__builtin_bswap64(key); 269 | } 270 | 271 | CART *idx; 272 | uint8_t* key_bytes; 273 | CARTIter iter; 274 | }; 275 | 276 | //*********************************************************** 277 | // CART EMAIL 278 | //*********************************************************** 279 | 280 | template 281 | class CArtIndex_Email : public Index 282 | { 283 | public: 284 | 285 | ~CArtIndex_Email() { 286 | delete idx; 287 | } 288 | 289 | bool load(std::vector &keys, std::vector &values) { 290 | idx->load(keys, values, maxKeyLength); 291 | idx->convert(); 292 | iter = CARTIter(idx); 293 | return true; 294 | } 295 | 296 | bool insert(KeyType key, uint64_t value) { 297 | idx->insert((uint8_t*)(const_cast(key.c_str())), value, maxKeyLength); 298 | return true; 299 | } 300 | 301 | uint64_t find(KeyType key) { 302 | return idx->lookup((uint8_t*)(const_cast(key.c_str())), key.length(), maxKeyLength); 303 | } 304 | 305 | uint64_t scan(KeyType key, int range) { 306 | uint64_t sum = 0; 307 | idx->lower_bound((uint8_t*)(const_cast(key.c_str())), key.length(), maxKeyLength, &iter); 308 | sum += iter.value(); 309 | for (int i = 0; i < range - 1; i++) { 310 | if (!iter++) break; 311 | sum += iter.value(); 312 | } 313 | return sum; 314 | } 315 | 316 | int64_t getMemory() const { 317 | return idx->getMemory(); 318 | } 319 | 320 | CArtIndex_Email() { 321 | maxKeyLength = 80; 322 | idx = new CART(maxKeyLength); 323 | } 324 | 325 | private: 326 | CART *idx; 327 | CARTIter iter; 328 | unsigned maxKeyLength; 329 | }; 330 | 331 | 332 | //*********************************************************** 333 | // FST 334 | //*********************************************************** 335 | 336 | template 337 | class FSTIndex : public Index 338 | { 339 | public: 340 | ~FSTIndex() { 341 | delete idx; 342 | } 343 | 344 | bool load(std::vector &keys, std::vector &values) { 345 | std::sort(keys.begin(), keys.end()); 346 | std::sort(values.begin(), values.end()); 347 | idx->load(keys, values); 348 | iter = FSTIter(idx); 349 | return true; 350 | } 351 | 352 | bool insert(KeyType key, uint64_t value) { 353 | return true; 354 | } 355 | 356 | uint64_t find(KeyType key) { 357 | uint64_t value; 358 | idx->lookup(key, value); 359 | return value; 360 | } 361 | 362 | uint64_t scan(KeyType key, int range) { 363 | uint64_t sum = 0; 364 | idx->lowerBound(key, iter); 365 | sum += iter.value(); 366 | for (int i = 0; i < range - 1; i++) { 367 | if (!iter++) break; 368 | sum += iter.value(); 369 | } 370 | return sum; 371 | } 372 | 373 | int64_t getMemory() const { 374 | /* 375 | std::cout << "cMemU = " << idx->cMemU() << "\n"; 376 | std::cout << "tMemU = " << idx->tMemU() << "\n"; 377 | std::cout << "oMemU = " << idx->oMemU() << "\n"; 378 | std::cout << "keyMemU = " << idx->keyMemU() << "\n"; 379 | std::cout << "valueMemU = " << idx->valueMemU() << "\n\n"; 380 | 381 | std::cout << "cMem = " << idx->cMem() << "\n"; 382 | std::cout << "tMem = " << idx->tMem() << "\n"; 383 | std::cout << "sMem = " << idx->sMem() << "\n"; 384 | std::cout << "keyMem = " << idx->keyMem() << "\n"; 385 | std::cout << "valueMem = " << idx->valueMem() << "\n\n"; 386 | 387 | std::cout << "mem = " << idx->mem() << "\n\n"; 388 | */ 389 | return idx->mem(); 390 | } 391 | 392 | FSTIndex() { 393 | idx = new FST(); 394 | } 395 | 396 | private: 397 | FST *idx; 398 | FSTIter iter; 399 | }; 400 | 401 | 402 | //*********************************************************** 403 | // FST Email 404 | //*********************************************************** 405 | 406 | template 407 | class FSTIndex_Email : public Index 408 | { 409 | public: 410 | ~FSTIndex_Email() { 411 | delete idx; 412 | } 413 | 414 | bool load(std::vector &keys, std::vector &values) { 415 | std::sort(keys.begin(), keys.end()); 416 | std::sort(values.begin(), values.end()); 417 | idx->load(keys, values, 80); 418 | iter = FSTIter(idx); 419 | return true; 420 | } 421 | 422 | bool insert(KeyType key, uint64_t value) { 423 | return true; 424 | } 425 | 426 | uint64_t find(KeyType key) { 427 | uint64_t value; 428 | idx->lookup((const uint8_t*)key.c_str(), key.length(), value); 429 | return value; 430 | } 431 | 432 | uint64_t scan(KeyType key, int range) { 433 | uint64_t sum = 0; 434 | idx->lowerBound((const uint8_t*)key.c_str(), key.length(), iter); 435 | for (int i = 0; i < range - 1; i++) { 436 | if (!iter++) break; 437 | sum += iter.value(); 438 | } 439 | return sum; 440 | } 441 | 442 | int64_t getMemory() const { 443 | /* 444 | std::cout << "cMemU = " << idx->cMemU() << "\n"; 445 | std::cout << "tMemU = " << idx->tMemU() << "\n"; 446 | std::cout << "oMemU = " << idx->oMemU() << "\n"; 447 | std::cout << "keyMemU = " << idx->keyMemU() << "\n"; 448 | std::cout << "valueMemU = " << idx->valueMemU() << "\n\n"; 449 | 450 | std::cout << "cMem = " << idx->cMem() << "\n"; 451 | std::cout << "tMem = " << idx->tMem() << "\n"; 452 | std::cout << "sMem = " << idx->sMem() << "\n"; 453 | std::cout << "keyMem = " << idx->keyMem() << "\n"; 454 | std::cout << "valueMem = " << idx->valueMem() << "\n\n"; 455 | 456 | std::cout << "mem = " << idx->mem() << "\n\n"; 457 | */ 458 | return idx->mem(); 459 | } 460 | 461 | FSTIndex_Email() { 462 | idx = new FST(); 463 | } 464 | 465 | private: 466 | FST *idx; 467 | FSTIter iter; 468 | }; 469 | 470 | 471 | -------------------------------------------------------------------------------- /third-party/art/include/ART.hpp: -------------------------------------------------------------------------------- 1 | /* 2 | Adaptive Radix Tree 3 | Viktor Leis, 2012 4 | leis@in.tum.de 5 | 6 | Modified by Huanchen Zhang, 2016 7 | */ 8 | 9 | #include // malloc, free 10 | #include // memset, memcpy 11 | #include // integer types 12 | #include // x86 SSE intrinsics 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | using namespace std; 21 | 22 | // Constants for the node types 23 | static const int8_t NodeType4=0; 24 | static const int8_t NodeType16=1; 25 | static const int8_t NodeType48=2; 26 | static const int8_t NodeType256=3; 27 | 28 | //static 29 | static const int8_t NodeTypeD=0; 30 | static const int8_t NodeTypeDP=1; 31 | static const int8_t NodeTypeF=2; 32 | static const int8_t NodeTypeFP=3; 33 | static const int8_t NodeTypeU=4; 34 | 35 | // The maximum prefix length for compressed paths stored in the 36 | // header, if the path is longer it is loaded from the database on 37 | // demand 38 | static const unsigned maxPrefixLength=9; 39 | 40 | static const unsigned NodeDItemTHold=227; 41 | 42 | // Shared header of all inner nodes 43 | struct Node { 44 | // length of the compressed path (prefix) 45 | uint32_t prefixLength; 46 | // number of non-null children 47 | uint16_t count; 48 | // node type 49 | int8_t type; 50 | // compressed path (prefix) 51 | uint8_t prefix[maxPrefixLength]; 52 | 53 | Node(int8_t type) : prefixLength(0),count(0),type(type) {} 54 | }; 55 | 56 | // Node with up to 4 children 57 | struct Node4 : Node { 58 | uint8_t key[4]; 59 | Node* child[4]; 60 | 61 | Node4() : Node(NodeType4) { 62 | memset(key,0,sizeof(key)); 63 | memset(child,0,sizeof(child)); 64 | } 65 | }; 66 | 67 | // Node with up to 16 children 68 | struct Node16 : Node { 69 | uint8_t key[16]; 70 | Node* child[16]; 71 | 72 | Node16() : Node(NodeType16) { 73 | memset(key,0,sizeof(key)); 74 | memset(child,0,sizeof(child)); 75 | } 76 | }; 77 | 78 | static const uint8_t emptyMarker=48; 79 | 80 | // Node with up to 48 children 81 | struct Node48 : Node { 82 | uint8_t childIndex[256]; 83 | Node* child[48]; 84 | 85 | Node48() : Node(NodeType48) { 86 | memset(childIndex,emptyMarker,sizeof(childIndex)); 87 | memset(child,0,sizeof(child)); 88 | } 89 | }; 90 | 91 | // Node with up to 256 children 92 | struct Node256 : Node { 93 | Node* child[256]; 94 | 95 | Node256() : Node(NodeType256) { 96 | memset(child,0,sizeof(child)); 97 | } 98 | }; 99 | 100 | typedef struct { 101 | Node* node; 102 | uint16_t cursor; 103 | } NodeCursor; 104 | 105 | //static----------------------------------------------------- 106 | struct NodeStatic { 107 | int8_t type; 108 | 109 | NodeStatic() : type(NodeTypeFP) {} 110 | NodeStatic(int8_t t) : type(t) {} 111 | }; 112 | 113 | struct NodeD : NodeStatic { 114 | uint8_t count; 115 | uint8_t data[0]; 116 | 117 | NodeD() : NodeStatic(NodeTypeD) {} 118 | NodeD(uint8_t size) : NodeStatic(NodeTypeD) { count = size; } 119 | 120 | uint8_t* key() { return data; } 121 | uint8_t* key(unsigned pos) { return &data[pos]; } 122 | NodeStatic** child() { return (NodeStatic**)(&data[count]); } 123 | NodeStatic** child(unsigned pos) { return (NodeStatic**)(&data[count + sizeof(NodeStatic*) * pos]); } 124 | }; 125 | 126 | struct NodeDP : NodeStatic { 127 | uint8_t count; 128 | uint32_t prefixLength; 129 | uint8_t data[0]; 130 | 131 | NodeDP() : NodeStatic(NodeTypeDP) {} 132 | NodeDP(uint8_t size) : NodeStatic(NodeTypeDP) { count = size; } 133 | NodeDP(uint8_t size, uint32_t pl) : NodeStatic(NodeTypeDP) { 134 | count = size; 135 | prefixLength = pl; 136 | } 137 | 138 | uint8_t* prefix() { return data; } 139 | uint8_t* key() { return &data[prefixLength]; } 140 | uint8_t* key(unsigned pos) { return &data[prefixLength + pos]; } 141 | NodeStatic** child() { 142 | return (NodeStatic**)(&data[prefixLength + count]); 143 | } 144 | NodeStatic** child(unsigned pos) { 145 | return (NodeStatic**)(&data[prefixLength + count + sizeof(NodeStatic*) * pos]); 146 | } 147 | }; 148 | 149 | struct NodeF : NodeStatic { 150 | uint16_t count; 151 | NodeStatic* child[256]; 152 | 153 | NodeF() : NodeStatic(NodeTypeF) {} 154 | NodeF(uint16_t size) : NodeStatic(NodeTypeF) { 155 | count = size; 156 | memset(child, 0, sizeof(child)); 157 | } 158 | }; 159 | 160 | struct NodeFP : NodeStatic { 161 | uint16_t count; 162 | uint32_t prefixLength; 163 | uint8_t data[0]; 164 | 165 | NodeFP() : NodeStatic(NodeTypeFP) {} 166 | NodeFP(uint16_t size) : NodeStatic(NodeTypeFP) { count = size; } 167 | NodeFP(uint16_t size, uint32_t pl) : NodeStatic(NodeTypeFP) { 168 | count = size; 169 | prefixLength = pl; 170 | memset(data, 0, prefixLength * sizeof(uint8_t) + 256 * sizeof(NodeStatic*)); 171 | } 172 | 173 | uint8_t* prefix() { return data; } 174 | NodeStatic** child() { return (NodeStatic**)&data[prefixLength]; } 175 | }; 176 | 177 | struct NodeU : NodeStatic { 178 | uint16_t count; 179 | uint32_t prefixLength; 180 | uint8_t prefix[maxPrefixLength]; 181 | uint8_t data[0]; 182 | 183 | NodeU() : NodeStatic(NodeTypeU) {} 184 | NodeU(uint16_t size) : NodeStatic(NodeTypeU) { count = size; } 185 | 186 | uint8_t* key() { return data; } 187 | NodeStatic** child() { return (NodeStatic**)(&data[count]); } 188 | }; 189 | 190 | //static 191 | typedef struct { 192 | NodeStatic* node; 193 | uint16_t cursor; 194 | } NodeStaticCursor; 195 | 196 | //----------------------------------------------------------- 197 | 198 | 199 | class ART; 200 | class CART; 201 | class ARTIter; 202 | class CARTIter; 203 | 204 | 205 | class ART { 206 | 207 | public: 208 | inline Node* makeLeaf(uintptr_t tid); 209 | inline uintptr_t getLeafValue(Node* node); 210 | inline bool isLeaf(Node* node); 211 | inline uint8_t flipSign(uint8_t keyByte); 212 | inline void loadKey(uintptr_t tid,uint8_t key[]); 213 | inline unsigned ctz(uint16_t x); 214 | 215 | //**************************************************************** 216 | 217 | inline Node** findChild(Node* n,uint8_t keyByte); 218 | inline Node* minimum(Node* node); 219 | inline bool leafMatches(Node* leaf,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength); 220 | inline unsigned prefixMismatch(Node* node,uint8_t key[],unsigned depth,unsigned maxKeyLength); 221 | 222 | inline Node* lookup(Node* node,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength); 223 | inline Node* lookupPessimistic(Node* node,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength); 224 | 225 | //**************************************************************** 226 | 227 | inline int CompareToPrefix(Node* node,uint8_t key[],unsigned depth,unsigned maxKeyLength); 228 | inline Node* findChild_recordPath(Node* n, uint8_t keyByte, ARTIter* iter); 229 | inline Node* lower_bound(Node* node,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength, ARTIter *iter); 230 | 231 | //**************************************************************** 232 | 233 | inline unsigned min(unsigned a,unsigned b); 234 | inline void copyPrefix(Node* src,Node* dst); 235 | 236 | inline void insert(Node* node,Node** nodeRef,uint8_t key[],unsigned depth,uintptr_t value,unsigned maxKeyLength); 237 | 238 | inline void insertNode4(Node4* node,Node** nodeRef,uint8_t keyByte,Node* child); 239 | inline void insertNode16(Node16* node,Node** nodeRef,uint8_t keyByte,Node* child); 240 | inline void insertNode48(Node48* node,Node** nodeRef,uint8_t keyByte,Node* child); 241 | inline void insertNode256(Node256* node,Node** nodeRef,uint8_t keyByte,Node* child); 242 | 243 | //**************************************************************** 244 | 245 | inline void erase(Node* node,Node** nodeRef,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength); 246 | 247 | inline void eraseNode4(Node4* node,Node** nodeRef,Node** leafPlace); 248 | inline void eraseNode16(Node16* node,Node** nodeRef,Node** leafPlace); 249 | inline void eraseNode48(Node48* node,Node** nodeRef,uint8_t keyByte); 250 | inline void eraseNode256(Node256* node,Node** nodeRef,uint8_t keyByte); 251 | 252 | 253 | ART(); 254 | ART(unsigned kl); 255 | ART(Node* r); 256 | ART(Node* r, unsigned kl); 257 | 258 | void load(vector &keys, vector &values, unsigned maxKeyLength); 259 | void load(vector &keys, vector &values); 260 | void insert(uint8_t key[], uintptr_t value, unsigned maxKeyLength); 261 | uint64_t lookup(uint8_t key[], unsigned keyLength, unsigned maxKeyLength); 262 | uint64_t lookup(uint64_t key64); 263 | bool lower_bound(uint8_t key[], unsigned keyLength, unsigned maxKeyLength, ARTIter* iter); 264 | bool lower_bound(uint64_t key64, ARTIter* iter); 265 | void erase(uint8_t key[], unsigned keyLength, unsigned maxKeyLength); 266 | uint64_t getMemory(); 267 | 268 | friend class ARTIter; 269 | 270 | private: 271 | Node* root; 272 | unsigned key_length; 273 | 274 | //stats 275 | uint64_t memory; 276 | uint64_t num_items; 277 | uint64_t node4_count; 278 | uint64_t node16_count; 279 | uint64_t node48_count; 280 | uint64_t node256_count; 281 | 282 | // This address is used to communicate that search failed 283 | Node* nullNode; 284 | }; 285 | 286 | class CART { 287 | 288 | public: 289 | inline Node* makeLeaf(uintptr_t tid); 290 | inline uintptr_t getLeafValue(Node* node); 291 | inline uintptr_t getLeafValue(NodeStatic* node); 292 | inline bool isLeaf(Node* node); 293 | inline bool isLeaf(NodeStatic* node); 294 | inline uint8_t flipSign(uint8_t keyByte); 295 | inline void loadKey(uintptr_t tid,uint8_t key[]); 296 | inline unsigned ctz(uint16_t x); 297 | 298 | //**************************************************************** 299 | 300 | inline Node** findChild(Node* n,uint8_t keyByte); 301 | inline NodeStatic** findChild(NodeStatic* n,uint8_t keyByte); 302 | inline Node* minimum(Node* node); 303 | inline NodeStatic* minimum(NodeStatic* node); 304 | inline bool leafMatches(Node* leaf,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength); 305 | inline bool leafMatches(NodeStatic* leaf,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength); 306 | inline unsigned prefixMismatch(Node* node,uint8_t key[],unsigned depth,unsigned maxKeyLength); 307 | inline unsigned prefixMismatch(NodeStatic* node,uint8_t key[],unsigned depth,unsigned maxKeyLength); 308 | 309 | inline NodeStatic* lookup(NodeStatic* node,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength); 310 | 311 | //**************************************************************** 312 | 313 | inline int CompareToPrefix(Node* node,uint8_t key[],unsigned depth,unsigned maxKeyLength); 314 | inline int CompareToPrefix(NodeStatic* n,uint8_t key[],unsigned depth,unsigned maxKeyLength); 315 | inline NodeStatic* findChild_recordPath(NodeStatic* n, uint8_t keyByte, CARTIter* iter); 316 | //inline Node* findChild_recordPath(Node* n, uint8_t keyByte, ARTIter* iter); 317 | inline NodeStatic* lower_bound(NodeStatic* node, uint8_t key[], unsigned keyLength, unsigned depth, unsigned maxKeyLength, CARTIter* iter); 318 | //inline Node* lower_bound(Node* node,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength, ARTIter *iter); 319 | 320 | //**************************************************************** 321 | 322 | inline unsigned min(unsigned a,unsigned b); 323 | inline void copyPrefix(Node* src,Node* dst); 324 | 325 | inline void insert(Node* node,Node** nodeRef,uint8_t key[],unsigned depth,uintptr_t value,unsigned maxKeyLength); 326 | 327 | inline void insertNode4(Node4* node,Node** nodeRef,uint8_t keyByte,Node* child); 328 | inline void insertNode16(Node16* node,Node** nodeRef,uint8_t keyByte,Node* child); 329 | inline void insertNode48(Node48* node,Node** nodeRef,uint8_t keyByte,Node* child); 330 | inline void insertNode256(Node256* node,Node** nodeRef,uint8_t keyByte,Node* child); 331 | 332 | //**************************************************************** 333 | 334 | inline void Node4_to_NodeD(Node4* n, NodeD* n_static); 335 | inline void Node16_to_NodeD(Node16* n, NodeD* n_static); 336 | inline void Node48_to_NodeD(Node48* n, NodeD* n_static); 337 | inline void Node256_to_NodeD(Node256* n, NodeD* n_static); 338 | inline void Node_to_NodeD(Node* n, NodeD* n_static); 339 | 340 | inline void Node4_to_NodeDP(Node4* n, NodeDP* n_static); 341 | inline void Node16_to_NodeDP(Node16* n, NodeDP* n_static); 342 | inline void Node48_to_NodeDP(Node48* n, NodeDP* n_static); 343 | inline void Node256_to_NodeDP(Node256* n, NodeDP* n_static); 344 | inline void Node_to_NodeDP(Node* n, NodeDP* n_static); 345 | 346 | inline void Node4_to_NodeF(Node4* n, NodeF* n_static); 347 | inline void Node16_to_NodeF(Node16* n, NodeF* n_static); 348 | inline void Node48_to_NodeF(Node48* n, NodeF* n_static); 349 | inline void Node256_to_NodeF(Node256* n, NodeF* n_static); 350 | inline void Node_to_NodeF(Node* n, NodeF* n_static); 351 | 352 | inline void Node4_to_NodeFP(Node4* n, NodeFP* n_static); 353 | inline void Node16_to_NodeFP(Node16* n, NodeFP* n_static); 354 | inline void Node48_to_NodeFP(Node48* n, NodeFP* n_static); 355 | inline void Node256_to_NodeFP(Node256* n, NodeFP* n_static); 356 | inline void Node_to_NodeFP(Node* n, NodeFP* n_static); 357 | 358 | inline size_t node_size(NodeStatic* n); 359 | inline NodeStatic* convert_to_static(); 360 | inline NodeStatic* convert_to_static(Node* n); 361 | 362 | 363 | CART(); 364 | CART(unsigned kl); 365 | CART(Node* r); 366 | CART(Node* r, unsigned kl); 367 | 368 | void load(vector &keys, vector &values, unsigned maxKeyLength); 369 | void load(vector &keys, vector &values); 370 | void insert(uint8_t key[], uintptr_t value, unsigned maxKeyLength); 371 | void convert(); 372 | uint64_t lookup(uint8_t key[], unsigned keyLength, unsigned maxKeyLength); 373 | uint64_t lookup(uint64_t key64); 374 | bool lower_bound(uint8_t key[], unsigned keyLength, unsigned maxKeyLength, CARTIter* iter); 375 | bool lower_bound(uint64_t key64, CARTIter* iter); 376 | uint64_t getMemory(); 377 | 378 | friend class CARTIter; 379 | 380 | private: 381 | Node* root; 382 | NodeStatic* static_root; 383 | unsigned key_length; 384 | 385 | //stats 386 | uint64_t memory; 387 | uint64_t static_memory; 388 | uint64_t num_items; 389 | uint64_t node4_count; 390 | uint64_t node16_count; 391 | uint64_t node48_count; 392 | uint64_t node256_count; 393 | uint64_t nodeD_count; 394 | uint64_t nodeDP_count; 395 | uint64_t nodeF_count; 396 | uint64_t nodeFP_count; 397 | 398 | // This address is used to communicate that search failed 399 | Node* nullNode; 400 | NodeStatic* nullNode_static; 401 | }; 402 | 403 | class ARTIter { 404 | public: 405 | inline Node* minimum_recordPath(Node* node); 406 | inline Node* nextSlot(); 407 | inline Node* currentLeaf(); 408 | inline Node* nextLeaf(); 409 | 410 | ARTIter(); 411 | ARTIter(ART* idx); 412 | 413 | uint64_t value(); 414 | bool operator ++ (int); 415 | 416 | friend class ART; 417 | 418 | private: 419 | ART* index; 420 | std::vector node_stack; 421 | uint64_t val; 422 | }; 423 | 424 | class CARTIter { 425 | public: 426 | inline NodeStatic* minimum_recordPath(NodeStatic* node); 427 | inline NodeStatic* nextSlot(); 428 | inline NodeStatic* currentLeaf(); 429 | inline NodeStatic* nextLeaf(); 430 | 431 | CARTIter(); 432 | CARTIter(CART* idx); 433 | 434 | uint64_t value(); 435 | bool operator ++ (int); 436 | 437 | friend class CART; 438 | 439 | private: 440 | CART* index; 441 | std::vector node_stack; 442 | uint64_t val; 443 | }; 444 | 445 | -------------------------------------------------------------------------------- /third-party/art/src/ART.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Adaptive Radix Tree 3 | Viktor Leis, 2012 4 | leis@in.tum.de 5 | 6 | Modified by Huanchen Zhang, 2016 7 | */ 8 | 9 | #include 10 | 11 | #define LOADKEY_INT 1 12 | 13 | //******************************************************************** 14 | // ARTIter 15 | //******************************************************************** 16 | 17 | inline Node* ART::makeLeaf(uintptr_t tid) { 18 | // Create a pseudo-leaf 19 | return reinterpret_cast((tid<<1)|1); 20 | } 21 | 22 | inline uintptr_t ART::getLeafValue(Node* node) { 23 | // The the value stored in the pseudo-leaf 24 | return reinterpret_cast(node)>>1; 25 | } 26 | 27 | inline bool ART::isLeaf(Node* node) { 28 | // Is the node a leaf? 29 | return reinterpret_cast(node)&1; 30 | } 31 | 32 | inline uint8_t ART::flipSign(uint8_t keyByte) { 33 | // Flip the sign bit, enables signed SSE comparison of unsigned values, used by Node16 34 | return keyByte^128; 35 | } 36 | 37 | #ifdef LOADKEY_INT 38 | inline void ART::loadKey(uintptr_t tid,uint8_t key[]) { 39 | // Store the key of the tuple into the key vector 40 | // Implementation is database specific 41 | reinterpret_cast(key)[0]=__builtin_bswap64(tid); 42 | } 43 | #else 44 | inline void ART::loadKey(uintptr_t tid,uint8_t key[]) { 45 | memcpy(reinterpret_cast(key), (const void*)tid, key_length); 46 | } 47 | #endif 48 | 49 | inline unsigned ART::ctz(uint16_t x) { 50 | // Count trailing zeros, only defined for x>0 51 | #ifdef __GNUC__ 52 | return __builtin_ctz(x); 53 | #else 54 | // Adapted from Hacker's Delight 55 | unsigned n=1; 56 | if ((x&0xFF)==0) {n+=8; x=x>>8;} 57 | if ((x&0x0F)==0) {n+=4; x=x>>4;} 58 | if ((x&0x03)==0) {n+=2; x=x>>2;} 59 | return n-(x&1); 60 | #endif 61 | } 62 | 63 | inline Node** ART::findChild(Node* n,uint8_t keyByte) { 64 | // Find the next child for the keyByte 65 | switch (n->type) { 66 | case NodeType4: { 67 | Node4* node=static_cast(n); 68 | for (unsigned i=0;icount;i++) 69 | if (node->key[i]==keyByte) 70 | return &node->child[i]; 71 | return &nullNode; 72 | } 73 | case NodeType16: { 74 | Node16* node=static_cast(n); 75 | __m128i cmp=_mm_cmpeq_epi8(_mm_set1_epi8(flipSign(keyByte)),_mm_loadu_si128(reinterpret_cast<__m128i*>(node->key))); 76 | unsigned bitfield=_mm_movemask_epi8(cmp)&((1<count)-1); 77 | if (bitfield) 78 | return &node->child[ctz(bitfield)]; else 79 | return &nullNode; 80 | } 81 | case NodeType48: { 82 | Node48* node=static_cast(n); 83 | if (node->childIndex[keyByte]!=emptyMarker) 84 | return &node->child[node->childIndex[keyByte]]; else 85 | return &nullNode; 86 | } 87 | case NodeType256: { 88 | Node256* node=static_cast(n); 89 | return &(node->child[keyByte]); 90 | } 91 | } 92 | throw; // Unreachable 93 | } 94 | 95 | inline Node* ART::minimum(Node* node) { 96 | // Find the leaf with smallest key 97 | if (!node) 98 | return NULL; 99 | 100 | if (isLeaf(node)) 101 | return node; 102 | 103 | switch (node->type) { 104 | case NodeType4: { 105 | Node4* n=static_cast(node); 106 | return minimum(n->child[0]); 107 | } 108 | case NodeType16: { 109 | Node16* n=static_cast(node); 110 | return minimum(n->child[0]); 111 | } 112 | case NodeType48: { 113 | Node48* n=static_cast(node); 114 | unsigned pos=0; 115 | while (n->childIndex[pos]==emptyMarker) 116 | pos++; 117 | return minimum(n->child[n->childIndex[pos]]); 118 | } 119 | case NodeType256: { 120 | Node256* n=static_cast(node); 121 | unsigned pos=0; 122 | while (!n->child[pos]) 123 | pos++; 124 | return minimum(n->child[pos]); 125 | } 126 | } 127 | throw; // Unreachable 128 | } 129 | 130 | inline bool ART::leafMatches(Node* leaf,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength) { 131 | // Check if the key of the leaf is equal to the searched key 132 | if (depth!=keyLength) { 133 | uint8_t leafKey[maxKeyLength]; 134 | loadKey(getLeafValue(leaf),leafKey); 135 | for (unsigned i=depth;iprefixLength>maxPrefixLength) { 146 | for (pos=0;posprefix[pos]) 148 | return pos; 149 | uint8_t minKey[maxKeyLength]; 150 | loadKey(getLeafValue(minimum(node)),minKey); 151 | for (;posprefixLength;pos++) 152 | if (key[depth+pos]!=minKey[depth+pos]) 153 | return pos; 154 | } else { 155 | for (pos=0;posprefixLength;pos++) 156 | if (key[depth+pos]!=node->prefix[pos]) 157 | return pos; 158 | } 159 | return pos; 160 | } 161 | 162 | inline Node* ART::lookup(Node* node,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength) { 163 | // Find the node with a matching key, optimistic version 164 | 165 | bool skippedPrefix=false; // Did we optimistically skip some prefix without checking it? 166 | 167 | while (node!=NULL) { 168 | if (isLeaf(node)) { 169 | if (!skippedPrefix&&depth==keyLength) // No check required 170 | return node; 171 | 172 | if (depth!=keyLength) { 173 | // Check leaf 174 | uint8_t leafKey[maxKeyLength]; 175 | loadKey(getLeafValue(node),leafKey); 176 | for (unsigned i=(skippedPrefix?0:depth);iprefixLength) { 184 | if (node->prefixLengthprefixLength;pos++) 186 | if (key[depth+pos]!=node->prefix[pos]) 187 | return NULL; 188 | } else 189 | skippedPrefix=true; 190 | depth+=node->prefixLength; 191 | } 192 | 193 | node=*findChild(node,key[depth]); 194 | depth++; 195 | } 196 | 197 | return NULL; 198 | } 199 | 200 | inline Node* ART::lookupPessimistic(Node* node,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength) { 201 | // Find the node with a matching key, alternative pessimistic version 202 | 203 | while (node!=NULL) { 204 | if (isLeaf(node)) { 205 | if (leafMatches(node,key,keyLength,depth,maxKeyLength)) 206 | return node; 207 | return NULL; 208 | } 209 | 210 | if (prefixMismatch(node,key,depth,maxKeyLength)!=node->prefixLength) 211 | return NULL; else 212 | depth+=node->prefixLength; 213 | 214 | node=*findChild(node,key[depth]); 215 | depth++; 216 | } 217 | 218 | return NULL; 219 | } 220 | 221 | inline unsigned ART::min(unsigned a,unsigned b) { 222 | // Helper function 223 | return (aprefixLength=src->prefixLength; 229 | memcpy(dst->prefix,src->prefix,min(src->prefixLength,maxPrefixLength)); 230 | } 231 | 232 | inline void ART::insert(Node* node,Node** nodeRef,uint8_t key[],unsigned depth,uintptr_t value,unsigned maxKeyLength) { 233 | // Insert the leaf value into the tree 234 | 235 | if (node==NULL) { 236 | *nodeRef=makeLeaf(value); 237 | return; 238 | } 239 | 240 | if (isLeaf(node)) { 241 | // Replace leaf with Node4 and store both leaves in it 242 | uint8_t existingKey[maxKeyLength]; 243 | loadKey(getLeafValue(node),existingKey); 244 | unsigned newPrefixLength=0; 245 | while (existingKey[depth+newPrefixLength]==key[depth+newPrefixLength]) 246 | newPrefixLength++; 247 | 248 | Node4* newNode=new Node4(); 249 | memory += sizeof(Node4); //h 250 | node4_count++; //h 251 | newNode->prefixLength=newPrefixLength; 252 | memcpy(newNode->prefix,key+depth,min(newPrefixLength,maxPrefixLength)); 253 | *nodeRef=newNode; 254 | 255 | insertNode4(newNode,nodeRef,existingKey[depth+newPrefixLength],node); 256 | insertNode4(newNode,nodeRef,key[depth+newPrefixLength],makeLeaf(value)); 257 | num_items++; //h 258 | return; 259 | } 260 | 261 | // Handle prefix of inner node 262 | if (node->prefixLength) { 263 | unsigned mismatchPos=prefixMismatch(node,key,depth,maxKeyLength); 264 | if (mismatchPos!=node->prefixLength) { 265 | // Prefix differs, create new node 266 | Node4* newNode=new Node4(); 267 | memory += sizeof(Node4); //h 268 | node4_count++; //h 269 | *nodeRef=newNode; 270 | newNode->prefixLength=mismatchPos; 271 | memcpy(newNode->prefix,node->prefix,min(mismatchPos,maxPrefixLength)); 272 | // Break up prefix 273 | if (node->prefixLengthprefix[mismatchPos],node); 275 | node->prefixLength-=(mismatchPos+1); 276 | memmove(node->prefix,node->prefix+mismatchPos+1,min(node->prefixLength,maxPrefixLength)); 277 | } else { 278 | node->prefixLength-=(mismatchPos+1); 279 | uint8_t minKey[maxKeyLength]; 280 | loadKey(getLeafValue(minimum(node)),minKey); 281 | insertNode4(newNode,nodeRef,minKey[depth+mismatchPos],node); 282 | memmove(node->prefix,minKey+depth+mismatchPos+1,min(node->prefixLength,maxPrefixLength)); 283 | } 284 | insertNode4(newNode,nodeRef,key[depth+mismatchPos],makeLeaf(value)); 285 | num_items++; //h 286 | return; 287 | } 288 | depth+=node->prefixLength; 289 | } 290 | 291 | // Recurse 292 | Node** child=findChild(node,key[depth]); 293 | if (*child) { 294 | insert(*child,child,key,depth+1,value,maxKeyLength); 295 | return; 296 | } 297 | 298 | // Insert leaf into inner node 299 | Node* newNode=makeLeaf(value); 300 | switch (node->type) { 301 | case NodeType4: insertNode4(static_cast(node),nodeRef,key[depth],newNode); break; 302 | case NodeType16: insertNode16(static_cast(node),nodeRef,key[depth],newNode); break; 303 | case NodeType48: insertNode48(static_cast(node),nodeRef,key[depth],newNode); break; 304 | case NodeType256: insertNode256(static_cast(node),nodeRef,key[depth],newNode); break; 305 | } 306 | num_items++; //h 307 | } 308 | 309 | inline void ART::insertNode4(Node4* node,Node** nodeRef,uint8_t keyByte,Node* child) { 310 | // Insert leaf into inner node 311 | if (node->count<4) { 312 | // Insert element 313 | unsigned pos; 314 | for (pos=0;(poscount)&&(node->key[pos]key+pos+1,node->key+pos,node->count-pos); 316 | memmove(node->child+pos+1,node->child+pos,(node->count-pos)*sizeof(uintptr_t)); 317 | node->key[pos]=keyByte; 318 | node->child[pos]=child; 319 | node->count++; 320 | } else { 321 | // Grow to Node16 322 | Node16* newNode=new Node16(); 323 | memory += sizeof(Node16); //h 324 | node16_count++; //h 325 | *nodeRef=newNode; 326 | newNode->count=4; 327 | copyPrefix(node,newNode); 328 | for (unsigned i=0;i<4;i++) 329 | newNode->key[i]=flipSign(node->key[i]); 330 | memcpy(newNode->child,node->child,node->count*sizeof(uintptr_t)); 331 | delete node; 332 | memory -= sizeof(Node4); //h 333 | node4_count--; //h 334 | return insertNode16(newNode,nodeRef,keyByte,child); 335 | } 336 | } 337 | 338 | inline void ART::insertNode16(Node16* node,Node** nodeRef,uint8_t keyByte,Node* child) { 339 | // Insert leaf into inner node 340 | if (node->count<16) { 341 | // Insert element 342 | uint8_t keyByteFlipped=flipSign(keyByte); 343 | __m128i cmp=_mm_cmplt_epi8(_mm_set1_epi8(keyByteFlipped),_mm_loadu_si128(reinterpret_cast<__m128i*>(node->key))); 344 | uint16_t bitfield=_mm_movemask_epi8(cmp)&(0xFFFF>>(16-node->count)); 345 | unsigned pos=bitfield?ctz(bitfield):node->count; 346 | memmove(node->key+pos+1,node->key+pos,node->count-pos); 347 | memmove(node->child+pos+1,node->child+pos,(node->count-pos)*sizeof(uintptr_t)); 348 | node->key[pos]=keyByteFlipped; 349 | node->child[pos]=child; 350 | node->count++; 351 | } else { 352 | // Grow to Node48 353 | Node48* newNode=new Node48(); 354 | memory += sizeof(Node48); //h 355 | node48_count++; //h 356 | *nodeRef=newNode; 357 | memcpy(newNode->child,node->child,node->count*sizeof(uintptr_t)); 358 | for (unsigned i=0;icount;i++) 359 | newNode->childIndex[flipSign(node->key[i])]=i; 360 | copyPrefix(node,newNode); 361 | newNode->count=node->count; 362 | delete node; 363 | memory -= sizeof(Node16); //h 364 | node16_count--; //h 365 | return insertNode48(newNode,nodeRef,keyByte,child); 366 | } 367 | } 368 | 369 | inline void ART::insertNode48(Node48* node,Node** nodeRef,uint8_t keyByte,Node* child) { 370 | // Insert leaf into inner node 371 | if (node->count<48) { 372 | // Insert element 373 | unsigned pos=node->count; 374 | if (node->child[pos]) 375 | for (pos=0;node->child[pos]!=NULL;pos++); 376 | node->child[pos]=child; 377 | node->childIndex[keyByte]=pos; 378 | node->count++; 379 | } else { 380 | // Grow to Node256 381 | Node256* newNode=new Node256(); 382 | memory += sizeof(Node256); //h 383 | node256_count++; //h 384 | for (unsigned i=0;i<256;i++) 385 | if (node->childIndex[i]!=48) 386 | newNode->child[i]=node->child[node->childIndex[i]]; 387 | newNode->count=node->count; 388 | copyPrefix(node,newNode); 389 | *nodeRef=newNode; 390 | delete node; 391 | memory -= sizeof(Node48); //h 392 | node48_count--; //h 393 | return insertNode256(newNode,nodeRef,keyByte,child); 394 | } 395 | } 396 | 397 | inline void ART::insertNode256(Node256* node,Node** nodeRef,uint8_t keyByte,Node* child) { 398 | // Insert leaf into inner node 399 | node->count++; 400 | node->child[keyByte]=child; 401 | } 402 | 403 | 404 | inline void ART::erase(Node* node,Node** nodeRef,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength) { 405 | // Delete a leaf from a tree 406 | 407 | if (!node) 408 | return; 409 | 410 | if (isLeaf(node)) { 411 | // Make sure we have the right leaf 412 | if (leafMatches(node,key,keyLength,depth,maxKeyLength)) 413 | *nodeRef=NULL; 414 | return; 415 | } 416 | 417 | // Handle prefix 418 | if (node->prefixLength) { 419 | if (prefixMismatch(node,key,depth,maxKeyLength)!=node->prefixLength) 420 | return; 421 | depth+=node->prefixLength; 422 | } 423 | 424 | Node** child=findChild(node,key[depth]); 425 | if (isLeaf(*child)&&leafMatches(*child,key,keyLength,depth,maxKeyLength)) { 426 | // Leaf found, delete it in inner node 427 | switch (node->type) { 428 | case NodeType4: eraseNode4(static_cast(node),nodeRef,child); break; 429 | case NodeType16: eraseNode16(static_cast(node),nodeRef,child); break; 430 | case NodeType48: eraseNode48(static_cast(node),nodeRef,key[depth]); break; 431 | case NodeType256: eraseNode256(static_cast(node),nodeRef,key[depth]); break; 432 | } 433 | } else { 434 | //Recurse 435 | erase(*child,child,key,keyLength,depth+1,maxKeyLength); 436 | } 437 | } 438 | 439 | inline void ART::eraseNode4(Node4* node,Node** nodeRef,Node** leafPlace) { 440 | // Delete leaf from inner node 441 | unsigned pos=leafPlace-node->child; 442 | memmove(node->key+pos,node->key+pos+1,node->count-pos-1); 443 | memmove(node->child+pos,node->child+pos+1,(node->count-pos-1)*sizeof(uintptr_t)); 444 | node->count--; 445 | 446 | if (node->count==1) { 447 | // Get rid of one-way node 448 | Node* child=node->child[0]; 449 | if (!isLeaf(child)) { 450 | // Concantenate prefixes 451 | unsigned l1=node->prefixLength; 452 | if (l1prefix[l1]=node->key[0]; 454 | l1++; 455 | } 456 | if (l1prefixLength,maxPrefixLength-l1); 458 | memcpy(node->prefix+l1,child->prefix,l2); 459 | l1+=l2; 460 | } 461 | // Store concantenated prefix 462 | memcpy(child->prefix,node->prefix,min(l1,maxPrefixLength)); 463 | child->prefixLength+=node->prefixLength+1; 464 | } 465 | *nodeRef=child; 466 | delete node; 467 | memory -= sizeof(Node4); //h 468 | node4_count--; //h 469 | } 470 | } 471 | 472 | inline void ART::eraseNode16(Node16* node,Node** nodeRef,Node** leafPlace) { 473 | // Delete leaf from inner node 474 | unsigned pos=leafPlace-node->child; 475 | memmove(node->key+pos,node->key+pos+1,node->count-pos-1); 476 | memmove(node->child+pos,node->child+pos+1,(node->count-pos-1)*sizeof(uintptr_t)); 477 | node->count--; 478 | 479 | if (node->count==3) { 480 | // Shrink to Node4 481 | Node4* newNode=new Node4(); 482 | memory += sizeof(Node4); //h 483 | node4_count++; //h 484 | newNode->count=node->count; 485 | copyPrefix(node,newNode); 486 | for (unsigned i=0;i<4;i++) 487 | newNode->key[i]=flipSign(node->key[i]); 488 | memcpy(newNode->child,node->child,sizeof(uintptr_t)*4); 489 | *nodeRef=newNode; 490 | delete node; 491 | memory -= sizeof(Node16); //h 492 | node16_count--; //h 493 | } 494 | } 495 | 496 | inline void ART::eraseNode48(Node48* node,Node** nodeRef,uint8_t keyByte) { 497 | // Delete leaf from inner node 498 | node->child[node->childIndex[keyByte]]=NULL; 499 | node->childIndex[keyByte]=emptyMarker; 500 | node->count--; 501 | 502 | if (node->count==12) { 503 | // Shrink to Node16 504 | Node16 *newNode=new Node16(); 505 | memory += sizeof(Node16); //h 506 | node16_count++; //h 507 | *nodeRef=newNode; 508 | copyPrefix(node,newNode); 509 | for (unsigned b=0;b<256;b++) { 510 | if (node->childIndex[b]!=emptyMarker) { 511 | newNode->key[newNode->count]=flipSign(b); 512 | newNode->child[newNode->count]=node->child[node->childIndex[b]]; 513 | newNode->count++; 514 | } 515 | } 516 | delete node; 517 | memory -= sizeof(Node48); //h 518 | node48_count--; //h 519 | } 520 | } 521 | 522 | inline void ART::eraseNode256(Node256* node,Node** nodeRef,uint8_t keyByte) { 523 | // Delete leaf from inner node 524 | node->child[keyByte]=NULL; 525 | node->count--; 526 | 527 | if (node->count==37) { 528 | // Shrink to Node48 529 | Node48 *newNode=new Node48(); 530 | memory += sizeof(Node48); //h 531 | node48_count++; //h 532 | *nodeRef=newNode; 533 | copyPrefix(node,newNode); 534 | for (unsigned b=0;b<256;b++) { 535 | if (node->child[b]) { 536 | newNode->childIndex[b]=newNode->count; 537 | newNode->child[newNode->count]=node->child[b]; 538 | newNode->count++; 539 | } 540 | } 541 | delete node; 542 | memory -= sizeof(Node256); //h 543 | node256_count--; //h 544 | } 545 | } 546 | 547 | inline int ART::CompareToPrefix(Node* node,uint8_t key[],unsigned depth,unsigned maxKeyLength) { 548 | unsigned pos; 549 | if (node->prefixLength>maxPrefixLength) { 550 | for (pos=0;posprefix[pos]) { 552 | if (key[depth+pos]>node->prefix[pos]) 553 | return 1; 554 | else 555 | return -1; 556 | } 557 | } 558 | uint8_t minKey[maxKeyLength]; 559 | loadKey(getLeafValue(minimum(node)),minKey); 560 | for (;posprefixLength;pos++) { 561 | if (key[depth+pos]!=minKey[depth+pos]) { 562 | if (key[depth+pos]>minKey[depth+pos]) 563 | return 1; 564 | else 565 | return -1; 566 | } 567 | } 568 | } else { 569 | for (pos=0;posprefixLength;pos++) { 570 | if (key[depth+pos]!=node->prefix[pos]) { 571 | if (key[depth+pos]>node->prefix[pos]) 572 | return 1; 573 | else 574 | return -1; 575 | } 576 | } 577 | } 578 | return 0; 579 | } 580 | 581 | inline Node* ART::findChild_recordPath(Node* n, uint8_t keyByte, ARTIter* iter) { 582 | NodeCursor nc; 583 | nc.node = n; 584 | switch (n->type) { 585 | case NodeType4: { 586 | Node4* node=static_cast(n); 587 | for (unsigned i=0;icount;i++) { 588 | if (node->key[i]>=keyByte) { 589 | nc.cursor = i; 590 | iter->node_stack.push_back(nc); 591 | if (node->key[i]==keyByte) 592 | return node->child[i]; 593 | else 594 | return iter->minimum_recordPath(node->child[i]); 595 | } 596 | } 597 | iter->node_stack.pop_back(); 598 | //return iter->minimum_recordPath(iter->nextSlot()); 599 | return iter->nextLeaf(); 600 | } 601 | case NodeType16: { 602 | Node16* node=static_cast(n); 603 | for (unsigned i=0;icount;i++) { 604 | //if (node->key[i]>=keyByte) { THE BUGGGGGG! 605 | if (node->key[i]>=flipSign(keyByte)) { 606 | nc.cursor = i; 607 | iter->node_stack.push_back(nc); 608 | //if (node->key[i]==keyByte) THE BUGGGGGG! 609 | if (node->key[i]==flipSign(keyByte)) 610 | return node->child[i]; 611 | else 612 | return iter->minimum_recordPath(node->child[i]); 613 | } 614 | } 615 | iter->node_stack.pop_back(); 616 | //return iter->minimum_recordPath(iter->nextSlot()); 617 | return iter->nextLeaf(); 618 | } 619 | case NodeType48: { 620 | Node48* node=static_cast(n); 621 | if (node->childIndex[keyByte]!=emptyMarker) { 622 | nc.cursor = keyByte; 623 | iter->node_stack.push_back(nc); 624 | return node->child[node->childIndex[keyByte]]; 625 | } 626 | else { 627 | for (unsigned i=keyByte; i<256; i++) { 628 | if (node->childIndex[i]!=emptyMarker) { 629 | nc.cursor = i; 630 | iter->node_stack.push_back(nc); 631 | return node->child[node->childIndex[i]]; 632 | } 633 | } 634 | iter->node_stack.pop_back(); 635 | //return iter->minimum_recordPath(iter->nextSlot()); 636 | return iter->nextLeaf(); 637 | } 638 | } 639 | case NodeType256: { 640 | Node256* node=static_cast(n); 641 | if (node->child[keyByte]!=NULL) { 642 | nc.cursor = keyByte; 643 | iter->node_stack.push_back(nc); 644 | return node->child[keyByte]; 645 | } 646 | else { 647 | for (unsigned i=keyByte; i<256; i++) { 648 | if (node->child[i]!=NULL) { 649 | nc.cursor = i; 650 | iter->node_stack.push_back(nc); 651 | return node->child[i]; 652 | } 653 | } 654 | iter->node_stack.pop_back(); 655 | //return iter->minimum_recordPath(iter->nextSlot()); 656 | return iter->nextLeaf(); 657 | } 658 | } 659 | } 660 | throw; // Unreachable 661 | } 662 | 663 | inline Node* ART::lower_bound(Node* node,uint8_t key[],unsigned keyLength,unsigned depth,unsigned maxKeyLength, ARTIter *iter) { 664 | iter->node_stack.clear(); 665 | while (node!=NULL) { 666 | if (isLeaf(node)) { 667 | return node; 668 | } 669 | 670 | int ctp = CompareToPrefix(node,key,depth,maxKeyLength); 671 | depth+=node->prefixLength; 672 | 673 | if (ctp > 0) { 674 | iter->node_stack.pop_back(); 675 | //return iter->minimum_recordPath(iter->nextSlot()); 676 | return iter->nextLeaf(); 677 | } 678 | else if (ctp < 0) { 679 | return iter->minimum_recordPath(node); 680 | } 681 | 682 | node = findChild_recordPath(node, key[depth], iter); 683 | depth++; 684 | } 685 | 686 | return NULL; 687 | } 688 | 689 | 690 | ART::ART() 691 | : root(NULL), key_length(8), memory(0), num_items(0), 692 | node4_count(0), node16_count(0), node48_count(0), node256_count(0), nullNode(NULL) 693 | {} 694 | 695 | ART::ART(unsigned kl) 696 | : root(NULL), key_length(kl), memory(0), num_items(0), 697 | node4_count(0), node16_count(0), node48_count(0), node256_count(0), nullNode(NULL) 698 | {} 699 | 700 | ART::ART(Node* r) 701 | : root(r), key_length(8), memory(0), num_items(0), 702 | node4_count(0), node16_count(0), node48_count(0), node256_count(0), nullNode(NULL) 703 | {} 704 | 705 | ART::ART(Node* r, unsigned kl) 706 | : root(r), key_length(kl), memory(0), num_items(0), 707 | node4_count(0), node16_count(0), node48_count(0), node256_count(0), nullNode(NULL) 708 | {} 709 | 710 | void ART::load(vector &keys, vector &values, unsigned maxKeyLength) { 711 | uint8_t key[maxKeyLength]; 712 | for (int i = 0; i < keys.size(); i++) { 713 | loadKey((uintptr_t)keys[i].c_str(), key); 714 | insert(key, values[i], key_length); 715 | } 716 | } 717 | 718 | void ART::load(vector &keys, vector &values) { 719 | uint8_t key[8]; 720 | for (int i = 0; i < keys.size(); i++) { 721 | loadKey(keys[i], key); 722 | insert(key, values[i], key_length); 723 | } 724 | } 725 | 726 | void ART::insert(uint8_t key[], uintptr_t value, unsigned maxKeyLength) { 727 | insert(root, &root, key, 0, value, maxKeyLength); 728 | } 729 | 730 | uint64_t ART::lookup(uint8_t key[], unsigned keyLength, unsigned maxKeyLength) { 731 | Node* leaf = lookup(root, key, keyLength, 0, maxKeyLength); 732 | if (isLeaf(leaf)) 733 | return getLeafValue(leaf); 734 | return (uint64_t)0; 735 | } 736 | 737 | uint64_t ART::lookup(uint64_t key64) { 738 | uint8_t key[8]; 739 | loadKey(key64, key); 740 | Node* leaf = lookup(root, key, 8, 0, 8); 741 | if (isLeaf(leaf)) 742 | return getLeafValue(leaf); 743 | return (uint64_t)0; 744 | } 745 | 746 | bool ART::lower_bound(uint8_t key[], unsigned keyLength, unsigned maxKeyLength, ARTIter* iter) { 747 | Node* leaf = lower_bound(root, key, keyLength, 0, maxKeyLength, iter); 748 | if (isLeaf(leaf)) { 749 | iter->val = (uint64_t)getLeafValue(leaf); 750 | iter->nextLeaf(); 751 | return true; 752 | } 753 | else { 754 | iter->val = 0; 755 | return false; 756 | } 757 | } 758 | 759 | bool ART::lower_bound(uint64_t key64, ARTIter* iter) { 760 | uint8_t key[8]; 761 | loadKey(key64, key); 762 | Node* leaf = lower_bound(root, key, 8, 0, 8, iter); 763 | if (isLeaf(leaf)) { 764 | iter->val = (uint64_t)getLeafValue(leaf); 765 | iter->nextLeaf(); 766 | return true; 767 | } 768 | else { 769 | iter->val = 0; 770 | return false; 771 | } 772 | } 773 | 774 | 775 | void ART::erase(uint8_t key[], unsigned keyLength, unsigned maxKeyLength) { 776 | erase(root, &root, key, keyLength, 0, maxKeyLength); 777 | } 778 | 779 | uint64_t ART::getMemory() { 780 | return memory; 781 | } 782 | 783 | 784 | //******************************************************************** 785 | // ARTIter 786 | //******************************************************************** 787 | ARTIter::ARTIter() { 788 | index = NULL; 789 | val = 0; 790 | } 791 | 792 | ARTIter::ARTIter(ART* idx) { 793 | index = idx; 794 | val = 0; 795 | } 796 | 797 | 798 | inline Node* ARTIter::minimum_recordPath(Node* node) { 799 | if (!node) 800 | return NULL; 801 | 802 | if (index->isLeaf(node)) 803 | return node; 804 | 805 | NodeCursor nc; 806 | nc.node = node; 807 | nc.cursor = 0; 808 | node_stack.push_back(nc); 809 | 810 | switch (node->type) { 811 | case NodeType4: { 812 | Node4* n=static_cast(node); 813 | return minimum_recordPath(n->child[0]); 814 | } 815 | case NodeType16: { 816 | Node16* n=static_cast(node); 817 | return minimum_recordPath(n->child[0]); 818 | } 819 | case NodeType48: { 820 | Node48* n=static_cast(node); 821 | unsigned pos=0; 822 | while (n->childIndex[pos]==emptyMarker) 823 | pos++; 824 | node_stack.back().cursor = pos; 825 | return minimum_recordPath(n->child[n->childIndex[pos]]); 826 | } 827 | case NodeType256: { 828 | Node256* n=static_cast(node); 829 | unsigned pos=0; 830 | while (!n->child[pos]) 831 | pos++; 832 | node_stack.back().cursor = pos; 833 | return minimum_recordPath(n->child[pos]); 834 | } 835 | } 836 | throw; // Unreachable 837 | } 838 | 839 | inline Node* ARTIter::nextSlot() { 840 | //while (node_stack.empty()) { THE BUGGGGGG! 841 | while (!node_stack.empty()) { 842 | Node* n = node_stack.back().node; 843 | uint16_t cursor = node_stack.back().cursor; 844 | cursor++; 845 | node_stack.back().cursor = cursor; 846 | switch (n->type) { 847 | case NodeType4: { 848 | Node4* node=static_cast(n); 849 | if (cursor < node->count) 850 | return node->child[cursor]; 851 | break; 852 | } 853 | case NodeType16: { 854 | Node16* node=static_cast(n); 855 | if (cursor < node->count) 856 | return node->child[cursor]; 857 | break; 858 | } 859 | case NodeType48: { 860 | Node48* node=static_cast(n); 861 | for (unsigned i=cursor; i<256; i++) 862 | if (node->childIndex[i]!=emptyMarker) { 863 | node_stack.back().cursor = i; 864 | return node->child[node->childIndex[i]]; 865 | } 866 | break; 867 | } 868 | case NodeType256: { 869 | Node256* node=static_cast(n); 870 | for (unsigned i=cursor; i<256; i++) 871 | if (node->child[i]!=NULL) { 872 | node_stack.back().cursor = i; 873 | return node->child[i]; 874 | } 875 | break; 876 | } 877 | } 878 | node_stack.pop_back(); 879 | } 880 | return NULL; 881 | } 882 | 883 | inline Node* ARTIter::currentLeaf() { 884 | if (node_stack.size() == 0) 885 | return NULL; 886 | 887 | Node* n = node_stack.back().node; 888 | uint16_t cursor = node_stack.back().cursor; 889 | 890 | switch (n->type) { 891 | case NodeType4: { 892 | Node4* node=static_cast(n); 893 | return node->child[cursor]; 894 | } 895 | case NodeType16: { 896 | Node16* node=static_cast(n); 897 | return node->child[cursor]; 898 | } 899 | case NodeType48: { 900 | Node48* node=static_cast(n); 901 | return node->child[node->childIndex[cursor]]; 902 | } 903 | case NodeType256: { 904 | Node256* node=static_cast(n); 905 | return node->child[cursor]; 906 | } 907 | } 908 | return NULL; 909 | } 910 | 911 | inline Node* ARTIter::nextLeaf() { 912 | return minimum_recordPath(nextSlot()); 913 | } 914 | 915 | uint64_t ARTIter::value() { 916 | return val; 917 | } 918 | 919 | bool ARTIter::operator ++ (int) { 920 | Node* leaf = currentLeaf(); 921 | if (index->isLeaf(leaf)) { 922 | val = (uint64_t)index->getLeafValue(leaf); 923 | nextLeaf(); 924 | return true; 925 | } 926 | else { 927 | val = 0; 928 | return false; 929 | } 930 | } 931 | -------------------------------------------------------------------------------- /fst/src/FST.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | FST::FST() : cutoff_level_(0), nodeCountU_(0), childCountU_(0), 4 | cbitsU_(NULL), tbitsU_(NULL), obitsU_(NULL), valuesU_(NULL), 5 | cbytes_(NULL), tbits_(NULL), sbits_(NULL), values_(NULL), 6 | tree_height_(0), last_value_pos_(0), 7 | c_lenU_(0), o_lenU_(0), c_memU_(0), t_memU_(0), o_memU_(0), val_memU_(0), 8 | c_mem_(0), t_mem_(0), s_mem_(0), val_mem_(0), num_t_(0) { } 9 | 10 | FST::~FST() { 11 | if (cbitsU_) delete cbitsU_; 12 | if (tbitsU_) delete tbitsU_; 13 | if (obitsU_) delete obitsU_; 14 | if (valuesU_) delete valuesU_; 15 | 16 | if (cbytes_) delete cbytes_; 17 | if (tbits_) delete tbits_; 18 | if (sbits_) delete sbits_; 19 | if (values_) delete values_; 20 | } 21 | 22 | //stat 23 | uint32_t FST::cMemU() { return c_memU_; } 24 | uint32_t FST::tMemU() { return t_memU_; } 25 | uint32_t FST::oMemU() { return o_memU_;} 26 | uint32_t FST::keyMemU() { return (c_memU_ + t_memU_ + o_memU_); } 27 | uint32_t FST::valueMemU() { return val_memU_; } 28 | 29 | uint64_t FST::cMem() { return c_mem_; } 30 | uint32_t FST::tMem() { return t_mem_; } 31 | uint32_t FST::sMem() { return s_mem_;} 32 | uint64_t FST::keyMem() { return (c_mem_ + t_mem_ + s_mem_); } 33 | uint64_t FST::valueMem() { return val_mem_; } 34 | 35 | uint64_t FST::mem() { return (c_memU_ + t_memU_ + o_memU_ + val_memU_ + c_mem_ + t_mem_ + s_mem_ + val_mem_); } 36 | 37 | uint32_t FST::numT() { return num_t_; } 38 | 39 | //******************************************************************* 40 | inline bool FST::insertChar_cond(const uint8_t ch, vector &c, vector &t, vector &s, int &pos, int &nc) { 41 | if (c.empty() || c.back() != ch) { 42 | c.push_back(ch); 43 | if (c.size() == 1) { 44 | setBit(s.back(), pos % 64); 45 | nc++; 46 | } 47 | pos++; 48 | if (pos % 64 == 0) { 49 | t.push_back(0); 50 | s.push_back(0); 51 | } 52 | return true; 53 | } 54 | else { 55 | if (pos % 64 == 0) 56 | setBit(t.rbegin()[1], 63); 57 | else 58 | setBit(t.back(), (pos - 1) % 64); 59 | return false; 60 | } 61 | } 62 | 63 | inline bool FST::insertChar(const uint8_t ch, bool isTerm, vector &c, vector &t, vector &s, int &pos, int &nc) { 64 | c.push_back(ch); 65 | if (!isTerm) 66 | setBit(t.back(), pos % 64); 67 | setBit(s.back(), pos % 64); 68 | nc++; 69 | pos++; 70 | if (pos % 64 == 0) { 71 | t.push_back(0); 72 | s.push_back(0); 73 | } 74 | return true; 75 | } 76 | 77 | //****************************************************** 78 | // LOAD 79 | //****************************************************** 80 | void FST::load(vector &keys, vector &values, int longestKeyLen) { 81 | tree_height_ = longestKeyLen; 82 | vector > c; 83 | vector > t; 84 | vector > s; 85 | vector > val; 86 | 87 | vector pos_list; 88 | vector nc; //node count 89 | 90 | // init 91 | for (int i = 0; i < longestKeyLen; i++) { 92 | c.push_back(vector()); 93 | t.push_back(vector()); 94 | s.push_back(vector()); 95 | val.push_back(vector()); 96 | 97 | pos_list.push_back(0); 98 | nc.push_back(0); 99 | 100 | t[i].push_back(0); 101 | s[i].push_back(0); 102 | } 103 | 104 | int last_value_level = 0; 105 | for (int k = 0; k < (int)keys.size(); k++) { 106 | string key = keys[k]; 107 | uint64_t value = values[k]; 108 | 109 | // if same key 110 | if (k < (int)(keys.size()-1) && key.compare(keys[k+1]) == 0) 111 | continue; 112 | 113 | int i = 0; 114 | while (i < key.length() && !insertChar_cond((uint8_t)key[i], c[i], t[i], s[i], pos_list[i], nc[i])) 115 | i++; 116 | 117 | if (i < key.length()) { 118 | if (k + 1 < (int)keys.size()) { 119 | int cpl = commonPrefixLen(key, keys[k+1]); 120 | if (i < cpl) { 121 | if (pos_list[i] % 64 == 0) 122 | setBit(t[i].rbegin()[1], 63); 123 | else 124 | setBit(t[i].back(), (pos_list[i] - 1) % 64); 125 | } 126 | 127 | while (i < cpl) { 128 | i++; 129 | if (i < cpl) 130 | insertChar((uint8_t)key[i], false, c[i], t[i], s[i], pos_list[i], nc[i]); 131 | else { 132 | if (i < key.length()) 133 | insertChar((uint8_t)key[i], true, c[i], t[i], s[i], pos_list[i], nc[i]); 134 | else { 135 | insertChar(TERM, true, c[i], t[i], s[i], pos_list[i], nc[i]); 136 | num_t_++; //stat 137 | } 138 | } 139 | } 140 | } 141 | val[i].push_back(value); 142 | } 143 | else 144 | cout << "ERROR!\n"; 145 | 146 | if (k >= keys.size() - 1) 147 | last_value_level = i; 148 | } 149 | 150 | // put together 151 | int nc_total = 0; 152 | for (int i = 0; i < (int)nc.size(); i++) 153 | nc_total += nc[i]; 154 | 155 | int nc_u = 0; 156 | while (nc_u * CUTOFF_RATIO < nc_total) { 157 | nc_u += nc[cutoff_level_]; 158 | cutoff_level_++; 159 | } 160 | cutoff_level_--; 161 | 162 | cout << "cutoff_level_ = " << cutoff_level_ << "\n"; 163 | 164 | // determine the position of the last value for range query boundary check 165 | if (last_value_level < cutoff_level_) { 166 | for (int i = 0; i <= last_value_level; i++) 167 | last_value_pos_ -= val[i].size(); 168 | last_value_pos_++; 169 | } 170 | else { 171 | for (int i = cutoff_level_; i <= last_value_level; i++) 172 | last_value_pos_ += val[i].size(); 173 | last_value_pos_--; 174 | } 175 | 176 | //------------------------------------------------- 177 | vector > cU; 178 | vector > tU; 179 | vector > oU; 180 | 181 | for (int i = 0; i < cutoff_level_; i++) { 182 | cU.push_back(vector()); 183 | tU.push_back(vector()); 184 | oU.push_back(vector()); 185 | } 186 | 187 | for (int i = 0; i < cutoff_level_; i++) { 188 | uint64_t sbitPos_i = 0; 189 | uint64_t smaxPos_i = pos_list[i]; 190 | 191 | uint8_t ch = 0; 192 | int j = 0; 193 | int k = 0; 194 | for (j = 0; j < (int)s[i].size(); j++) { 195 | if (sbitPos_i >= smaxPos_i) break; 196 | for (k = 0; k < 64; k++) { 197 | if (sbitPos_i >= smaxPos_i) break; 198 | 199 | ch = c[i][sbitPos_i]; 200 | if (readBit(s[i][j], k)) { 201 | for (int l = 0; l < 4; l++) { 202 | cU[i].push_back(0); 203 | tU[i].push_back(0); 204 | } 205 | nodeCountU_++; 206 | 207 | if (ch == TERM) 208 | oU[i].push_back(1); 209 | else { 210 | oU[i].push_back(0); 211 | setLabel((uint64_t*)cU[i].data() + cU[i].size() - 4, ch); 212 | if (readBit(t[i][j], k)) { 213 | setLabel((uint64_t*)tU[i].data() + tU[i].size() - 4, ch); 214 | childCountU_++; 215 | } 216 | } 217 | } 218 | else { 219 | setLabel((uint64_t*)cU[i].data() + cU[i].size() - 4, ch); 220 | if (readBit(t[i][j], k)) { 221 | setLabel((uint64_t*)tU[i].data() + tU[i].size() - 4, ch); 222 | childCountU_++; 223 | } 224 | } 225 | sbitPos_i++; 226 | } 227 | } 228 | } 229 | 230 | o_lenU_ = 0; 231 | int vallenU = 0; 232 | for (int i = 0; i < (int)cU.size(); i++) { 233 | c_lenU_ += cU[i].size(); 234 | o_lenU_ += oU[i].size(); 235 | vallenU += val[i].size(); 236 | } 237 | 238 | uint64_t* cbitsU = new uint64_t[c_lenU_]; 239 | uint64_t* tbitsU = new uint64_t[c_lenU_]; 240 | uint64_t* obitsU = new uint64_t[o_lenU_ / 64 + 1]; 241 | valuesU_ = new uint64_t[vallenU]; 242 | 243 | // init 244 | for (int i = 0; i < c_lenU_; i++) { 245 | cbitsU[i] = 0; 246 | tbitsU[i] = 0; 247 | } 248 | for (int i = 0; i < (o_lenU_ / 64 + 1); i++) 249 | obitsU[i] = 0; 250 | 251 | uint64_t c_bitPosU = 0; 252 | for (int i = 0; i < (int)cU.size(); i++) { 253 | for (int j = 0; j < (int)cU[i].size(); j++) { 254 | for (int k = 0; k < 64; k++) { 255 | if (readBit(cU[i][j], k)) 256 | setBit(cbitsU[c_bitPosU/64], c_bitPosU % 64); 257 | c_bitPosU++; 258 | } 259 | } 260 | } 261 | 262 | int c_sizeU = (c_lenU_ / 32 + 1) * 32; // round-up to 1024-bit block size for Poppy 263 | cbitsU_ = new BitmapRankFPoppy(cbitsU, c_sizeU * 64); 264 | c_memU_ = cbitsU_->getNbits() / 8; //stat 265 | 266 | uint64_t t_bitPosU = 0; 267 | for (int i = 0; i < (int)tU.size(); i++) { 268 | for (int j = 0; j < (int)tU[i].size(); j++) { 269 | for (int k = 0; k < 64; k++) { 270 | if (readBit(tU[i][j], k)) 271 | setBit(tbitsU[t_bitPosU/64], t_bitPosU % 64); 272 | t_bitPosU++; 273 | } 274 | } 275 | } 276 | 277 | int t_sizeU = (c_lenU_ / 32 + 1) * 32; // round-up to 1024-bit block size for Poppy 278 | tbitsU_ = new BitmapRankFPoppy(tbitsU, t_sizeU * 64); 279 | t_memU_ = tbitsU_->getNbits() / 8; //stat 280 | 281 | uint64_t o_bitPosU = 0; 282 | for (int i = 0; i < (int)oU.size(); i++) { 283 | for (int j = 0; j < (int)oU[i].size(); j++) { 284 | if (oU[i][j] == 1) 285 | setBit(obitsU[o_bitPosU/64], o_bitPosU % 64); 286 | o_bitPosU++; 287 | } 288 | } 289 | 290 | int o_sizeU = (o_lenU_ / 64 / 32 + 1) * 32; // round-up to 1024-bit block size for Poppy 291 | obitsU_ = new BitmapRankFPoppy(obitsU, o_sizeU * 64); 292 | o_memU_ = obitsU_->getNbits() / 8; //stat 293 | 294 | uint64_t val_posU = 0; 295 | for (int i = 0; i < cutoff_level_; i++) { 296 | for (int j = 0; j < (int)val[i].size(); j++) { 297 | valuesU_[val_posU] = val[i][j]; 298 | val_posU++; 299 | } 300 | } 301 | val_memU_ = vallenU * 8; 302 | 303 | //------------------------------------------------- 304 | for (int i = cutoff_level_; i < (int)c.size(); i++) 305 | c_mem_ += c[i].size(); 306 | 307 | for (int i = cutoff_level_; i < (int)c.size(); i++) 308 | val_mem_ += val[i].size() * sizeof(uint64_t); 309 | 310 | if (c_mem_ % 64 == 0) { 311 | t_mem_ = c_mem_ / 64; 312 | s_mem_ = c_mem_ / 64; 313 | } 314 | else { 315 | t_mem_ = c_mem_ / 64 + 1; 316 | s_mem_ = c_mem_ / 64 + 1; 317 | } 318 | 319 | t_mem_ = (t_mem_ / 32 + 1) * 32; // round-up to 2048-bit block size for Poppy 320 | s_mem_ = (s_mem_ / 32 + 1) * 32; // round-up to 2048-bit block size for Poppy 321 | 322 | cbytes_ = new uint8_t[c_mem_]; 323 | uint64_t* tbits = new uint64_t[t_mem_]; 324 | uint64_t* sbits = new uint64_t[s_mem_]; 325 | values_ = new uint64_t[val_mem_ / sizeof(uint64_t)]; 326 | 327 | // init 328 | for (int i = 0; i < t_mem_; i++) { 329 | tbits[i] = 0; 330 | sbits[i] = 0; 331 | } 332 | 333 | uint64_t c_pos = 0; 334 | for (int i = cutoff_level_; i < (int)c.size(); i++) { 335 | for (int j = 0; j < (int)c[i].size(); j++) { 336 | cbytes_[c_pos] = c[i][j]; 337 | c_pos++; 338 | } 339 | } 340 | 341 | uint64_t t_bitPos = 0; 342 | for (int i = cutoff_level_; i < (int)t.size(); i++) { 343 | uint64_t bitPos_i = 0; 344 | uint64_t maxPos_i = pos_list[i]; 345 | for (int j = 0; j < (int)t[i].size(); j++) { 346 | if (bitPos_i >= maxPos_i) break; 347 | for (int k = 0; k < 64; k++) { 348 | if (bitPos_i >= maxPos_i) break; 349 | if (readBit(t[i][j], k)) 350 | setBit(tbits[t_bitPos/64], t_bitPos % 64); 351 | t_bitPos++; 352 | bitPos_i++; 353 | } 354 | } 355 | } 356 | 357 | tbits_ = new BitmapRankPoppy(tbits, t_mem_ * 64); 358 | t_mem_ = tbits_->getMem(); //stat 359 | 360 | uint64_t s_bitPos = 0; 361 | for (int i = cutoff_level_; i < (int)s.size(); i++) { 362 | uint64_t bitPos_i = 0; 363 | uint64_t maxPos_i = pos_list[i]; 364 | for (int j = 0; j < (int)s[i].size(); j++) { 365 | if (bitPos_i >= maxPos_i) break; 366 | for (int k = 0; k < 64; k++) { 367 | if (bitPos_i >= maxPos_i) break; 368 | if (readBit(s[i][j], k)) 369 | setBit(sbits[s_bitPos/64], s_bitPos % 64); 370 | s_bitPos++; 371 | bitPos_i++; 372 | } 373 | } 374 | } 375 | 376 | sbits_ = new BitmapSelectPoppy(sbits, s_mem_ * 64); 377 | s_mem_ = sbits_->getMem(); //stat 378 | 379 | uint64_t val_pos = 0; 380 | for (int i = cutoff_level_; i < (int)val.size(); i++) { 381 | for (int j = 0; j < (int)val[i].size(); j++) { 382 | values_[val_pos] = val[i][j]; 383 | val_pos++; 384 | } 385 | } 386 | //------------------------------------------------- 387 | } 388 | 389 | void FST::load(vector &keys, vector &values) { 390 | vector keys_str; 391 | for (int i = 0; i < (int)keys.size(); i++) { 392 | char key[8]; 393 | reinterpret_cast(key)[0]=__builtin_bswap64(keys[i]); 394 | keys_str.push_back(string(key, 8)); 395 | } 396 | load(keys_str, values, sizeof(uint64_t)); 397 | } 398 | 399 | //****************************************************** 400 | // IS O BIT SET U? 401 | //****************************************************** 402 | inline bool FST::isCbitSetU(uint64_t nodeNum, uint8_t kc) { 403 | return isLabelExist(cbitsU_->bits_ + (nodeNum << 2), kc); 404 | } 405 | //****************************************************** 406 | // IS O BIT SET U? 407 | //****************************************************** 408 | inline bool FST::isTbitSetU(uint64_t nodeNum, uint8_t kc) { 409 | return isLabelExist(tbitsU_->bits_ + (nodeNum << 2), kc); 410 | } 411 | //****************************************************** 412 | // IS O BIT SET U? 413 | //****************************************************** 414 | inline bool FST::isObitSetU(uint64_t nodeNum) { 415 | return readBit(obitsU_->bits_[nodeNum >> 6], nodeNum & (uint64_t)63); 416 | } 417 | //****************************************************** 418 | // IS S BIT SET? 419 | //****************************************************** 420 | inline bool FST::isSbitSet(uint64_t pos) { 421 | return readBit(sbits_->bits_[pos >> 6], pos & (uint64_t)63); 422 | } 423 | //****************************************************** 424 | // IS T BIT SET? 425 | //****************************************************** 426 | inline bool FST::isTbitSet(uint64_t pos) { 427 | return readBit(tbits_->bits_[pos >> 6], pos & (uint64_t)63); 428 | } 429 | //****************************************************** 430 | // GET VALUE POS U 431 | //****************************************************** 432 | inline uint64_t FST::valuePosU(uint64_t nodeNum, uint64_t pos) { 433 | return cbitsU_->rank(pos + 1) - tbitsU_->rank(pos + 1) + obitsU_->rank(nodeNum + 1) - 1; 434 | } 435 | //****************************************************** 436 | // GET VALUE POS 437 | //****************************************************** 438 | inline uint64_t FST::valuePos(uint64_t pos) { 439 | return pos - tbits_->rank(pos+1); 440 | } 441 | 442 | //****************************************************** 443 | // CHILD NODE NUM 444 | //****************************************************** 445 | inline uint64_t FST::childNodeNumU(uint64_t pos) { 446 | return tbitsU_->rank(pos + 1); 447 | } 448 | inline uint64_t FST::childNodeNum(uint64_t pos) { 449 | return tbits_->rank(pos + 1); 450 | } 451 | 452 | //****************************************************** 453 | // CHILD POS 454 | //****************************************************** 455 | inline uint64_t FST::childpos(uint64_t nodeNum) { 456 | return sbits_->select(nodeNum - nodeCountU_ + 1); 457 | } 458 | 459 | 460 | //****************************************************** 461 | // NODE SIZE 462 | //****************************************************** 463 | inline int FST::nodeSize(uint64_t pos) { 464 | pos++; 465 | uint64_t startIdx = pos >> 6; 466 | uint64_t shift = pos & (uint64_t)0x3F; 467 | 468 | uint64_t bits = sbits_->bits_[startIdx] << shift; 469 | if (bits > 0) 470 | return __builtin_clzll(bits) + 1; 471 | 472 | for (int i = 1; i < 5; i++) { 473 | bits = sbits_->bits_[startIdx + i]; 474 | if (bits > 0) 475 | return 64 * i - shift + __builtin_clzll(bits) + 1; 476 | } 477 | return -1; 478 | } 479 | 480 | //****************************************************** 481 | // SIMD SEARCH 482 | //****************************************************** 483 | inline bool FST::simdSearch(uint64_t &pos, uint64_t size, uint8_t target) { 484 | uint64_t s = 0; 485 | while (size >> 4) { 486 | __m128i cmp= _mm_cmpeq_epi8(_mm_set1_epi8(target), _mm_loadu_si128(reinterpret_cast<__m128i*>(cbytes_ + pos + s))); 487 | unsigned bitfield= _mm_movemask_epi8(cmp); 488 | if (bitfield) { 489 | pos += (s + __builtin_ctz(bitfield)); 490 | return true; 491 | } 492 | s += 16; 493 | size -= 16; 494 | } 495 | 496 | if (size > 0) { 497 | __m128i cmp= _mm_cmpeq_epi8(_mm_set1_epi8(target), _mm_loadu_si128(reinterpret_cast<__m128i*>(cbytes_ + pos + s))); 498 | unsigned bitfield= _mm_movemask_epi8(cmp) & ((1 << size) - 1); 499 | if (bitfield) { 500 | pos += (s + __builtin_ctz(bitfield)); 501 | return true; 502 | } 503 | } 504 | return false; 505 | } 506 | 507 | //****************************************************** 508 | // BINARY SEARCH 509 | //****************************************************** 510 | inline bool FST::binarySearch(uint64_t &pos, uint64_t size, uint8_t target) { 511 | uint64_t l = pos; 512 | uint64_t r = pos + size - 1; 513 | uint64_t m = (l + r) >> 1; 514 | 515 | while (l <= r) { 516 | if (cbytes_[m] == target) { 517 | pos = m; 518 | return true; 519 | } 520 | else if (cbytes_[m] < target) 521 | l = m + 1; 522 | else 523 | r = m - 1; 524 | m = (l + r) >> 1; 525 | } 526 | return false; 527 | } 528 | 529 | inline bool FST::binarySearch_lowerBound(uint64_t &pos, uint64_t size, uint8_t target) { 530 | uint64_t rightBound = pos + size; 531 | uint64_t l = pos; 532 | uint64_t r = pos + size - 1; 533 | uint64_t m = (l + r) >> 1; 534 | 535 | while (l < r) { 536 | if (cbytes_[m] == target) { 537 | pos = m; 538 | return true; 539 | } 540 | else if (cbytes_[m] < target) 541 | l = m + 1; 542 | else 543 | r = m - 1; 544 | m = (l + r) >> 1; 545 | } 546 | 547 | if (cbytes_[m] < target) 548 | pos = m + 1; 549 | else 550 | pos = m; 551 | 552 | return pos < rightBound; 553 | } 554 | 555 | //****************************************************** 556 | // LINEAR SEARCH 557 | //****************************************************** 558 | inline bool FST::linearSearch(uint64_t &pos, uint64_t size, uint8_t target) { 559 | for (int i = 0; i < size; i++) { 560 | if (cbytes_[pos] == target) 561 | return true; 562 | pos++; 563 | } 564 | return false; 565 | } 566 | 567 | inline bool FST::linearSearch_lowerBound(uint64_t &pos, uint64_t size, uint8_t target) { 568 | for (int i = 0; i < size; i++) { 569 | if (cbytes_[pos] >= target) 570 | return true; 571 | pos++; 572 | } 573 | return false; 574 | } 575 | 576 | //****************************************************** 577 | // NODE SEARCH 578 | //****************************************************** 579 | inline bool FST::nodeSearch(uint64_t &pos, int size, uint8_t target) { 580 | if (size < 3) 581 | return linearSearch(pos, size, target); 582 | else if (size < 12) 583 | return binarySearch(pos, size, target); 584 | else 585 | return simdSearch(pos, size, target); 586 | } 587 | 588 | inline bool FST::nodeSearch_lowerBound(uint64_t &pos, int size, uint8_t target) { 589 | if (size < 3) 590 | return linearSearch_lowerBound(pos, size, target); 591 | else 592 | return binarySearch_lowerBound(pos, size, target); 593 | } 594 | 595 | 596 | //****************************************************** 597 | // LOOKUP 598 | //****************************************************** 599 | bool FST::lookup(const uint8_t* key, const int keylen, uint64_t &value) { 600 | int keypos = 0; 601 | uint64_t nodeNum = 0; 602 | uint8_t kc = (uint8_t)key[keypos]; 603 | uint64_t pos = kc; 604 | 605 | while (keypos < keylen && keypos < cutoff_level_) { 606 | kc = (uint8_t)key[keypos]; 607 | pos = (nodeNum << 8) + kc; 608 | 609 | __builtin_prefetch(tbitsU_->bits_ + (nodeNum << 2) + (kc >> 6), 0); 610 | __builtin_prefetch(tbitsU_->rankLUT_ + ((pos + 1) >> 6), 0); 611 | 612 | if (!isCbitSetU(nodeNum, kc)) 613 | return false; 614 | 615 | if (!isTbitSetU(nodeNum, kc)) { 616 | value = valuesU_[valuePosU(nodeNum, pos)]; 617 | return true; 618 | } 619 | 620 | nodeNum = childNodeNumU(pos); 621 | keypos++; 622 | } 623 | 624 | if (keypos < cutoff_level_) { 625 | if (isObitSetU(nodeNum)) { 626 | value = valuesU_[valuePosU(nodeNum, (nodeNum << 8))]; 627 | return true; 628 | } 629 | return false; 630 | } 631 | 632 | //----------------------------------------------------------------------- 633 | pos = (cutoff_level_ == 0) ? 0 : childpos(nodeNum); 634 | 635 | while (keypos < keylen) { 636 | kc = (uint8_t)key[keypos]; 637 | 638 | int nsize = nodeSize(pos); 639 | if (!nodeSearch(pos, nsize, kc)) 640 | return false; 641 | 642 | if (!isTbitSet(pos)) { 643 | value = values_[valuePos(pos)]; 644 | return true; 645 | } 646 | 647 | pos = childpos(childNodeNum(pos) + childCountU_); 648 | keypos++; 649 | 650 | __builtin_prefetch(cbytes_ + pos, 0, 1); 651 | __builtin_prefetch(tbits_->bits_ + (pos >> 6), 0, 1); 652 | __builtin_prefetch(tbits_->rankLUT_ + ((pos + 1) >> 9), 0); 653 | } 654 | 655 | if (cbytes_[pos] == TERM && !isTbitSet(pos)) { 656 | value = values_[valuePos(pos)]; 657 | return true; 658 | } 659 | return false; 660 | } 661 | 662 | bool FST::lookup(const uint64_t key, uint64_t &value) { 663 | uint8_t key_str[8]; 664 | reinterpret_cast(key_str)[0]=__builtin_bswap64(key); 665 | 666 | return lookup(key_str, 8, value); 667 | } 668 | 669 | 670 | //****************************************************** 671 | // NEXT ITEM U 672 | //****************************************************** 673 | inline bool FST::nextItemU(uint64_t nodeNum, uint8_t kc, uint8_t &cc) { 674 | return isLabelExist_lowerBound(cbitsU_->bits_ + (nodeNum << 2), kc, cc); 675 | } 676 | 677 | //****************************************************** 678 | // NEXT LEFT ITEM 679 | //****************************************************** 680 | inline bool FST::nextLeftU(int level, uint64_t pos, FSTIter* iter) { 681 | uint64_t nodeNum = pos >> 8; 682 | uint8_t cc = pos & 255; 683 | 684 | if (!isTbitSetU(nodeNum, cc)) { 685 | iter->setVU(level, nodeNum, pos); 686 | return true; 687 | } 688 | 689 | level++; 690 | nodeNum = (iter->positions[level].keyPos < 0) ? childNodeNumU(pos) : (iter->positions[level].keyPos >> 8); 691 | 692 | while (level < cutoff_level_) { 693 | if (isObitSetU(nodeNum)) { 694 | iter->setKVU(level, nodeNum, (nodeNum << 8), true); 695 | return true; 696 | } 697 | nextItemU(nodeNum, 0, cc); 698 | pos = (nodeNum << 8) + cc; 699 | iter->positions[level].keyPos = pos; 700 | 701 | if (!isTbitSetU(nodeNum, cc)) { 702 | iter->setVU(level, nodeNum, pos); 703 | return true; 704 | } 705 | 706 | level++; 707 | nodeNum = (iter->positions[level].keyPos < 0) ? childNodeNumU(pos) : (iter->positions[level].keyPos >> 8); 708 | } 709 | 710 | pos = (iter->positions[level].keyPos < 0) ? childpos(nodeNum) : iter->positions[level].keyPos; 711 | return nextLeft(level, pos, iter); 712 | } 713 | 714 | inline bool FST::nextLeft(int level, uint64_t pos, FSTIter* iter) { 715 | while (isTbitSet(pos)) { 716 | iter->positions[level].keyPos = pos; 717 | level++; 718 | pos = (iter->positions[level].keyPos < 0) ? childpos(childNodeNum(pos) + childCountU_) : iter->positions[level].keyPos; 719 | } 720 | iter->setKV(level, pos); 721 | return true; 722 | } 723 | 724 | 725 | //****************************************************** 726 | // NEXT NODE 727 | //****************************************************** 728 | inline bool FST::nextNodeU(int level, uint64_t nodeNum, FSTIter* iter) { 729 | int cur_level = (level < cutoff_level_) ? level : (cutoff_level_ - 1); 730 | uint8_t cc = 0; 731 | uint8_t kc = 0; 732 | bool inNode = false; 733 | 734 | while (!inNode) { 735 | nodeNum++; 736 | if (isObitSetU(nodeNum)) { 737 | iter->positions[cur_level].keyPos = nodeNum << 8; 738 | iter->positions[cur_level].isO = true; 739 | } 740 | else { 741 | nextItemU(nodeNum, 0, cc); 742 | iter->positions[cur_level].keyPos = (nodeNum << 8) + cc; 743 | } 744 | 745 | if (cur_level == 0) 746 | break; 747 | 748 | cur_level--; 749 | nodeNum = iter->positions[cur_level].keyPos >> 8; 750 | kc = iter->positions[cur_level].keyPos & 255; 751 | 752 | uint8_t next_kc = (kc == 0 && iter->positions[cur_level].isO) ? kc : (kc + 1); 753 | iter->positions[cur_level].isO = false; 754 | inNode = (kc == 255) ? false : nextItemU(nodeNum, next_kc, cc); 755 | } 756 | iter->positions[cur_level].keyPos = (nodeNum << 8) + cc; 757 | 758 | while (cur_level < level && cur_level < cutoff_level_) { 759 | uint64_t pos = iter->positions[cur_level].keyPos; 760 | nodeNum = pos >> 8; 761 | cc = pos & 255; 762 | if (!isTbitSetU(nodeNum, cc)) { 763 | iter->setVU(cur_level, nodeNum, pos); 764 | return true; 765 | } 766 | cur_level++; 767 | } 768 | 769 | if (level < cutoff_level_) 770 | return nextLeftU(level, iter->positions[cur_level].keyPos, iter); 771 | else 772 | return false; 773 | } 774 | 775 | inline bool FST::nextNode(int level, uint64_t pos, FSTIter* iter) { 776 | bool inNode = false; 777 | int cur_level = level - 1; 778 | while (!inNode && cur_level >= cutoff_level_) { 779 | iter->positions[cur_level].keyPos++; 780 | pos = iter->positions[cur_level].keyPos; 781 | inNode = !isSbitSet(pos); 782 | cur_level--; 783 | } 784 | 785 | if (!inNode && cur_level < cutoff_level_) { 786 | uint64_t nodeNum = iter->positions[cur_level].keyPos >> 8; 787 | uint8_t kc = iter->positions[cur_level].keyPos & 255; 788 | uint8_t cc = 0; 789 | uint8_t next_kc = (kc == 0 && iter->positions[cur_level].isO) ? kc : (kc + 1); 790 | iter->positions[cur_level].isO = false; 791 | 792 | inNode = (kc == 255) ? false : nextItemU(nodeNum, next_kc, cc); 793 | 794 | if (!inNode) { 795 | if (nextNodeU(level, (iter->positions[cur_level].keyPos >> 8), iter)) 796 | return true; 797 | } 798 | else { 799 | iter->positions[cur_level].keyPos = (nodeNum << 8) + cc; 800 | return nextLeftU(cur_level, iter->positions[cur_level].keyPos, iter); 801 | } 802 | } 803 | 804 | cur_level++; 805 | while (cur_level < level) { 806 | uint64_t pos = iter->positions[cur_level].keyPos; 807 | if (!isTbitSet(pos)) { 808 | iter->setV(cur_level, pos); 809 | return true; 810 | } 811 | cur_level++; 812 | } 813 | 814 | return nextLeft(level, iter->positions[cur_level].keyPos, iter); 815 | } 816 | 817 | //****************************************************** 818 | // LOWER BOUND 819 | //****************************************************** 820 | bool FST::lowerBound(const uint8_t* key, const int keylen, FSTIter &iter) { 821 | iter.clear(); 822 | int keypos = 0; 823 | uint64_t nodeNum = 0; 824 | uint8_t kc = (uint8_t)key[keypos]; 825 | uint8_t cc = 0; 826 | uint64_t pos = kc; 827 | 828 | while (keypos < keylen && keypos < cutoff_level_) { 829 | kc = (uint8_t)key[keypos]; 830 | pos = (nodeNum << 8) + kc; 831 | 832 | __builtin_prefetch(tbitsU_->bits_ + (nodeNum << 2) + (kc >> 6), 0); 833 | __builtin_prefetch(tbitsU_->rankLUT_ + ((pos + 1) >> 6), 0); 834 | 835 | if (!nextItemU(nodeNum, kc, cc)) { // next char is in next node 836 | nextNodeU(keypos, nodeNum, &iter); 837 | return nextLeftU(keypos, iter.positions[keypos].keyPos, &iter); 838 | } 839 | 840 | if (cc != kc) { 841 | iter.positions[keypos].keyPos = (nodeNum << 8) + cc; 842 | return nextLeftU(keypos, iter.positions[keypos].keyPos, &iter); 843 | } 844 | 845 | iter.positions[keypos].keyPos = pos; 846 | 847 | if (!isTbitSetU(nodeNum, kc)) { // found key terminiation (value) 848 | iter.len = keypos + 1; 849 | iter.positions[keypos].valPos = valuePosU(nodeNum, pos); 850 | return true; 851 | } 852 | 853 | nodeNum = childNodeNumU(pos); 854 | keypos++; 855 | } 856 | 857 | if (keypos < cutoff_level_) { 858 | pos = nodeNum << 8; 859 | if (isObitSetU(nodeNum)) { 860 | iter.setKVU(keypos, nodeNum, pos, true); 861 | return true; 862 | } 863 | keypos--; 864 | return nextLeftU(keypos, iter.positions[keypos].keyPos, &iter); 865 | } 866 | 867 | //---------------------------------------------------------- 868 | pos = (cutoff_level_ == 0) ? 0 : childpos(nodeNum); 869 | 870 | bool inNode = true; 871 | while (keypos < keylen) { 872 | kc = (uint8_t)key[keypos]; 873 | 874 | int nsize = nodeSize(pos); 875 | inNode = nodeSearch_lowerBound(pos, nsize, kc); 876 | 877 | iter.positions[keypos].keyPos = pos; 878 | 879 | if (!inNode) { 880 | nextNode(keypos, pos, &iter); 881 | return nextLeft(keypos, pos, &iter); 882 | } 883 | 884 | cc = cbytes_[pos]; 885 | if (cc != kc) 886 | return nextLeft(keypos, pos, &iter); 887 | 888 | if (!isTbitSet(pos)) { 889 | iter.len = keypos + 1; 890 | iter.positions[keypos].valPos = valuePos(pos); 891 | return true; 892 | } 893 | 894 | pos = childpos(childNodeNum(pos) + childCountU_); 895 | keypos++; 896 | 897 | __builtin_prefetch(cbytes_ + pos, 0, 1); 898 | __builtin_prefetch(tbits_->bits_ + (pos >> 6), 0, 1); 899 | __builtin_prefetch(tbits_->rankLUT_ + ((pos + 1) >> 9), 0); 900 | } 901 | 902 | if (cbytes_[pos] == TERM && !isTbitSet(pos)) { 903 | iter.positions[keypos].keyPos = pos; 904 | iter.len = keypos + 1; 905 | iter.positions[keypos].valPos = valuePos(pos); 906 | return true; 907 | } 908 | keypos--; 909 | return nextLeft(keypos, iter.positions[keypos].keyPos, &iter); 910 | } 911 | 912 | bool FST::lowerBound(const uint64_t key, FSTIter &iter) { 913 | uint8_t key_str[8]; 914 | reinterpret_cast(key_str)[0]=__builtin_bswap64(key); 915 | return lowerBound(key_str, 8, iter); 916 | } 917 | 918 | 919 | //****************************************************** 920 | // PRINT 921 | //****************************************************** 922 | void FST::printU() { 923 | cout << "\n======================================================\n\n"; 924 | for (int i = 0; i < c_lenU_; i += 4) { 925 | for (int j = 0; j < 256; j++) { 926 | if (isLabelExist(cbitsU_->bits_ + i, (uint8_t)j)) 927 | cout << (char)j; 928 | } 929 | cout << " || "; 930 | } 931 | 932 | cout << "\n======================================================\n\n"; 933 | for (int i = 0; i < c_lenU_; i += 4) { 934 | for (int j = 0; j < 256; j++) { 935 | if (isLabelExist(tbitsU_->bits_ + i, (uint8_t)j)) 936 | cout << (char)j; 937 | //cout << j << " "; 938 | } 939 | cout << " || "; 940 | } 941 | 942 | cout << "\n======================================================\n\n"; 943 | for (int i = 0; i < o_lenU_; i++) { 944 | if (readBit(obitsU_->bits_[i/64], i % 64)) 945 | cout << "1"; 946 | else 947 | cout << "0"; 948 | } 949 | 950 | cout << "\n======================================================\n\n"; 951 | for (int i = 0; i < val_memU_/8; i++) { 952 | cout << valuesU_[i] << " "; 953 | } 954 | cout << "\n"; 955 | } 956 | 957 | void FST::print() { 958 | int c_pos = 0; 959 | int v_pos = 0; 960 | int s_pos = 0; 961 | int value_pos = 0; 962 | 963 | cout << "\n======================================================\n\n"; 964 | 965 | for (int i = 0; i < c_mem_; i++) 966 | cout << "(" << i << ")" << cbytes_[i] << " "; 967 | 968 | cout << "\n======================================================\n\n"; 969 | for (int i = 0; i < c_mem_; i++) { 970 | if (readBit(tbits_->getBits()[i/64], i % 64)) 971 | cout << "(" << i << ")" << "1 "; 972 | else 973 | cout << "(" << i << ")" << "0 "; 974 | } 975 | 976 | cout << "\n======================================================\n\n"; 977 | for (int i = 0; i < c_mem_; i++) { 978 | if (readBit(sbits_->getBits()[i/64], i % 64)) 979 | cout << "(" << i << ")" << "1 "; 980 | else 981 | cout << "(" << i << ")" << "0 "; 982 | } 983 | 984 | cout << "\n======================================================\n\n"; 985 | for (int i = 0; i < val_mem_/8; i++) { 986 | cout << "(" << i << ")" << values_[i] << " "; 987 | } 988 | cout << "\n"; 989 | } 990 | 991 | 992 | //****************************************************** 993 | // ITERATOR 994 | //****************************************************** 995 | FSTIter::FSTIter() : index(NULL), len(0), isEnd(false), cBoundU(0), cBound(0), cutoff_level(0), tree_height(0), last_value_pos(0) { } 996 | 997 | FSTIter::FSTIter(FST* idx) { 998 | index = idx; 999 | tree_height = index->tree_height_; 1000 | cutoff_level = index->cutoff_level_; 1001 | cBoundU = (index->c_lenU_ << 6) - 1; 1002 | cBound = index->cMem() - 1; 1003 | last_value_pos = index->last_value_pos_; 1004 | 1005 | len = 0; 1006 | isEnd = false; 1007 | 1008 | for (int i = 0; i < tree_height; i++) { 1009 | Cursor c; 1010 | c.keyPos = -1; 1011 | c.valPos = -1; 1012 | c.isO = false; 1013 | positions.push_back(c); 1014 | } 1015 | } 1016 | 1017 | void FSTIter::clear() { 1018 | for (int i = 0; i < tree_height; i++) { 1019 | positions[i].keyPos = -1; 1020 | positions[i].valPos = -1; 1021 | positions[i].isO = false; 1022 | } 1023 | 1024 | len = 0; 1025 | isEnd = false; 1026 | } 1027 | 1028 | inline void FSTIter::setVU(int level, uint64_t nodeNum, uint64_t pos) { 1029 | len = level + 1; 1030 | if (positions[level].valPos < 0) 1031 | positions[level].valPos = index->valuePosU(nodeNum, pos); 1032 | else 1033 | positions[level].valPos++; 1034 | } 1035 | 1036 | inline void FSTIter::setKVU(int level, uint64_t nodeNum, uint64_t pos, bool o) { 1037 | positions[level].keyPos = pos; 1038 | positions[level].isO = o; 1039 | len = level + 1; 1040 | if (positions[level].valPos < 0) 1041 | positions[level].valPos = index->valuePosU(nodeNum, pos); 1042 | else 1043 | positions[level].valPos++; 1044 | } 1045 | 1046 | inline void FSTIter::setV(int level, uint64_t pos) { 1047 | len = level + 1; 1048 | if (positions[level].valPos < 0) 1049 | positions[level].valPos = index->valuePos(pos); 1050 | else 1051 | positions[level].valPos++; 1052 | } 1053 | 1054 | inline void FSTIter::setKV(int level, uint64_t pos) { 1055 | positions[level].keyPos = pos; 1056 | len = level + 1; 1057 | if (positions[level].valPos < 0) 1058 | positions[level].valPos = index->valuePos(pos); 1059 | else 1060 | positions[level].valPos++; 1061 | } 1062 | 1063 | //TODO inlining 1064 | uint64_t FSTIter::value () { 1065 | if (len <= cutoff_level) { 1066 | __builtin_prefetch(index->valuesU_ + positions[len-1].valPos + 1, 0, 1); 1067 | return index->valuesU_[positions[len-1].valPos]; 1068 | } 1069 | else { 1070 | __builtin_prefetch(index->values_ + positions[len-1].valPos + 1, 0, 1); 1071 | return index->values_[positions[len-1].valPos]; 1072 | } 1073 | } 1074 | 1075 | bool FSTIter::operator ++ (int) { 1076 | if (unlikely(isEnd)) 1077 | return false; 1078 | 1079 | if (unlikely(positions[len-1].valPos == (0 - last_value_pos) || positions[len-1].valPos == last_value_pos)) 1080 | if ((last_value_pos < 0 && len <= cutoff_level) || (last_value_pos > 0 && len > cutoff_level)) { 1081 | isEnd = true; 1082 | return false; 1083 | } 1084 | 1085 | uint64_t nodeNum = 0; 1086 | uint8_t kc = 0; 1087 | uint8_t cc = 0; 1088 | bool inNode = true; 1089 | int level = len - 1; 1090 | while (level >= 0) { 1091 | if (level < cutoff_level) { 1092 | if (unlikely(positions[level].keyPos >= cBoundU)) { 1093 | level--; 1094 | continue; 1095 | } 1096 | 1097 | nodeNum = positions[level].keyPos >> 8; 1098 | kc = positions[level].keyPos & 255; 1099 | uint8_t next_kc = (kc == 0 && positions[level].isO) ? kc : (kc + 1); 1100 | positions[level].isO = false; 1101 | 1102 | inNode = (kc == 255) ? false : index->nextItemU(nodeNum, next_kc, cc); 1103 | 1104 | if (!inNode) 1105 | return index->nextNodeU(level, nodeNum, this); 1106 | 1107 | positions[level].keyPos = (nodeNum << 8) + cc; 1108 | return index->nextLeftU(level, positions[level].keyPos, this); 1109 | } 1110 | else { 1111 | if (unlikely(positions[level].keyPos >= cBound)) { 1112 | level--; 1113 | continue; 1114 | } 1115 | 1116 | positions[level].keyPos++; 1117 | 1118 | if (index->isSbitSet(positions[level].keyPos)) 1119 | return index->nextNode(level, positions[level].keyPos, this); 1120 | 1121 | return index->nextLeft(level, positions[level].keyPos, this); 1122 | } 1123 | } 1124 | 1125 | return false; 1126 | } 1127 | 1128 | //TODO 1129 | bool FSTIter::operator -- (int) { 1130 | return true; 1131 | } 1132 | 1133 | --------------------------------------------------------------------------------