├── 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 | Threads | # of Items | userspace | llds | llds improvement |
74 | 2 | 500000000 | 3564 | 1761 | 2.02x |
75 | 16 | 1500000000 | 9291 | 4112 | 2.26x |
76 | 24 | 2000000000 | 12645 | 5670 | 2.23x |
77 |
78 |
79 | Unhalted CPU cycles (10000 cycles @ 133mHz)
80 | -----------------------------------------
81 |
82 | Threads | # of Items | userspace | llds |
83 | 2 | 500000000 | 87418776 | 377458531 |
84 | 16 | 1500000000 | 279203932 | 5107099682 |
85 | 24 | 2000000000 | 968091233 | 5529234102 |
86 |
87 |
88 | L1 cache hits (200000 per sample)
89 | ----------------------------------
90 |
91 | Threads | # of Items | userspace | llds | llds improvement |
92 | 2 | 500000000 | 3077671 | 5502292 | 1.78x |
93 | 16 | 1500000000 | 15120921 | 27231553 | 1.80x |
94 | 24 | 2000000000 | 23746988 | 39196177 | 1.65x |
95 |
96 |
97 | L2 cache hits (200000 per sample)
98 | ----------------------------------
99 |
100 | Threads | # of Items | userspace | llds | llds improvement |
101 | 2 | 500000000 | 21866 | 60214 | 2.75x |
102 | 16 | 1500000000 | 82101 | 511285 | 6.23x |
103 | 24 | 2000000000 | 127072 | 800846 | 6.30x |
104 |
105 |
106 | L3/Last-Level cache hits (200000 per sample)
107 | ---------------------------------------------
108 |
109 | Threads | # of Items | userspace | llds | llds improvement |
110 | 2 | 500000000 | 26069 | 32259 | 1.24x |
111 | 16 | 1500000000 | 148827 | 254562 | 1.71x |
112 | 24 | 2000000000 | 270191 | 341649 | 1.26x |
113 |
114 |
115 | L1 Data Prefetch misses (200000 per *hardware* sample)
116 | ---------------------------------------------
117 |
118 | Threads | # of Items | userspace | llds | llds improvement |
119 | 2 | 500000000 | 52396 | 21113 | 2.48x |
120 | 16 | 1500000000 | 350753 | 120891 | 2.90x |
121 | 24 | 2000000000 | 544791 | 210268 | 2.59x |
122 |
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 |
--------------------------------------------------------------------------------