├── CMakeLists.txt ├── LICENSE ├── README.markdown ├── include ├── llds.h ├── llds_common.docids.h └── llds_common.h ├── libforrest ├── examples │ └── insert.c └── src │ ├── forrest.c │ └── forrest.h ├── linux ├── llds │ ├── Makefile │ ├── kmodllds.h │ └── llds.c └── llds_docids │ ├── Makefile │ ├── kmodllds.h │ └── llds.c └── tests ├── rbtree ├── Makefile ├── rb_llds.c ├── rb_rmtree.c ├── rb_userspace.c ├── rbtree.c ├── rbtree.h ├── tests.c └── tests.h └── run_test.sh /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | EXEC_PROGRAM(uname . 2 | ARGS -r 3 | OUTPUT_VARIABLE linuxrel) 4 | 5 | PROJECT(forrest C) 6 | cmake_minimum_required(VERSION 2.6) 7 | INCLUDE_DIRECTORIES(BEFORE ".") 8 | INCLUDE_DIRECTORIES(BEFORE "..") 9 | INCLUDE_DIRECTORIES(BEFORE "include") 10 | INCLUDE_DIRECTORIES(BEFORE "libforrest/src/") 11 | INCLUDE_DIRECTORIES(BEFORE "/usr/src/linux-headers-${linuxrel}/include") 12 | INCLUDE_DIRECTORIES(BEFORE "/usr/src/linux-headers-${linuxrel}/arch/x86/include") 13 | OPTION(BUILD_SHARED_LIBS "Set to OFF to statically build libforrest" ON) 14 | ADD_LIBRARY(forrest libforrest/src/forrest.c) 15 | 16 | # CFLAGS ... speaking of which we need to create both debug and rel rules to this cmake file... 17 | SET_TARGET_PROPERTIES(forrest PROPERTIES CMAKE_C_FLAGS "-g -O2 -Wall") 18 | 19 | MESSAGE( STATUS "CMAKE_BINARY_DIR: " ${CMAKE_BINARY_DIR} ) 20 | MESSAGE( STATUS "CMAKE_CURRENT_BINARY_DIR: " ${CMAKE_CURRENT_BINARY_DIR} ) 21 | MESSAGE( STATUS "CMAKE_SOURCE_DIR: " ${CMAKE_SOURCE_DIR} ) 22 | MESSAGE( STATUS "CMAKE_CURRENT_SOURCE_DIR: " ${CMAKE_CURRENT_SOURCE_DIR} ) 23 | MESSAGE( STATUS "PROJECT_BINARY_DIR: " ${PROJECT_BINARY_DIR} ) 24 | MESSAGE( STATUS "PROJECT_SOURCE_DIR: " ${PROJECT_SOURCE_DIR} ) 25 | MESSAGE( STATUS "EXECUTABLE_OUTPUT_PATH: " ${EXECUTABLE_OUTPUT_PATH} ) 26 | MESSAGE( STATUS "LIBRARY_OUTPUT_PATH: " ${LIBRARY_OUTPUT_PATH} ) 27 | MESSAGE( STATUS "CMAKE_MODULE_PATH: " ${CMAKE_MODULE_PATH} ) 28 | MESSAGE( STATUS "CMAKE_COMMAND: " ${CMAKE_COMMAND} ) 29 | MESSAGE( STATUS "CMAKE_ROOT: " ${CMAKE_ROOT} ) 30 | MESSAGE( STATUS "CMAKE_CURRENT_LIST_FILE: " ${CMAKE_CURRENT_LIST_FILE} ) 31 | MESSAGE( STATUS "CMAKE_INCLUDE_PATH: " ${CMAKE_INCLUDE_PATH} ) 32 | MESSAGE( STATUS "CMAKE_LIBRARY_PATH: " ${CMAKE_LIBRARY_PATH} ) 33 | MESSAGE( STATUS "CMAKE_SYSTEM: " ${CMAKE_SYSTEM} ) 34 | MESSAGE( STATUS "CMAKE_SYSTEM_NAME: " ${CMAKE_SYSTEM_NAME} ) 35 | MESSAGE( STATUS "CMAKE_SYSTEM_VERSION: " ${CMAKE_SYSTEM_VERSION} ) 36 | MESSAGE( STATUS "CMAKE_SYSTEM_PROCESSOR: " ${CMAKE_SYSTEM_PROCESSOR} ) 37 | MESSAGE( STATUS "CMAKE_SKIP_RPATH: " ${CMAKE_SKIP_RPATH} ) 38 | MESSAGE( STATUS "CMAKE_VERBOSE_MAKEFILE: " ${CMAKE_VERBOSE_MAKEFILE} ) 39 | MESSAGE( STATUS "CMAKE_SUPPRESS_REGENERATION: " ${CMAKE_SUPPRESS_REGENERATION} ) 40 | MESSAGE( STATUS "CMAKE_C_FLAGS: " ${CMAKE_C_FLAGS} ) 41 | MESSAGE( STATUS "CMAKE_CXX_FLAGS: " ${CMAKE_CXX_FLAGS} ) 42 | MESSAGE( STATUS "CMAKE_BUILD_TYPE: " ${CMAKE_BUILD_TYPE} ) 43 | MESSAGE( STATUS "BUILD_SHARED_LIBS: " ${BUILD_SHARED_LIBS} ) 44 | MESSAGE( STATUS "CMAKE_C_COMPILER: " ${CMAKE_C_COMPILER} ) 45 | MESSAGE( STATUS "CMAKE_CXX_COMPILER: " ${CMAKE_CXX_COMPILER} ) 46 | MESSAGE( STATUS "CMAKE_COMPILER_IS_GNUCC: " ${CMAKE_COMPILER_IS_GNUCC} ) 47 | MESSAGE( STATUS "CMAKE_COMPILER_IS_GNUCXX: " ${CMAKE_COMPILER_IS_GNUCXX} ) 48 | MESSAGE( STATUS "CMAKE_AR: " ${CMAKE_AR} ) 49 | MESSAGE( STATUS "CMAKE_RANLIB: " ${CMAKE_RANLIB} ) 50 | 51 | # maybe for the future but think hard before you start linking ugly libraries to this library and ruining its beauty 52 | #TARGET_LINK_LIBRARIES(forrest) 53 | 54 | INSTALL(TARGETS forrest DESTINATION lib PERMISSIONS 55 | OWNER_READ OWNER_WRITE OWNER_EXECUTE 56 | GROUP_READ GROUP_EXECUTE 57 | WORLD_READ WORLD_EXECUTE) 58 | 59 | INSTALL(FILES libforrest/src/forrest.h include/llds.h include/llds_common.h 60 | DESTINATION include/llds) 61 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2011, John Jawed 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: 5 | 6 | Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. 7 | Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. 8 | Neither the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. 9 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 10 | -------------------------------------------------------------------------------- /README.markdown: -------------------------------------------------------------------------------- 1 | Low-Level Data Structure 2 | ======================== 3 | llds is a btree implementation which attempts to maximize memory efficiency via bypassing the virtual memory layer (vmalloc) and through optimized data structure memory semantics. 4 | 5 | The llds general working thesis is: for large memory applications, virtual memory layers can hurt application performance due to increased memory latency when dealing with large data structures. Specifically, data page tables/directories within the kernel and increased DRAM requests can be avoided to boost application memory access. 6 | 7 | Applicable use cases: applications on systems that utilize large in-memory data structures. In our testing, "large" was defined as >4GB structures, which did yield significant gains with llds vs equivalent userspace implementations. 8 | 9 | Intel® Xeon Phi™ processors have somewhat reduced, certainly not eliminated, many of the cache cohorency improvements provided by llds. 10 | 11 | llds still provides better performance (pipeline prefetch) and space efficiency on Phi™ microarchitectures. 12 | 13 | llds 2.0 (WIP) will attempt to better leverage the Phi™'s memory ring bus. 14 | 15 | Complexity 16 | ====================== 17 | | Function | Mean | Worst Case | 18 | | ------------- |:-------------:|:-----:| 19 | | Search | O(log n) | O(log n) | 20 | | Insert | O(log n) | O(log n) | 21 | | Delete | O(log n) | O(log n) | 22 | | Update | O(log n) | O(log n) | 23 | 24 | 25 | Installing/Configuring 26 | ====================== 27 | 28 | The build environment will need libproc, glibc, and linux headers. For Ubuntu/Debian based distros this is available in the libproc-dev, linux-libc-dev, and build-essential pkgs. 29 | 30 | libforrest 31 | ------------------------- 32 |
 33 | $ cmake .
 34 | $ make
 35 | # make install
 36 | 
37 | 38 | llds kernel module 39 | ------------------------- 40 |
 41 | $ cd linux/llds && make -j$(nproc)
 42 | # insmod llds.ko
 43 | # mknod /dev/llds c 834 0
 44 | 
45 | 46 | How it Works 47 | ============ 48 | llds is a Linux kernel module (3.x, 4.x, 5.x, 6.x) which leverages facilities provided by the kernel mm for optimal DRAM memory access. llds uses the red-black tree data structure, which is highly optimized in the kernel and is used to manage processes, epoll file descriptors, file systems, and many other components of the kernel. 49 | 50 | Memory management in llds is optimized for traversal latency, not space efficiency, though space savings are probable due to better alignment in most use cases. llds data structures should not consume any more memory than their equivalent user space implementations. 51 | 52 | Traversal latency is optimized by exploiting underlying physical RAM mechanics, avoiding CPU cache pollution, NUMA cross-check chatter, and streamlining CPU data prefetching (L1D cache lines). Fragmented memory access is less efficient when interacting with modern DRAM controllers. The efficiency also further suffers on NUMA systems as the number of processors/memory banks increases. 53 | 54 | libforrest 55 | ========== 56 | Developers can interact directly with the llds chardev using ioctl(2), however, it is highly recommended that the libforrest API is used to avoid incompatibilities should the ioctl interface change in the future. 57 | 58 | libforrest provides the basic key-value store operations: get, set, and delete. In addition, it provides a 64-bit MurmurHash (rev. A) for llds key hashing. 59 | 60 | Examples are provided in the libforrest/examples directory. 61 | 62 | Benchmarks 63 | ========== 64 | Benchmarks are inherently fluid. All samples and timings are available at http://github.com/johnj/llds-benchmarks, additionally there is a `run_tests.sh` script provided which utilizes oprofile. Along with the `run_tests.sh` script, there is a user-space implementation of red-black trees and an equivalent llds implementation. The goal of benchmarking is about opining to the results of a particular environment but all the tools and scripts are available to let users test their own mileage. 65 | 66 | Benchmark environment: Dell PowerEdge R610, 4x Intel Xeon L5640 (Westmere) w/HT (24 cores), 192GB DDR3 DRAM, Ubuntu 10.04.3 LTS. The keys are 64-bit integers and the values are incremented strings (ie, "0", "1", "2"..."N"). There were no major page faults. 67 | 68 | For conciseness, only tests with 2/16/24 threads and 500K/1.5M/2M keys are listed. dmidecode, samples, and full benchmarks are available at http://github.com/johnj/llds-benchmarks 69 | 70 | Wall Timings (in seconds) 71 | ------------------------- 72 | 73 | 74 | 75 | 76 | 77 |
Threads# of Itemsuserspacelldsllds improvement
2500000000356417612.02x
161500000000929141122.26x
2420000000001264556702.23x
78 | 79 | Unhalted CPU cycles (10000 cycles @ 133mHz) 80 | ----------------------------------------- 81 | 82 | 83 | 84 | 85 | 86 |
Threads# of Itemsuserspacellds
250000000087418776377458531
1615000000002792039325107099682
2420000000009680912335529234102
87 | 88 | L1 cache hits (200000 per sample) 89 | ---------------------------------- 90 | 91 | 92 | 93 | 94 | 95 |
Threads# of Itemsuserspacelldsllds improvement
2500000000307767155022921.78x
16150000000015120921272315531.80x
24200000000023746988391961771.65x
96 | 97 | L2 cache hits (200000 per sample) 98 | ---------------------------------- 99 | 100 | 101 | 102 | 103 | 104 |
Threads# of Itemsuserspacelldsllds improvement
250000000021866602142.75x
161500000000821015112856.23x
2420000000001270728008466.30x
105 | 106 | L3/Last-Level cache hits (200000 per sample) 107 | --------------------------------------------- 108 | 109 | 110 | 111 | 112 | 113 |
Threads# of Itemsuserspacelldsllds improvement
250000000026069322591.24x
1615000000001488272545621.71x
2420000000002701913416491.26x
114 | 115 | L1 Data Prefetch misses (200000 per *hardware* sample) 116 | --------------------------------------------- 117 | 118 | 119 | 120 | 121 | 122 |
Threads# of Itemsuserspacelldsllds improvement
250000000052396211132.48x
1615000000003507531208912.90x
2420000000005447912102682.59x
123 | 124 | 125 | Status 126 | ====== 127 | llds is being used in at least two production search engines. Please let me know if you are using llds in production, you can remain anonymous. 128 | 129 | Work on llds 2.0 is underway to better utilize cache rings which are "today"'s architecture. 130 | 131 | Known Limitations/Issues 132 | ======================== 133 | - libforrest has a limit on the value which comes back from kernel space, the default is 4096 bytes, it can be adjusted through the FORREST_MAX_VAL_LEN directive at compile time. 134 | - Only 64-bit architecture support. 135 | - Only tested on x86, it may work on other arch's, drop a line if it does. 136 | 137 | Future Work 138 | =========== 139 | - Support for additional data structures (hashes are questionable) 140 | - Add atomic operations (increment, decrement, CAS, etc.) in libforrest and llds 141 | - Research about the virtual memory overhead & implementation in the kernel with mitigation techniques 142 | -------------------------------------------------------------------------------- /include/llds.h: -------------------------------------------------------------------------------- 1 | #ifndef __K_LLDS_H 2 | #define __K_LLDS_H 3 | 4 | #include "llds_common.h" 5 | 6 | #define CHRDEV_MJR_NUM 834 7 | #define LLDS_IOCTL_SET_ENTRY _IOR(CHRDEV_MJR_NUM, 1, llds_result_ent) 8 | #define LLDS_IOCTL_SEARCH _IOR(CHRDEV_MJR_NUM, 2, llds_result_ent) 9 | #define LLDS_IOCTL_EXPIRE_DOC_ID _IOR(CHRDEV_MJR_NUM, 3, llds_result_ent) 10 | #define LLDS_IOCTL_RMTREE _IOR(CHRDEV_MJR_NUM, 4, llds_result_ent) 11 | #define LLDS_CDEV_NAME "llds" 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /include/llds_common.docids.h: -------------------------------------------------------------------------------- 1 | #ifndef __K_LLDS_COMMON_H 2 | #define __K_LLDS_COMMON_H 3 | 4 | /* yeah...if these structs become unaligned I'll personally git-blame you to the ends of the earth */ 5 | struct ft_result_ent { 6 | unsigned long long key; /* 8 */ 7 | uint64_t doc_id; /* 8 */ 8 | uint64_t *found_doc_ids; /* 8 */ 9 | int result; /* 4 */ 10 | unsigned int nfound_doc_ids; /* 4 */ 11 | }; 12 | 13 | #endif 14 | -------------------------------------------------------------------------------- /include/llds_common.h: -------------------------------------------------------------------------------- 1 | #ifndef __K_LLDS_COMMON_H 2 | #define __K_LLDS_COMMON_H 3 | 4 | /* yeah...if these structs become unaligned I'll personally git-blame you to the ends of the earth */ 5 | typedef struct __llds_result_ent { 6 | uint64_t key; /* 8 */ 7 | void *val; /* 8 */ 8 | int vlen; /* 4 */ 9 | int result; /* 4 */ 10 | } llds_result_ent; 11 | 12 | #endif 13 | -------------------------------------------------------------------------------- /libforrest/examples/insert.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "forrest.h" 4 | 5 | void main() { 6 | long i = 0; 7 | FORREST *cs; 8 | cs = forrest_alloc(); 9 | 10 | char *idx_key = "key2"; 11 | char *val = "llds is new"; 12 | 13 | uint64_t key = ft_MurmurHash64A(idx_key, strlen(idx_key), 9); 14 | fprintf(stderr, "[set] key: %s mmhash: %lu\n", idx_key, key); 15 | forrest_insert_key(cs, key, val, strlen(val) + 1); 16 | llds_result_ent *res = forrest_get_key(cs, key); 17 | fprintf(stderr, "[get] %s\n", (char *)res->val); 18 | } 19 | -------------------------------------------------------------------------------- /libforrest/src/forrest.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "forrest.h" 4 | 5 | /* http://sites.google.com/site/murmurhash/ this hash func is seriously *AWESOME* */ 6 | uint64_t ft_MurmurHash64A(const void * key, int len, unsigned int seed) 7 | { 8 | const uint64_t m = 0xc6a4a7935bd1e995; 9 | const int r = 47; 10 | 11 | uint64_t h = seed ^ (len * m); 12 | 13 | const uint64_t * data = (const uint64_t *)key; 14 | const uint64_t * end = data + (len/8); 15 | 16 | while(data != end) 17 | { 18 | uint64_t k = *data++; 19 | 20 | k *= m; 21 | k ^= k >> r; 22 | k *= m; 23 | 24 | h ^= k; 25 | h *= m; 26 | } 27 | 28 | const unsigned char * data2 = (const unsigned char*)data; 29 | 30 | switch(len & 7) 31 | { 32 | case 7: h ^= (uint64_t)(data2[6]) << 48; 33 | case 6: h ^= (uint64_t)(data2[5]) << 40; 34 | case 5: h ^= (uint64_t)(data2[4]) << 32; 35 | case 4: h ^= (uint64_t)(data2[3]) << 24; 36 | case 3: h ^= (uint64_t)(data2[2]) << 16; 37 | case 2: h ^= (uint64_t)(data2[1]) << 8; 38 | case 1: h ^= (uint64_t)(data2[0]); 39 | h *= m; 40 | }; 41 | 42 | h ^= h >> r; 43 | h *= m; 44 | h ^= h >> r; 45 | 46 | return h; 47 | } 48 | 49 | /* return error or something more suitable for rel */ 50 | FORREST *forrest_alloc() { 51 | FORREST *cs = malloc(sizeof(FORREST)); 52 | char full_dev_name[128] = ""; 53 | snprintf(full_dev_name, 128, "/dev/%s", LLDS_CDEV_NAME); 54 | cs->fd = open(full_dev_name, O_RDONLY); 55 | return cs; 56 | } 57 | 58 | llds_result_ent *forrest_insert_key(FORREST *cs, uint64_t key, void *val, int vlen) { 59 | llds_result_ent *res = malloc(sizeof(llds_result_ent)); 60 | 61 | if(!res) { 62 | perror("malloc"); 63 | return NULL; 64 | } 65 | 66 | res->key = key; 67 | res->result = -1; 68 | res->val = val; 69 | res->vlen = vlen; 70 | ioctl(cs->fd, LLDS_IOCTL_SET_ENTRY, res); 71 | 72 | return res; 73 | } 74 | 75 | llds_result_ent *forrest_get_key(FORREST *cs, uint64_t key) { 76 | llds_result_ent *res = malloc(sizeof(llds_result_ent)); 77 | 78 | if(!res) { 79 | perror("malloc"); 80 | return NULL; 81 | } 82 | 83 | res->key = key; 84 | res->result = -1; 85 | res->val = malloc(FORREST_MAX_VAL_LEN); 86 | ioctl(cs->fd, LLDS_IOCTL_SEARCH, res); 87 | return res; 88 | } 89 | 90 | llds_result_ent *forrest_rm_tree(FORREST *cs) { 91 | llds_result_ent *res = malloc(sizeof(llds_result_ent)); 92 | 93 | if(!res) { 94 | perror("malloc"); 95 | return NULL; 96 | } 97 | 98 | res->key = 0L; 99 | res->result = -1; 100 | res->val = NULL; 101 | ioctl(cs->fd, LLDS_IOCTL_RMTREE, res); 102 | return res; 103 | } 104 | 105 | void *forrest_cleanup(FORREST *cs) { 106 | if(cs) { return NULL; } 107 | 108 | if(cs->fd) { 109 | close(cs->fd); 110 | } 111 | 112 | free(cs); 113 | 114 | return SUCCESS; 115 | } 116 | -------------------------------------------------------------------------------- /libforrest/src/forrest.h: -------------------------------------------------------------------------------- 1 | #ifndef __FORREST_H 2 | #define __FORREST_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include "llds.h" 16 | 17 | #define SUCCESS 0 /* The request was successful. */ 18 | 19 | typedef struct __forrest { 20 | int fd; 21 | } forrest; 22 | 23 | typedef forrest FORREST; 24 | 25 | FORREST *forrest_alloc(); 26 | uint64_t ft_MurmurHash64A(const void *key, int len, unsigned int seed); 27 | llds_result_ent *forrest_insert_key(FORREST *cs, uint64_t key, void *val, int vlen); 28 | llds_result_ent *forrest_get_key(FORREST *cs, uint64_t key); 29 | llds_result_ent *forrest_rm_tree(FORREST *cs); 30 | 31 | #ifndef FORREST_MAX_VAL_LEN 32 | #define FORREST_MAX_VAL_LEN 4096L 33 | #endif 34 | 35 | #endif 36 | -------------------------------------------------------------------------------- /linux/llds/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += llds.o 2 | 3 | all: 4 | make -C /lib/modules/`uname -r`/build M=`pwd` KBUILD_VERBOSE=1 CONFIG_DEBUG_INFO=1 modules 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | 9 | install: 10 | make -C /lib/modules/`uname -r`/build M=`pwd` KBUILD_VERBOSE=1 CONFIG_DEBUG_INFO=1 modules_install 11 | -------------------------------------------------------------------------------- /linux/llds/kmodllds.h: -------------------------------------------------------------------------------- 1 | #ifndef __KMODLLDS_H 2 | #define __KMODLLDS_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct ioctl_res { 9 | unsigned long long key; 10 | long result; 11 | }; 12 | 13 | extern spinlock_t rbtree_wr_spinlock; 14 | extern spinlock_t expired_docs_wr_spinlock; 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /linux/llds/llds.c: -------------------------------------------------------------------------------- 1 | /* LICENSE - see LICENSE file included in this source distribution */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | 18 | #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,4,0) 19 | #include 20 | #include 21 | #else 22 | #include 23 | #endif 24 | #include 25 | #include 26 | #include 27 | 28 | #include "kmodllds.h" 29 | #include "../../include/llds_common.h" 30 | #include "../../include/llds.h" 31 | 32 | MODULE_LICENSE("BSD"); 33 | 34 | #define SUCCESS 0 35 | #define FAILURE 1 36 | 37 | typedef struct __kllds_entry { 38 | uint64_t key; 39 | void *val; 40 | int vlen; 41 | struct rb_node rb; 42 | } kllds_entry; 43 | 44 | struct rb_root kllds_rbtree = RB_ROOT; 45 | static struct kmem_cache *tmp_kllds_cache; 46 | 47 | /* this is only used for tree set ops */ 48 | rwlock_t kllds_rwlck = __RW_LOCK_UNLOCKED(kllds_rwlck); 49 | 50 | static __always_inline kllds_entry *alloc_kllds_cache(void) { 51 | kllds_entry *ret; 52 | ret = kmem_cache_zalloc(tmp_kllds_cache, GFP_ATOMIC); /* jj: do we really need to 0 out here? */ 53 | return ret; 54 | } 55 | 56 | static void kllds_remove_rbtree(void) { 57 | kllds_entry *elem; 58 | struct rb_node *next; 59 | 60 | write_lock(&kllds_rwlck); 61 | while ((next = rb_first(&kllds_rbtree))) { 62 | elem = rb_entry(next, kllds_entry, rb); 63 | rb_erase(next, &kllds_rbtree); 64 | kfree(elem->val); 65 | kmem_cache_free(tmp_kllds_cache, elem); 66 | } 67 | write_unlock(&kllds_rwlck); 68 | } 69 | 70 | static __always_inline kllds_entry *kllds_search_by_key(struct rb_root *tn_root, uint64_t search_key) { 71 | struct rb_node *next; 72 | kllds_entry *tn = NULL; 73 | 74 | read_lock(&kllds_rwlck); 75 | 76 | next = tn_root->rb_node; 77 | while(next) { 78 | tn = rb_entry(next, kllds_entry, rb); 79 | 80 | if(search_key < tn->key) { 81 | next = next->rb_left; 82 | } else if (search_key > tn->key) { 83 | next = next->rb_right; 84 | } else { 85 | read_unlock(&kllds_rwlck); 86 | return tn; 87 | } 88 | } 89 | 90 | read_unlock(&kllds_rwlck); 91 | 92 | return NULL; 93 | } 94 | 95 | static void kllds_insert_by_key(struct rb_root *ver_root, kllds_entry *ins_tn) { 96 | struct rb_node **uncle = &ver_root->rb_node; 97 | struct rb_node *parent = NULL; 98 | kllds_entry *tn; 99 | 100 | write_lock(&kllds_rwlck); 101 | while(*uncle!=NULL) { 102 | parent = *uncle; 103 | tn = rb_entry(parent, kllds_entry, rb); 104 | 105 | if(ins_tn->key < tn->key) { 106 | uncle = &parent->rb_left; 107 | } else if(ins_tn->key > tn->key) { 108 | uncle = &parent->rb_right; 109 | } else { 110 | write_unlock(&kllds_rwlck); 111 | return; 112 | } 113 | } 114 | rb_link_node(&ins_tn->rb, parent, uncle); 115 | rb_insert_color(&ins_tn->rb, ver_root); 116 | write_unlock(&kllds_rwlck); 117 | } 118 | 119 | static int handle_dev_open(struct inode *inode, struct file *file) { 120 | try_module_get(THIS_MODULE); 121 | return SUCCESS; 122 | } 123 | 124 | static int handle_dev_release(struct inode *inode, struct file *file) { 125 | module_put(THIS_MODULE); 126 | return SUCCESS; 127 | } 128 | 129 | static ssize_t handle_dev_read(struct file *fp, char __user * buf, size_t len, loff_t * offset) { 130 | /* NOOP right now, we need to simply apply a rcu read lock here and then start reading the tree...providing it in some sort of format back to the process which wanted to read...needs to be reset per open() */ 131 | return len; 132 | } 133 | 134 | static ssize_t handle_dev_write(struct file *fp, const char __user * buf, size_t len, loff_t * offset) { 135 | /* NOOP right now, needs to take a "line" of data and figure out the hash etc */ 136 | return len; 137 | } 138 | 139 | long handle_ioctl_ops(struct file *f, unsigned int ioctl_opcode, unsigned long up) { 140 | kllds_entry *res_elem ____cacheline_aligned_in_smp; 141 | llds_result_ent __user *uptr = (llds_result_ent __user *)up; 142 | 143 | if(ioctl_opcode==LLDS_IOCTL_RMTREE) { 144 | printk(KERN_INFO "llds: removing tree\n"); 145 | kllds_remove_rbtree(); 146 | } 147 | 148 | res_elem = kllds_search_by_key(&kllds_rbtree, uptr->key); 149 | 150 | if(ioctl_opcode==LLDS_IOCTL_SEARCH) { 151 | if(!res_elem) { 152 | return FAILURE; 153 | } 154 | if(copy_to_user(uptr->val, res_elem->val, res_elem->vlen)) { 155 | return -EFAULT; 156 | } 157 | return SUCCESS; 158 | } 159 | 160 | /*if(ioctl_opcode==LLDS_IOCTL_SET_ENTRY) { case optimized out */ 161 | if(res_elem) { 162 | /* find, and update value */ 163 | } else { 164 | kllds_entry *ins_elem ____cacheline_aligned_in_smp; 165 | /* new entry */ 166 | ins_elem = alloc_kllds_cache(); 167 | ins_elem->key = uptr->key; 168 | ins_elem->vlen = uptr->vlen; 169 | 170 | ins_elem->val = kzalloc(uptr->vlen, GFP_ATOMIC); 171 | if(copy_from_user(ins_elem->val, uptr->val, uptr->vlen)) { 172 | return -EFAULT; 173 | } 174 | 175 | kllds_insert_by_key(&kllds_rbtree, ins_elem); 176 | } 177 | /*}*/ 178 | 179 | return SUCCESS; 180 | } 181 | 182 | struct file_operations fops = { 183 | .read = handle_dev_read, 184 | .write = handle_dev_write, 185 | .unlocked_ioctl = handle_ioctl_ops, 186 | .compat_ioctl = handle_ioctl_ops, 187 | .open = handle_dev_open, 188 | .release = handle_dev_release, 189 | }; 190 | 191 | static int __init kllds_init_module(void) { 192 | int ret_val; 193 | ret_val = register_chrdev(CHRDEV_MJR_NUM, LLDS_CDEV_NAME, &fops); 194 | 195 | printk(KERN_INFO "llds: make sure you execute the following:\nllds: # mknod /dev/%s c %d 0\nllds: before attempting to work with libforrest (unless it exists)!\n", LLDS_CDEV_NAME, CHRDEV_MJR_NUM); 196 | printk(KERN_INFO "llds: page size is %lu bytes\n", PAGE_SIZE); 197 | 198 | tmp_kllds_cache = KMEM_CACHE(__kllds_entry, SLAB_HWCACHE_ALIGN); 199 | return 0; 200 | } 201 | 202 | static void __exit kllds_cleanup_module(void) { 203 | kmem_cache_destroy(tmp_kllds_cache); 204 | unregister_chrdev(CHRDEV_MJR_NUM, LLDS_CDEV_NAME); 205 | } 206 | 207 | module_init(kllds_init_module) 208 | module_exit(kllds_cleanup_module) 209 | -------------------------------------------------------------------------------- /linux/llds_docids/Makefile: -------------------------------------------------------------------------------- 1 | obj-m += llds.o 2 | 3 | all: 4 | make -C /lib/modules/`uname -r`/build M=`pwd` 5 | 6 | clean: 7 | make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean 8 | -------------------------------------------------------------------------------- /linux/llds_docids/kmodllds.h: -------------------------------------------------------------------------------- 1 | #ifndef __KADU_KERN_H 2 | #define __KADU_KERN_H 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | struct ioctl_res { 9 | unsigned long long key; 10 | long result; 11 | }; 12 | 13 | extern spinlock_t rbtree_wr_spinlock; 14 | extern spinlock_t expired_docs_wr_spinlock; 15 | 16 | #endif 17 | -------------------------------------------------------------------------------- /linux/llds_docids/llds.c: -------------------------------------------------------------------------------- 1 | /* LICENSE - see LICENSE file included in this source distribution */ 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include "kmodllds.h" 22 | #include "../../include/llds_common.h" 23 | #include "../../include/llds.h" 24 | 25 | #define SUCCESS 0 26 | #define FAILURE 1 27 | 28 | typedef struct __kllds_entry { 29 | uint64_t key; 30 | uint64_t *doc_ids; 31 | int ndoc_ids; 32 | struct rb_node rb; 33 | } kllds_entry; 34 | 35 | struct rb_root kllds_rbtree = RB_ROOT; 36 | static struct kmem_cache *tmp_kllds_cache; 37 | 38 | uint64_t *expired_doc_ids = NULL; 39 | uint64_t *realloced_expired_doc_ids; 40 | int nexpired_doc_ids = 0L; 41 | 42 | /* this is only used for tree set ops */ 43 | DEFINE_SPINLOCK(jj_wr_spinlock); 44 | /* expired docs write lock */ 45 | DEFINE_SPINLOCK(expired_doft_wr_spinlock); 46 | 47 | static __always_inline kllds_entry *alloc_kllds_cache(void) { 48 | kllds_entry *ret; 49 | ret = kmem_cache_zalloc(tmp_kllds_cache, GFP_ATOMIC); /* jj: do we really need to 0 out here? */ 50 | return ret; 51 | } 52 | 53 | static __always_inline kllds_entry *kllds_search_by_key(struct rb_root *tn_root, uint64_t search_key) { 54 | struct rb_node *next; 55 | kllds_entry *tn = NULL; 56 | 57 | rcu_read_lock(); 58 | 59 | next = tn_root->rb_node; 60 | while(next) { 61 | tn = rb_entry(next, kllds_entry, rb); 62 | 63 | if(search_key < tn->key) { 64 | next = next->rb_left; 65 | } else if (search_key > tn->key) { 66 | next = next->rb_right; 67 | } else { 68 | return tn; 69 | } 70 | } 71 | rcu_read_unlock(); 72 | 73 | return NULL; 74 | } 75 | 76 | static void kllds_insert_by_key(struct rb_root *ver_root, kllds_entry *ins_tn) { 77 | struct rb_node **uncle = &ver_root->rb_node; 78 | struct rb_node *parent = NULL; 79 | kllds_entry *tn; 80 | 81 | spin_lock(&jj_wr_spinlock); 82 | while(*uncle!=NULL) { 83 | parent = *uncle; 84 | tn = rb_entry(parent, kllds_entry, rb); 85 | 86 | if(ins_tn->key < tn->key) { 87 | uncle = &parent->rb_left; 88 | } else if(ins_tn->key > tn->key) { 89 | uncle = &parent->rb_right; 90 | } else { 91 | spin_unlock(&jj_wr_spinlock); 92 | return; 93 | } 94 | } 95 | rb_link_node(&ins_tn->rb, parent, uncle); 96 | rb_insert_color(&ins_tn->rb, ver_root); 97 | spin_unlock(&jj_wr_spinlock); 98 | } 99 | 100 | static int handle_dev_open(struct inode *inode, struct file *file) { 101 | try_module_get(THIS_MODULE); 102 | return SUCCESS; 103 | } 104 | 105 | static int handle_dev_release(struct inode *inode, struct file *file) { 106 | module_put(THIS_MODULE); 107 | return SUCCESS; 108 | } 109 | 110 | static ssize_t handle_dev_read(struct file *fp, char __user * buf, size_t len, loff_t * offset) { 111 | /* NOOP right now, we need to simply apply a rcu read lock here and then start reading the tree...providing it in some sort of format back to the process which wanted to read...needs to be reset per open() */ 112 | return len; 113 | } 114 | 115 | static ssize_t handle_dev_write(struct file *fp, const char __user * buf, size_t len, loff_t * offset) { 116 | /* NOOP right now, needs to take a "line" of data and figure out the hash etc */ 117 | return len; 118 | } 119 | 120 | long handle_ioctl_ops(struct file *f, unsigned int ioctl_opcode, unsigned long uptr) { 121 | struct ft_result_ent __user *jj = (struct ft_result_ent __user *)uptr; 122 | int doci; 123 | kllds_entry *res_elem ____cacheline_aligned_in_smp; 124 | uint64_t *new_doc_ids; 125 | 126 | /* jj: I'm not happy with how we handle expired docs, this needs to be purged by some process or the mod needs to be rm/ins again to maintain efficiency */ 127 | if(ioctl_opcode==LLDS_IOCTL_EXPIRE_DOC_ID) { 128 | if(!nexpired_doc_ids) { 129 | spin_lock(&expired_doft_wr_spinlock); 130 | expired_doc_ids = kzalloc(sizeof(uint64_t), GFP_ATOMIC); 131 | expired_doc_ids[0] = jj->doc_id; 132 | nexpired_doc_ids = 1L; 133 | spin_unlock(&expired_doft_wr_spinlock); 134 | } else { 135 | for(doci=0; docidoc_id==expired_doc_ids[doci]) { 137 | return SUCCESS; 138 | } 139 | } 140 | spin_lock(&expired_doft_wr_spinlock); 141 | ++nexpired_doc_ids; 142 | realloced_expired_doc_ids = krealloc(expired_doc_ids, sizeof(uint64_t) * (nexpired_doc_ids + 1), GFP_ATOMIC); 143 | expired_doc_ids = realloced_expired_doc_ids; 144 | expired_doc_ids[nexpired_doc_ids-1] = jj->doc_id; 145 | spin_unlock(&expired_doft_wr_spinlock); 146 | } 147 | return SUCCESS; 148 | } 149 | 150 | jj->result = 0UL; 151 | 152 | res_elem = kllds_search_by_key(&kllds_rbtree, jj->key); 153 | 154 | if(ioctl_opcode==LLDS_IOCTL_SEARCH) { 155 | if(!res_elem) { 156 | return FAILURE; 157 | } 158 | jj->result = 1UL; 159 | /*jj->found_doc_ids = res_elem->doc_ids;*/ 160 | copy_to_user(jj->found_doc_ids, res_elem->doc_ids, sizeof(uint64_t) * res_elem->ndoc_ids); 161 | jj->nfound_doc_ids = res_elem->ndoc_ids; 162 | return SUCCESS; 163 | } 164 | 165 | /*if(ioctl_opcode==LLDS_IOCTL_SET_ENTRY) { case optimized out */ 166 | if(res_elem) { 167 | for(doci=0; docindoc_ids; doci++) { 168 | if(jj->doc_id==res_elem->doc_ids[doci]) { 169 | /* found this docid, no need to do anything further */ 170 | return SUCCESS; 171 | } 172 | } 173 | ++res_elem->ndoc_ids; 174 | new_doc_ids = krealloc(res_elem->doc_ids, sizeof(uint64_t) * (res_elem->ndoc_ids), GFP_ATOMIC); 175 | if(!new_doc_ids) { 176 | return -ENOMEM; 177 | } 178 | res_elem->doc_ids = new_doc_ids; 179 | res_elem->doc_ids[res_elem->ndoc_ids-1] = jj->doc_id; 180 | } else { 181 | kllds_entry *ins_elem ____cacheline_aligned_in_smp; 182 | /* new entry */ 183 | ins_elem = alloc_kllds_cache(); 184 | ins_elem->key = jj->key; 185 | ins_elem->doc_ids = kzalloc(sizeof(uint64_t), GFP_ATOMIC); 186 | 187 | /* we don't need to copy primitive mem addrs from userspace, the following code is left as a reference for any future work that might be need to copy more than primitives */ 188 | //get_user(new_doc_id, &jj->doc_id); 189 | //ins_elem->doc_ids[0] = new_doc_id; 190 | ins_elem->doc_ids[0] = jj->doc_id; 191 | ins_elem->ndoc_ids = 1UL; 192 | kllds_insert_by_key(&kllds_rbtree, ins_elem); 193 | } 194 | /*}*/ 195 | 196 | /* jj: feel free to bring back this switch when you have more than 2 iocodes to deal with, I just wanted to optimize the paths/branch prediction for the 2 ops supported initially */ 197 | /*switch(ioctl_opcode) { 198 | case LLDS_IOCTL_SEARCH: 199 | elem = kllds_search_by_key(&kllds_rbtree, (uint64_t)jj->key); 200 | case LLDS_IOCTL_SET_ENTRY: 201 | elem = alloc_kllds_cache(); 202 | elem->key = (uint64_t)jj->key; 203 | kllds_insert_by_key(&kllds_rbtree, elem); 204 | break; 205 | }*/ 206 | return SUCCESS; 207 | } 208 | 209 | struct file_operations fops = { 210 | .read = handle_dev_read, 211 | .write = handle_dev_write, 212 | #ifdef HAVE_UNLOCKED_IOCTL 213 | .unlocked_ioctl = handle_ioctl_ops, 214 | #else 215 | #error "seriously you shouldn't be running kllds with unlocked ioctl...get a real kernel, this is a feeble attempt to stop you from an epic fail! <3 jj" 216 | .ioctl = handle_ioctl_ops, 217 | #endif 218 | .open = handle_dev_open, 219 | .release = handle_dev_release, 220 | }; 221 | 222 | static int __init kllds_init_module(void) { 223 | int ret_val; 224 | ret_val = register_chrdev(CHRDEV_MJR_NUM, LLDS_CDEV_NAME, &fops); 225 | 226 | printk(KERN_INFO "Make sure you execute the following:\n# mknod /dev/%s c %d 0\nbefore trying to work with libforrest (unless it exists)!\n", LLDS_CDEV_NAME, CHRDEV_MJR_NUM); 227 | printk(KERN_INFO "Page size: %lu!\n", PAGE_SIZE); 228 | 229 | tmp_kllds_cache = KMEM_CACHE(__kllds_entry, SLAB_HWCACHE_ALIGN); 230 | return 0; 231 | } 232 | 233 | static void __exit kllds_cleanup_module(void) { 234 | unregister_chrdev(CHRDEV_MJR_NUM, LLDS_CDEV_NAME); 235 | kmem_cache_destroy(tmp_kllds_cache); 236 | kfree(expired_doc_ids); 237 | } 238 | 239 | module_init(kllds_init_module) 240 | module_exit(kllds_cleanup_module) 241 | -------------------------------------------------------------------------------- /tests/rbtree/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all 2 | 3 | all: 4 | gcc -ggdb -O2 -o rb_userspace -I. -I../../include -I../../libforrest/src -lforrest rbtree.c rb_userspace.c tests.c -lproc -pthread 5 | gcc -ggdb -O2 -o rb_llds -I. -I../../include -I../../libforrest/src -lforrest rb_llds.c tests.c -lproc -pthread 6 | gcc -ggdb -O2 -o rb_rmtree -I. -I../../include -I../../libforrest/src -lforrest rb_rmtree.c 7 | -------------------------------------------------------------------------------- /tests/rbtree/rb_llds.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "forrest.h" 5 | #include "tests.h" 6 | 7 | #include 8 | #include 9 | 10 | void *rb_t_thr(void *a) { 11 | uint64_t i, start, end; 12 | FORREST *f; 13 | llds_result_ent *res; 14 | rb_t_attrs *attrs = (rb_t_attrs *)a; 15 | int mode = attrs->mode; 16 | char idx_key[128], val[128]; 17 | 18 | f = forrest_alloc(); 19 | 20 | start = attrs->tno * attrs->items_per_loop; 21 | end = start + attrs->items_per_loop; 22 | 23 | fprintf(stderr, "helo from %s thread %d\n", mode ? "reader" : "writer", attrs->tno + 1); 24 | 25 | for(i=start; ival); 35 | } 36 | 37 | /* the copy below doesn't need to happen for llds (it is copied later by llds from userspace) but it is here keep the benchmark as equivalent as possible 38 | res->val = malloc(vl + 1); 39 | memcpy(res->val, val, vl + 1); 40 | 41 | free(res->val);*/ 42 | 43 | free(res); 44 | } 45 | 46 | forrest_cleanup(f); 47 | } 48 | 49 | int main(int argc, char **argv) { 50 | uint64_t i, items_per_thread = NO_OF_ITEMS; 51 | int threads = 1; 52 | pthread_t t_th[MAX_THREADS]; 53 | 54 | if(argc > 1) { 55 | threads = atoi(argv[1]); 56 | threads = threads > MAX_THREADS ? MAX_THREADS : threads; 57 | } 58 | if(argc > 2) { 59 | items_per_thread = atoi(argv[2]); 60 | } 61 | 62 | if(threads > 1) { 63 | items_per_thread = items_per_thread / threads; 64 | } 65 | 66 | fprintf(stderr, "benchmarking with %d threads (%lu items per thread)\n", threads, items_per_thread); 67 | 68 | /* writers */ 69 | for(i=0; itno = i; 72 | attrs->items_per_loop = items_per_thread; 73 | attrs->mode = 0; 74 | 75 | if(pthread_create(&t_th[i], NULL, rb_t_thr, (void*)attrs)) { 76 | fprintf(stderr, "thread creation failed:\n"); 77 | perror("pthread_create"); 78 | } 79 | } 80 | 81 | for(i=0; itno = i; 89 | attrs->items_per_loop = items_per_thread; 90 | attrs->mode = 1; 91 | 92 | if(pthread_create(&t_th[i], NULL, rb_t_thr, (void*)attrs)) { 93 | fprintf(stderr, "thread creation failed:\n"); 94 | perror("pthread_create"); 95 | } 96 | } 97 | 98 | for(i=0; i 2 | #include 3 | #include "forrest.h" 4 | 5 | int main(int argc, char **argv) { 6 | FORREST *f; 7 | f = forrest_alloc(); 8 | 9 | forrest_rm_tree(f); 10 | 11 | forrest_cleanup(f); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /tests/rbtree/rb_userspace.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 1 /* for CPU_* macros in sched.h */ 2 | #include 3 | #include 4 | #include 5 | 6 | #include "forrest.h" 7 | #include "tests.h" 8 | 9 | #include 10 | #include 11 | 12 | pthread_rwlock_t rb_t_wrl = PTHREAD_RWLOCK_INITIALIZER; 13 | 14 | typedef struct __tmp_llds_entry { 15 | uint64_t key; 16 | void *val; 17 | int vlen; 18 | struct rb_node rb; 19 | } t_llds_entry; 20 | 21 | struct rb_root rt = RB_ROOT; 22 | 23 | static inline t_llds_entry *t_llds_search_by_key(struct rb_root *tn_root, uint64_t search_key) { 24 | struct rb_node *next; 25 | t_llds_entry *tn = NULL; 26 | 27 | next = tn_root->rb_node; 28 | while(next) { 29 | tn = rb_entry(next, t_llds_entry, rb); 30 | 31 | if(search_key < tn->key) { 32 | next = next->rb_left; 33 | } else if (search_key > tn->key) { 34 | next = next->rb_right; 35 | } else { 36 | return tn; 37 | } 38 | } 39 | 40 | return NULL; 41 | } 42 | 43 | static inline t_llds_entry *t_alloc_llds_entry(void) { 44 | t_llds_entry *ret; 45 | ret = malloc(sizeof(t_llds_entry)); 46 | return ret; 47 | } 48 | 49 | static void t_llds_insert_by_key(struct rb_root *ver_root, t_llds_entry *ins_tn) { 50 | struct rb_node **uncle = &ver_root->rb_node; 51 | struct rb_node *parent = NULL; 52 | t_llds_entry *tn; 53 | 54 | while(*uncle!=NULL) { 55 | parent = *uncle; 56 | tn = rb_entry(parent, t_llds_entry, rb); 57 | 58 | if(ins_tn->key < tn->key) { 59 | uncle = &parent->rb_left; 60 | } else if(ins_tn->key > tn->key) { 61 | uncle = &parent->rb_right; 62 | } else { 63 | return; 64 | } 65 | } 66 | rb_link_node(&ins_tn->rb, parent, uncle); 67 | rb_insert_color(&ins_tn->rb, ver_root); 68 | } 69 | 70 | void *rb_t_thr(void *a) { 71 | uint64_t i, start, end; 72 | rb_t_attrs *attrs = (rb_t_attrs *)a; 73 | int mode = attrs->mode; 74 | char idx_key[128], val[128]; 75 | t_llds_entry *ins_elem, *res; 76 | 77 | start = attrs->tno * attrs->items_per_loop; 78 | end = start + attrs->items_per_loop; 79 | 80 | fprintf(stderr, "helo from %s thread %d\n", mode ? "reader" : "writer", attrs->tno); 81 | 82 | for(i=start; ikey = key; 98 | 99 | ins_elem->val = malloc(vl + 1); 100 | memcpy(ins_elem->val, val, vl + 1); 101 | 102 | pthread_rwlock_wrlock(&rb_t_wrl); 103 | t_llds_insert_by_key(&rt, ins_elem); 104 | pthread_rwlock_unlock(&rb_t_wrl); 105 | } 106 | } 107 | } 108 | 109 | int main(int argc, char **argv) { 110 | uint64_t i, items_per_thread = NO_OF_ITEMS; 111 | int threads = 1; 112 | pthread_t t_th[MAX_THREADS]; 113 | 114 | if(argc > 1) { 115 | threads = atoi(argv[1]); 116 | threads = threads > MAX_THREADS ? MAX_THREADS : threads; 117 | } 118 | if(argc > 2) { 119 | items_per_thread = atoi(argv[2]); 120 | } 121 | 122 | if(threads > 1) { 123 | items_per_thread = items_per_thread / threads; 124 | } 125 | 126 | fprintf(stderr, "benchmarking with %d threads (%lu items per thread)\n", threads, items_per_thread); 127 | 128 | print_resource_usage(); 129 | 130 | /* writers */ 131 | for(i=0; itno = i; 134 | attrs->items_per_loop = items_per_thread; 135 | attrs->mode = 0; 136 | 137 | if(pthread_create(&t_th[i], NULL, rb_t_thr, (void*)attrs)) { 138 | fprintf(stderr, "thread creation failed:\n"); 139 | perror("pthread_create"); 140 | } 141 | } 142 | 143 | for(i=0; itno = i; 153 | attrs->items_per_loop = items_per_thread; 154 | attrs->mode = 1; 155 | 156 | if(pthread_create(&t_th[i], NULL, rb_t_thr, (void*)attrs)) { 157 | fprintf(stderr, "thread creation failed:\n"); 158 | perror("pthread_create"); 159 | } 160 | } 161 | 162 | for(i=0; i user space by JJ 3 | (C) 1999 Andrea Arcangeli 4 | (C) 2002 David Woodhouse 5 | (C) 2011 John "JJ" Jawed 6 | 7 | This program is free software; you can redistribute it and/or modify 8 | it under the terms of the GNU General Public License as published by 9 | the Free Software Foundation; either version 2 of the License, or 10 | (at your option) any later version. 11 | 12 | This program is distributed in the hope that it will be useful, 13 | but WITHOUT ANY WARRANTY; without even the implied warranty of 14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 15 | GNU General Public License for more details. 16 | 17 | You should have received a copy of the GNU General Public License 18 | along with this program; if not, write to the Free Software 19 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 20 | 21 | */ 22 | 23 | #include 24 | 25 | static void __rb_rotate_left(struct rb_node *node, struct rb_root *root) 26 | { 27 | struct rb_node *right = node->rb_right; 28 | struct rb_node *parent = rb_parent(node); 29 | 30 | if ((node->rb_right = right->rb_left)) 31 | rb_set_parent(right->rb_left, node); 32 | right->rb_left = node; 33 | 34 | rb_set_parent(right, parent); 35 | 36 | if (parent) 37 | { 38 | if (node == parent->rb_left) 39 | parent->rb_left = right; 40 | else 41 | parent->rb_right = right; 42 | } 43 | else 44 | root->rb_node = right; 45 | rb_set_parent(node, right); 46 | } 47 | 48 | static void __rb_rotate_right(struct rb_node *node, struct rb_root *root) 49 | { 50 | struct rb_node *left = node->rb_left; 51 | struct rb_node *parent = rb_parent(node); 52 | 53 | if ((node->rb_left = left->rb_right)) 54 | rb_set_parent(left->rb_right, node); 55 | left->rb_right = node; 56 | 57 | rb_set_parent(left, parent); 58 | 59 | if (parent) 60 | { 61 | if (node == parent->rb_right) 62 | parent->rb_right = left; 63 | else 64 | parent->rb_left = left; 65 | } 66 | else 67 | root->rb_node = left; 68 | rb_set_parent(node, left); 69 | } 70 | 71 | void rb_insert_color(struct rb_node *node, struct rb_root *root) 72 | { 73 | struct rb_node *parent, *gparent; 74 | 75 | while ((parent = rb_parent(node)) && rb_is_red(parent)) 76 | { 77 | gparent = rb_parent(parent); 78 | 79 | if (parent == gparent->rb_left) 80 | { 81 | { 82 | register struct rb_node *uncle = gparent->rb_right; 83 | if (uncle && rb_is_red(uncle)) 84 | { 85 | rb_set_black(uncle); 86 | rb_set_black(parent); 87 | rb_set_red(gparent); 88 | node = gparent; 89 | continue; 90 | } 91 | } 92 | 93 | if (parent->rb_right == node) 94 | { 95 | register struct rb_node *tmp; 96 | __rb_rotate_left(parent, root); 97 | tmp = parent; 98 | parent = node; 99 | node = tmp; 100 | } 101 | 102 | rb_set_black(parent); 103 | rb_set_red(gparent); 104 | __rb_rotate_right(gparent, root); 105 | } else { 106 | { 107 | register struct rb_node *uncle = gparent->rb_left; 108 | if (uncle && rb_is_red(uncle)) 109 | { 110 | rb_set_black(uncle); 111 | rb_set_black(parent); 112 | rb_set_red(gparent); 113 | node = gparent; 114 | continue; 115 | } 116 | } 117 | 118 | if (parent->rb_left == node) 119 | { 120 | register struct rb_node *tmp; 121 | __rb_rotate_right(parent, root); 122 | tmp = parent; 123 | parent = node; 124 | node = tmp; 125 | } 126 | 127 | rb_set_black(parent); 128 | rb_set_red(gparent); 129 | __rb_rotate_left(gparent, root); 130 | } 131 | } 132 | 133 | rb_set_black(root->rb_node); 134 | } 135 | 136 | static void __rb_erase_color(struct rb_node *node, struct rb_node *parent, 137 | struct rb_root *root) 138 | { 139 | struct rb_node *other; 140 | 141 | while ((!node || rb_is_black(node)) && node != root->rb_node) 142 | { 143 | if (parent->rb_left == node) 144 | { 145 | other = parent->rb_right; 146 | if (rb_is_red(other)) 147 | { 148 | rb_set_black(other); 149 | rb_set_red(parent); 150 | __rb_rotate_left(parent, root); 151 | other = parent->rb_right; 152 | } 153 | if ((!other->rb_left || rb_is_black(other->rb_left)) && 154 | (!other->rb_right || rb_is_black(other->rb_right))) 155 | { 156 | rb_set_red(other); 157 | node = parent; 158 | parent = rb_parent(node); 159 | } 160 | else 161 | { 162 | if (!other->rb_right || rb_is_black(other->rb_right)) 163 | { 164 | rb_set_black(other->rb_left); 165 | rb_set_red(other); 166 | __rb_rotate_right(other, root); 167 | other = parent->rb_right; 168 | } 169 | rb_set_color(other, rb_color(parent)); 170 | rb_set_black(parent); 171 | rb_set_black(other->rb_right); 172 | __rb_rotate_left(parent, root); 173 | node = root->rb_node; 174 | break; 175 | } 176 | } 177 | else 178 | { 179 | other = parent->rb_left; 180 | if (rb_is_red(other)) 181 | { 182 | rb_set_black(other); 183 | rb_set_red(parent); 184 | __rb_rotate_right(parent, root); 185 | other = parent->rb_left; 186 | } 187 | if ((!other->rb_left || rb_is_black(other->rb_left)) && 188 | (!other->rb_right || rb_is_black(other->rb_right))) 189 | { 190 | rb_set_red(other); 191 | node = parent; 192 | parent = rb_parent(node); 193 | } 194 | else 195 | { 196 | if (!other->rb_left || rb_is_black(other->rb_left)) 197 | { 198 | rb_set_black(other->rb_right); 199 | rb_set_red(other); 200 | __rb_rotate_left(other, root); 201 | other = parent->rb_left; 202 | } 203 | rb_set_color(other, rb_color(parent)); 204 | rb_set_black(parent); 205 | rb_set_black(other->rb_left); 206 | __rb_rotate_right(parent, root); 207 | node = root->rb_node; 208 | break; 209 | } 210 | } 211 | } 212 | if (node) 213 | rb_set_black(node); 214 | } 215 | 216 | void rb_erase(struct rb_node *node, struct rb_root *root) 217 | { 218 | struct rb_node *child, *parent; 219 | int color; 220 | 221 | if (!node->rb_left) 222 | child = node->rb_right; 223 | else if (!node->rb_right) 224 | child = node->rb_left; 225 | else 226 | { 227 | struct rb_node *old = node, *left; 228 | 229 | node = node->rb_right; 230 | while ((left = node->rb_left) != NULL) 231 | node = left; 232 | 233 | if (rb_parent(old)) { 234 | if (rb_parent(old)->rb_left == old) 235 | rb_parent(old)->rb_left = node; 236 | else 237 | rb_parent(old)->rb_right = node; 238 | } else 239 | root->rb_node = node; 240 | 241 | child = node->rb_right; 242 | parent = rb_parent(node); 243 | color = rb_color(node); 244 | 245 | if (parent == old) { 246 | parent = node; 247 | } else { 248 | if (child) 249 | rb_set_parent(child, parent); 250 | parent->rb_left = child; 251 | 252 | node->rb_right = old->rb_right; 253 | rb_set_parent(old->rb_right, node); 254 | } 255 | 256 | node->rb_parent_color = old->rb_parent_color; 257 | node->rb_left = old->rb_left; 258 | rb_set_parent(old->rb_left, node); 259 | 260 | goto color; 261 | } 262 | 263 | parent = rb_parent(node); 264 | color = rb_color(node); 265 | 266 | if (child) 267 | rb_set_parent(child, parent); 268 | if (parent) 269 | { 270 | if (parent->rb_left == node) 271 | parent->rb_left = child; 272 | else 273 | parent->rb_right = child; 274 | } 275 | else 276 | root->rb_node = child; 277 | 278 | color: 279 | if (color == RB_BLACK) 280 | __rb_erase_color(child, parent, root); 281 | } 282 | 283 | static void rb_augment_path(struct rb_node *node, rb_augment_f func, void *data) 284 | { 285 | struct rb_node *parent; 286 | 287 | up: 288 | func(node, data); 289 | parent = rb_parent(node); 290 | if (!parent) 291 | return; 292 | 293 | if (node == parent->rb_left && parent->rb_right) 294 | func(parent->rb_right, data); 295 | else if (parent->rb_left) 296 | func(parent->rb_left, data); 297 | 298 | node = parent; 299 | goto up; 300 | } 301 | 302 | /* 303 | * after inserting @node into the tree, update the tree to account for 304 | * both the new entry and any damage done by rebalance 305 | */ 306 | void rb_augment_insert(struct rb_node *node, rb_augment_f func, void *data) 307 | { 308 | if (node->rb_left) 309 | node = node->rb_left; 310 | else if (node->rb_right) 311 | node = node->rb_right; 312 | 313 | rb_augment_path(node, func, data); 314 | } 315 | 316 | /* 317 | * before removing the node, find the deepest node on the rebalance path 318 | * that will still be there after @node gets removed 319 | */ 320 | struct rb_node *rb_augment_erase_begin(struct rb_node *node) 321 | { 322 | struct rb_node *deepest; 323 | 324 | if (!node->rb_right && !node->rb_left) 325 | deepest = rb_parent(node); 326 | else if (!node->rb_right) 327 | deepest = node->rb_left; 328 | else if (!node->rb_left) 329 | deepest = node->rb_right; 330 | else { 331 | deepest = rb_next(node); 332 | if (deepest->rb_right) 333 | deepest = deepest->rb_right; 334 | else if (rb_parent(deepest) != node) 335 | deepest = rb_parent(deepest); 336 | } 337 | 338 | return deepest; 339 | } 340 | 341 | /* 342 | * after removal, update the tree to account for the removed entry 343 | * and any rebalance damage. 344 | */ 345 | void rb_augment_erase_end(struct rb_node *node, rb_augment_f func, void *data) 346 | { 347 | if (node) 348 | rb_augment_path(node, func, data); 349 | } 350 | 351 | /* 352 | * This function returns the first node (in sort order) of the tree. 353 | */ 354 | struct rb_node *rb_first(const struct rb_root *root) 355 | { 356 | struct rb_node *n; 357 | 358 | n = root->rb_node; 359 | if (!n) 360 | return NULL; 361 | while (n->rb_left) 362 | n = n->rb_left; 363 | return n; 364 | } 365 | 366 | struct rb_node *rb_last(const struct rb_root *root) 367 | { 368 | struct rb_node *n; 369 | 370 | n = root->rb_node; 371 | if (!n) 372 | return NULL; 373 | while (n->rb_right) 374 | n = n->rb_right; 375 | return n; 376 | } 377 | 378 | struct rb_node *rb_next(const struct rb_node *node) 379 | { 380 | struct rb_node *parent; 381 | 382 | if (rb_parent(node) == node) 383 | return NULL; 384 | 385 | /* If we have a right-hand child, go down and then left as far 386 | as we can. */ 387 | if (node->rb_right) { 388 | node = node->rb_right; 389 | while (node->rb_left) 390 | node=node->rb_left; 391 | return (struct rb_node *)node; 392 | } 393 | 394 | /* No right-hand children. Everything down and left is 395 | smaller than us, so any 'next' node must be in the general 396 | direction of our parent. Go up the tree; any time the 397 | ancestor is a right-hand child of its parent, keep going 398 | up. First time it's a left-hand child of its parent, said 399 | parent is our 'next' node. */ 400 | while ((parent = rb_parent(node)) && node == parent->rb_right) 401 | node = parent; 402 | 403 | return parent; 404 | } 405 | 406 | struct rb_node *rb_prev(const struct rb_node *node) 407 | { 408 | struct rb_node *parent; 409 | 410 | if (rb_parent(node) == node) 411 | return NULL; 412 | 413 | /* If we have a left-hand child, go down and then right as far 414 | as we can. */ 415 | if (node->rb_left) { 416 | node = node->rb_left; 417 | while (node->rb_right) 418 | node=node->rb_right; 419 | return (struct rb_node *)node; 420 | } 421 | 422 | /* No left-hand children. Go up till we find an ancestor which 423 | is a right-hand child of its parent */ 424 | while ((parent = rb_parent(node)) && node == parent->rb_left) 425 | node = parent; 426 | 427 | return parent; 428 | } 429 | 430 | void rb_replace_node(struct rb_node *victim, struct rb_node *new, 431 | struct rb_root *root) 432 | { 433 | struct rb_node *parent = rb_parent(victim); 434 | 435 | /* Set the surrounding nodes to point to the replacement */ 436 | if (parent) { 437 | if (victim == parent->rb_left) 438 | parent->rb_left = new; 439 | else 440 | parent->rb_right = new; 441 | } else { 442 | root->rb_node = new; 443 | } 444 | if (victim->rb_left) 445 | rb_set_parent(victim->rb_left, new); 446 | if (victim->rb_right) 447 | rb_set_parent(victim->rb_right, new); 448 | 449 | /* Copy the pointers/colour from the victim to the replacement */ 450 | *new = *victim; 451 | } 452 | -------------------------------------------------------------------------------- /tests/rbtree/rbtree.h: -------------------------------------------------------------------------------- 1 | /* 2 | Red Black Trees - modified from kernel -> user space by JJ 3 | (C) 1999 Andrea Arcangeli 4 | (C) 2011 John "JJ" Jawed 5 | 6 | This program is free software; you can redistribute it and/or modify 7 | it under the terms of the GNU General Public License as published by 8 | the Free Software Foundation; either version 2 of the License, or 9 | (at your option) any later version. 10 | 11 | This program is distributed in the hope that it will be useful, 12 | but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | GNU General Public License for more details. 15 | 16 | You should have received a copy of the GNU General Public License 17 | along with this program; if not, write to the Free Software 18 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 19 | 20 | To use rbtrees you'll have to implement your own insert and search cores. 21 | This will avoid us to use callbacks and to drop drammatically performances. 22 | I know it's not the cleaner way, but in C (not in C++) to get 23 | performances and genericity... 24 | 25 | Some example of insert and search follows here. The search is a plain 26 | normal search over an ordered tree. The insert instead must be implemented 27 | in two steps: First, the code must insert the element in order as a red leaf 28 | in the tree, and then the support library function rb_insert_color() must 29 | be called. Such function will do the not trivial work to rebalance the 30 | rbtree, if necessary. 31 | 32 | ----------------------------------------------------------------------- 33 | static inline struct page * rb_search_page_cache(struct inode * inode, 34 | unsigned long offset) 35 | { 36 | struct rb_node * n = inode->i_rb_page_cache.rb_node; 37 | struct page * page; 38 | 39 | while (n) 40 | { 41 | page = rb_entry(n, struct page, rb_page_cache); 42 | 43 | if (offset < page->offset) 44 | n = n->rb_left; 45 | else if (offset > page->offset) 46 | n = n->rb_right; 47 | else 48 | return page; 49 | } 50 | return NULL; 51 | } 52 | 53 | static inline struct page * __rb_insert_page_cache(struct inode * inode, 54 | unsigned long offset, 55 | struct rb_node * node) 56 | { 57 | struct rb_node ** p = &inode->i_rb_page_cache.rb_node; 58 | struct rb_node * parent = NULL; 59 | struct page * page; 60 | 61 | while (*p) 62 | { 63 | parent = *p; 64 | page = rb_entry(parent, struct page, rb_page_cache); 65 | 66 | if (offset < page->offset) 67 | p = &(*p)->rb_left; 68 | else if (offset > page->offset) 69 | p = &(*p)->rb_right; 70 | else 71 | return page; 72 | } 73 | 74 | rb_link_node(node, parent, p); 75 | 76 | return NULL; 77 | } 78 | 79 | static inline struct page * rb_insert_page_cache(struct inode * inode, 80 | unsigned long offset, 81 | struct rb_node * node) 82 | { 83 | struct page * ret; 84 | if ((ret = __rb_insert_page_cache(inode, offset, node))) 85 | goto out; 86 | rb_insert_color(node, &inode->i_rb_page_cache); 87 | out: 88 | return ret; 89 | } 90 | ----------------------------------------------------------------------- 91 | */ 92 | 93 | #ifndef _LLDS_RBTREE_H 94 | #define _LLDS_RBTREE_H 95 | 96 | #include 97 | #include 98 | 99 | #define offsetof(type, member) __builtin_offsetof (type, member) 100 | 101 | /** 102 | * container_of - cast a member of a structure out to the containing structure 103 | * @ptr: the pointer to the member. 104 | * @type: the type of the container struct this is embedded in. 105 | * @member: the name of the member within the struct. 106 | * 107 | */ 108 | #define container_of(ptr, type, member) ({ \ 109 | const typeof( ((type *)0)->member ) *__mptr = (ptr); \ 110 | (type *)( (char *)__mptr - offsetof(type,member) );}) 111 | 112 | struct rb_node 113 | { 114 | unsigned long rb_parent_color; 115 | #define RB_RED 0 116 | #define RB_BLACK 1 117 | struct rb_node *rb_right; 118 | struct rb_node *rb_left; 119 | } __attribute__((aligned(sizeof(long)))); 120 | /* The alignment might seem pointless, but allegedly CRIS needs it */ 121 | 122 | struct rb_root 123 | { 124 | struct rb_node *rb_node; 125 | }; 126 | 127 | 128 | #define rb_parent(r) ((struct rb_node *)((r)->rb_parent_color & ~3)) 129 | #define rb_color(r) ((r)->rb_parent_color & 1) 130 | #define rb_is_red(r) (!rb_color(r)) 131 | #define rb_is_black(r) rb_color(r) 132 | #define rb_set_red(r) do { (r)->rb_parent_color &= ~1; } while (0) 133 | #define rb_set_black(r) do { (r)->rb_parent_color |= 1; } while (0) 134 | 135 | static inline void rb_set_parent(struct rb_node *rb, struct rb_node *p) 136 | { 137 | rb->rb_parent_color = (rb->rb_parent_color & 3) | (unsigned long)p; 138 | } 139 | static inline void rb_set_color(struct rb_node *rb, int color) 140 | { 141 | rb->rb_parent_color = (rb->rb_parent_color & ~1) | color; 142 | } 143 | 144 | #define RB_ROOT (struct rb_root) { NULL, } 145 | #define rb_entry(ptr, type, member) container_of(ptr, type, member) 146 | 147 | #define RB_EMPTY_ROOT(root) ((root)->rb_node == NULL) 148 | #define RB_EMPTY_NODE(node) (rb_parent(node) == node) 149 | #define RB_CLEAR_NODE(node) (rb_set_parent(node, node)) 150 | 151 | static inline void rb_init_node(struct rb_node *rb) 152 | { 153 | rb->rb_parent_color = 0; 154 | rb->rb_right = NULL; 155 | rb->rb_left = NULL; 156 | RB_CLEAR_NODE(rb); 157 | } 158 | 159 | extern void rb_insert_color(struct rb_node *, struct rb_root *); 160 | extern void rb_erase(struct rb_node *, struct rb_root *); 161 | 162 | typedef void (*rb_augment_f)(struct rb_node *node, void *data); 163 | 164 | extern void rb_augment_insert(struct rb_node *node, 165 | rb_augment_f func, void *data); 166 | extern struct rb_node *rb_augment_erase_begin(struct rb_node *node); 167 | extern void rb_augment_erase_end(struct rb_node *node, 168 | rb_augment_f func, void *data); 169 | 170 | /* Find logical next and previous nodes in a tree */ 171 | extern struct rb_node *rb_next(const struct rb_node *); 172 | extern struct rb_node *rb_prev(const struct rb_node *); 173 | extern struct rb_node *rb_first(const struct rb_root *); 174 | extern struct rb_node *rb_last(const struct rb_root *); 175 | 176 | /* Fast replacement of a single node without remove/rebalance/add/rebalance */ 177 | extern void rb_replace_node(struct rb_node *victim, struct rb_node *new, 178 | struct rb_root *root); 179 | 180 | static inline void rb_link_node(struct rb_node * node, struct rb_node * parent, 181 | struct rb_node ** rb_link) 182 | { 183 | node->rb_parent_color = (unsigned long )parent; 184 | node->rb_left = node->rb_right = NULL; 185 | 186 | *rb_link = node; 187 | } 188 | 189 | #endif /* _LLDS_RBTREE_H */ 190 | -------------------------------------------------------------------------------- /tests/rbtree/tests.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include "tests.h" 4 | 5 | void print_resource_usage() { 6 | struct proc_t u; 7 | look_up_our_self(&u); 8 | printf("rss usage: %lu bytes\n", u.rss * __PG_SIZE); 9 | } 10 | -------------------------------------------------------------------------------- /tests/rbtree/tests.h: -------------------------------------------------------------------------------- 1 | #ifndef _LLDS_TEST_H 2 | #define _LLDS_TEST_H 3 | 4 | #include 5 | 6 | /*#define NO_OF_ITEMS 1279000000L*/ 7 | /*#define NO_OF_ITEMS 4020000000L*/ 8 | #define NO_OF_ITEMS 2003009040L 9 | 10 | #define MAX_THREADS 24L 11 | #define __PG_SIZE (sysconf(_SC_PAGESIZE)) 12 | 13 | typedef struct __rb_t_attrs { 14 | int tno; 15 | int mode; 16 | uint64_t items_per_loop; 17 | } rb_t_attrs; 18 | 19 | void print_resource_usage(); 20 | 21 | #endif 22 | -------------------------------------------------------------------------------- /tests/run_test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # certainly not the prettiest script of all time but it stream lined the suite :-) 4 | 5 | for o in `seq 500000000 500000000 2000000000` 6 | do 7 | ~/llds/tests/rbtree/rb_rmtree 8 | 9 | for i in 2 4 8 16 24 10 | do 11 | opcontrol --shutdown 12 | opcontrol --start --vmlinux=/boot/vmlinux --event=CPU_CLK_UNHALTED:10000 13 | opcontrol --reset 14 | echo "userspace w/${i} threads & ${o} items" 15 | free 16 | time ~/llds/tests/rbtree/rb_userspace ${i} ${o} 17 | free 18 | opreport -l > /var/tmp/rb_userspace_cpu_${i}_${o} 19 | opannotate --source --output-dir=/var/tmp/rb_user_cpu_unhalted_${i}_${o} 20 | opannotate --assembly > /var/tmp/rb_user_cpu_unhalted_asm_${i}_${o} 21 | opcontrol --shutdown 22 | opcontrol --start --vmlinux=/boot/vmlinux --event=CPU_CLK_UNHALTED:10000 23 | opcontrol --reset 24 | echo "llds w/${i} threads & ${o} items" 25 | mknod /dev/llds c 834 0 26 | rmmod llds 27 | insmod ~/llds/linux/llds/llds.ko 28 | free 29 | time ~/llds/tests/rbtree/rb_llds ${i} ${o} 30 | free 31 | opreport -l > /var/tmp/rb_llds_cpu_${i}_${o} 32 | opannotate --source --output-dir=/var/tmp/rb_llds_cpu_unhalted_${i}_${o} 33 | opannotate --assembly > /var/tmp/rb_llds_cpu_unhalted_asm_${i}_${o} 34 | ~/llds/tests/rbtree/rb_rmtree 35 | done 36 | 37 | for i in 2 4 8 16 24 38 | do 39 | opcontrol --shutdown 40 | opcontrol --start --vmlinux=/boot/vmlinux -e MEM_LOAD_RETIRED:200000:0x01:1:1 41 | opcontrol --reset 42 | echo "userspace w/${i} threads & ${o} items" 43 | free 44 | time ~/llds/tests/rbtree/rb_userspace ${i} ${o} 45 | free 46 | opreport -l > /var/tmp/rb_userspace_l1_${i}_${o} 47 | opannotate --source --output-dir=/var/tmp/rb_user_mem_retired_l1_${i}_${o} 48 | opannotate --assembly > /var/tmp/rb_user_mem_retired_l1_asm_${i}_${o} 49 | opcontrol --shutdown 50 | opcontrol --start --vmlinux=/boot/vmlinux -e MEM_LOAD_RETIRED:200000:0x01:1:1 51 | opcontrol --reset 52 | echo "llds w/${i} threads & ${o} items" 53 | mknod /dev/llds c 834 0 54 | rmmod llds 55 | insmod ~/llds/linux/llds/llds.ko 56 | free 57 | time ~/llds/tests/rbtree/rb_llds ${i} ${o} 58 | free 59 | opreport -l > /var/tmp/rb_llds_l1_${i}_${o} 60 | opannotate --source --output-dir=/var/tmp/rb_llds_mem_retired_l1_${i}_${o} 61 | opannotate --assembly > /var/tmp/rb_llds_mem_retired_l1_asm_${i}_${o} 62 | ~/llds/tests/rbtree/rb_rmtree 63 | done 64 | 65 | for i in 1 2 4 8 16 24 66 | do 67 | opcontrol --shutdown 68 | opcontrol --start --vmlinux=/boot/vmlinux -e MEM_LOAD_RETIRED:200000:0x02:1:1 69 | opcontrol --reset 70 | echo "userspace w/${i} threads & ${o} items" 71 | free 72 | time ~/llds/tests/rbtree/rb_userspace ${i} ${o} 73 | free 74 | opreport -l > /var/tmp/rb_userspace_l2_${i}_${o} 75 | opannotate --source --output-dir=/var/tmp/rb_user_mem_retired_l2_${i}_${o} 76 | opannotate --assembly > /var/tmp/rb_user_mem_retired_l2_asm_${i}_${o} 77 | opcontrol --shutdown 78 | opcontrol --start --vmlinux=/boot/vmlinux -e MEM_LOAD_RETIRED:200000:0x02:1:1 79 | opcontrol --reset 80 | echo "llds w/${i} threads & ${o} items" 81 | mknod /dev/llds c 834 0 82 | rmmod llds 83 | insmod ~/llds/linux/llds/llds.ko 84 | free 85 | time ~/llds/tests/rbtree/rb_llds ${i} ${o} 86 | free 87 | opreport -l > /var/tmp/rb_llds_l2_${i}_${o} 88 | opannotate --source --output-dir=/var/tmp/rb_llds_mem_retired_l2_${i}_${o} 89 | opannotate --assembly > /var/tmp/rb_llds_mem_retired_l2_asm_${i}_${o} 90 | ~/llds/tests/rbtree/rb_rmtree 91 | done 92 | 93 | for i in 1 2 4 8 16 24 94 | do 95 | opcontrol --shutdown 96 | opcontrol --start --vmlinux=/boot/vmlinux -e MEM_LOAD_RETIRED:200000:0x04:1:1 97 | opcontrol --reset 98 | echo "userspace w/${i} threads & ${o} items" 99 | free 100 | time ~/llds/tests/rbtree/rb_userspace ${i} ${o} 101 | free 102 | opreport -l > /var/tmp/rb_userspace_llc_${i} 103 | opannotate --source --output-dir=/var/tmp/rb_user_mem_retired_llc_${i}_${o} 104 | opannotate --assembly > /var/tmp/rb_user_mem_retired_llc_asm_${i}_${o} 105 | opcontrol --shutdown 106 | opcontrol --start --vmlinux=/boot/vmlinux -e MEM_LOAD_RETIRED:200000:0x04:1:1 107 | opcontrol --reset 108 | echo "llds w/${i} threads & ${o} items" 109 | mknod /dev/llds c 834 0 110 | rmmod llds 111 | insmod ~/llds/linux/llds/llds.ko 112 | free 113 | time ~/llds/tests/rbtree/rb_llds ${i} ${o} 114 | free 115 | opreport -l > /var/tmp/rb_llds_llc_${i}_${o} 116 | opannotate --source --output-dir=/var/tmp/rb_llds_mem_retired_llc_${i}_${o} 117 | opannotate --assembly > /var/tmp/rb_llds_mem_retired_llc_asm_${i}_${o} 118 | ~/llds/tests/rbtree/rb_rmtree 119 | done 120 | 121 | for i in 1 2 4 8 16 24 122 | do 123 | opcontrol --shutdown 124 | opcontrol --start --vmlinux=/boot/vmlinux -e OFFCORE_REQUESTS:100000 125 | opcontrol --reset 126 | echo "userspace w/${i} threads & ${o} items" 127 | free 128 | time ~/llds/tests/rbtree/rb_userspace ${i} ${o} 129 | free 130 | opreport -l > /var/tmp/rb_userspace_offcore_${i}_${o} 131 | opannotate --source --output-dir=/var/tmp/rb_user_offcore_${i}_${o} 132 | opannotate --assembly > /var/tmp/rb_user_offcore_asm_${i}_${o} 133 | opcontrol --shutdown 134 | opcontrol --start --vmlinux=/boot/vmlinux -e OFFCORE_REQUESTS:100000 135 | opcontrol --reset 136 | echo "llds w/${i} threads & ${o} items" 137 | mknod /dev/llds c 834 0 138 | rmmod llds 139 | insmod ~/llds/linux/llds/llds.ko 140 | free 141 | time ~/llds/tests/rbtree/rb_llds ${i} ${o} 142 | free 143 | opreport -l > /var/tmp/rb_llds_offcore_${i}_${o} 144 | opannotate --source --output-dir=/var/tmp/rb_llds_offcore_${i}_${o} 145 | opannotate --assembly > /var/tmp/rb_llds_offcore_asm_${i}_${o} 146 | ~/llds/tests/rbtree/rb_rmtree 147 | done 148 | 149 | for i in 1 2 4 8 16 24 150 | do 151 | opcontrol --shutdown 152 | opcontrol --start --vmlinux=/boot/vmlinux -e L1D_PREFETCH:200000 153 | opcontrol --reset 154 | echo "userspace w/${i} threads & ${o} items" 155 | free 156 | time ~/llds/tests/rbtree/rb_userspace ${i} ${o} 157 | free 158 | opreport -l > /var/tmp/rb_userspace_l1d_pf_${i}_${o} 159 | opannotate --source --output-dir=/var/tmp/rb_user_l1d_prefetch_${i}_${o} 160 | opannotate --assembly > /var/tmp/rb_user_l1d_prefetch_asm_${i}_${o} 161 | opcontrol --shutdown 162 | opcontrol --start --vmlinux=/boot/vmlinux -e L1D_PREFETCH:200000 163 | opcontrol --reset 164 | echo "llds w/${i} threads & ${o} items" 165 | mknod /dev/llds c 834 0 166 | rmmod llds 167 | insmod ~/llds/linux/llds/llds.ko 168 | free 169 | time ~/llds/tests/rbtree/rb_llds ${i} ${o} 170 | free 171 | opreport -l > /var/tmp/rb_llds_l1d_pf_${i}_${o} 172 | opannotate --source --output-dir=/var/tmp/rb_llds_l1d_prefetch_${i}_${o} 173 | opannotate --assembly > /var/tmp/rb_llds_l1d_prefetch_asm_${i}_${o} 174 | ~/llds/tests/rbtree/rb_rmtree 175 | done 176 | done 177 | --------------------------------------------------------------------------------