├── .gitignore ├── Makefile ├── README ├── fifo.c ├── fifo.h ├── main.c └── main_pipe.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | main_futex 3 | main_emulation 4 | main_eventfd 5 | main_efd_nonblock 6 | main_pipe 7 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | #CFLAGS=-O0 -Wall -pedantic -ggdb3 -std=gnu99 3 | CFLAGS=-m32 -flto -O3 -march=native -ggdb3 -DAO_USE_PENTIUM4_INSTRS -std=gnu99 -DFIFO_OVERRIDE -DJUST_MEMCPY 4 | LINK=gcc -m32 -flto -O3 -march=native -ggdb3 5 | 6 | %.o : %.c 7 | gcc $(CFLAGS) -c -o $@ $< 8 | 9 | %.s : %.c 10 | gcc $(CFLAGS) -fverbose-asm -S -o $@ $< 11 | 12 | all : main_futex main_eventfd main_efd_nonblock main_emulation main_pipe 13 | 14 | fifo_eventfd.o : fifo.c fifo.h 15 | gcc -DUSE_EVENTFD=1 $(CFLAGS) -c -o $@ $< 16 | 17 | fifo_efd_nonblock.o : fifo.c fifo.h 18 | gcc -DUSE_EVENTFD=1 -DEVENTFD_NONBLOCKING=1 $(CFLAGS) -c -o $@ $< 19 | 20 | fifo_eventfd_emulation.o : fifo.c fifo.h 21 | gcc -DUSE_EVENTFD=1 -DUSE_EVENTFD_EMULATION=1 $(CFLAGS) -c -o $@ $< 22 | 23 | fifo_eventfd.s : fifo.c fifo.h 24 | gcc -DUSE_EVENTFD=1 $(CFLAGS) -fverbose-asm -S -o $@ $< 25 | 26 | fifo_efd_nonblock.s : fifo.c fifo.h 27 | gcc -DUSE_EVENTFD=1 -DEVENTFD_NONBLOCKING=1 $(CFLAGS) -fverbose-asm -S -o $@ $< 28 | 29 | fifo_eventfd_emulation.s : fifo.c fifo.h 30 | gcc -DUSE_EVENTFD=1 -DUSE_EVENTFD_EMULATION=1 $(CFLAGS) -fverbose-asm -S -o $@ $< 31 | 32 | clean: 33 | rm -f *.o main_futex main_eventfd main_emulation main_pipe main_efd_nonblock 34 | 35 | main.o fifo.o: fifo.h 36 | 37 | main_futex : main.o fifo.o 38 | $(LINK) -o $@ $^ -lpthread 39 | 40 | main_eventfd: main.o fifo_eventfd.o 41 | $(LINK) -o $@ $^ -lpthread 42 | 43 | main_efd_nonblock: main.o fifo_efd_nonblock.o 44 | $(LINK) -o $@ $^ -lpthread 45 | 46 | main_emulation: main.o fifo_eventfd_emulation.o 47 | $(LINK) -o $@ $^ -lpthread 48 | 49 | main_pipe: main_pipe.o 50 | $(LINK) -o $@ $^ -lpthread 51 | -------------------------------------------------------------------------------- /README: -------------------------------------------------------------------------------- 1 | All this code is under public domain. 2 | 3 | This is my old experiment with shared-memory transport. It uses some 4 | linux-specific facilities (futex & eventfd) and compares throughput of 5 | shared memory 'pipe' (zero-copy) versus plain pipe (obviosly, copying). 6 | 7 | You'll need libatomic-ops-dev and recent-enough Linux. But pipe 8 | eventfd emulation should be compile-able on any Unix. And you may need 9 | to tweak Makefile a bit. 10 | 11 | Quite surpisingly, even minimal data processing pretty much negates 12 | speed benefits of zero-copy shared memory transport (remove 13 | JUST_MEMCPY define to see youself). 14 | 15 | Another interesing thing is that old wakeup-via-pipe trick (used by 16 | main_emulation) is not so much slower than efficent eventfd (0.44 17 | seconds vs. 0.33 seconds). 18 | 19 | My results: piping 300000000 (300 megs) of data takes: 20 | 21 | ./main_pipe (plain read & write to/from pipe) 22 | ~0.68 sec 23 | 24 | ./main_emulation (portable eventfd emulation via pipe) 25 | ~0.44 sec 26 | 27 | ./main_futex (futex variant) 28 | ~0.93 sec 29 | 30 | ./main_eventfd (eventfd variant 1) 31 | ~0.40 32 | 33 | ./main_efd_nonblock (eventfd variant 2) 34 | ~0.33 35 | 36 | A bit more elaborate processing of data (generation & verification via 37 | nrand48) gives times around 10 sec, where difference between pipe 38 | implementation doesn't matter that much. 39 | -------------------------------------------------------------------------------- /fifo.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 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 | 16 | #include "fifo.h" 17 | 18 | #define likely(cond) __builtin_expect((cond), 1) 19 | #define unlikely(cond) __builtin_expect((cond), 0) 20 | 21 | #ifndef FIFO_OVERRIDE 22 | 23 | #define USE_EVENTFD 1 24 | #define EVENTFD_NONBLOCKING 0 25 | #define USE_EVENTFD_EMULATION 0 26 | 27 | #endif 28 | 29 | #define NO_LIBC_EVENTFD 30 | 31 | #if USE_EVENTFD_EMULATION && USE_EVENTFD 32 | char *fifo_implementation_type = "eventfd emulation via pipe(2)"; 33 | #elif USE_EVENTFD 34 | #if EVENTFD_NONBLOCKING 35 | char *fifo_implementation_type = "non-blocking eventfd(2)"; 36 | #else 37 | char *fifo_implementation_type = "eventfd(2)"; 38 | #endif 39 | #else 40 | char *fifo_implementation_type = "futex(2)"; 41 | #endif 42 | 43 | 44 | #if USE_EVENTFD 45 | #include 46 | 47 | #if USE_EVENTFD_EMULATION 48 | typedef uint32_t eventfd_t; 49 | 50 | #else /* USE_EVENTFD_EMULATION */ 51 | 52 | #ifdef NO_LIBC_EVENTFD 53 | 54 | typedef uint64_t eventfd_t; 55 | 56 | static 57 | int eventfd(unsigned initval, int flags) 58 | { 59 | return syscall(__NR_eventfd, initval, flags); 60 | } 61 | #else 62 | #include 63 | 64 | #endif /* ! NONLIBC_EVENTFD */ 65 | #endif /* ! USE_EVENTFD_EMULATION */ 66 | #else /* ! USE_EVENTFD */ 67 | #include 68 | #endif 69 | 70 | #define SPIN_COUNT 256 71 | 72 | __attribute__((aligned(64))) 73 | int64_t fifo_reader_exchange_count; 74 | int64_t fifo_writer_wake_count; 75 | int64_t fifo_reader_wait_spins; 76 | int64_t fifo_reader_wait_calls; 77 | 78 | __attribute__((aligned(64))) 79 | int64_t fifo_writer_exchange_count; 80 | int64_t fifo_reader_wake_count; 81 | int64_t fifo_writer_wait_spins; 82 | int64_t fifo_writer_wait_calls; 83 | 84 | #if USE_EVENTFD 85 | static __attribute__((unused)) 86 | int file_flags_change(int fd, int and_mask, int or_mask) 87 | { 88 | int flags; 89 | int err = fcntl(fd, F_GETFL, (long)&flags); 90 | if (err < 0) 91 | return err; 92 | flags = (flags & and_mask) | or_mask; 93 | return fcntl(fd, F_SETFL, (long) &flags); 94 | } 95 | 96 | #if USE_EVENTFD_EMULATION 97 | static 98 | int eventfd_create(struct shm_fifo_eventfd_storage *this) 99 | { 100 | int fds[2]; 101 | int rv; 102 | rv = pipe(fds); 103 | if (rv < 0) 104 | return rv; 105 | this->fd = fds[0]; 106 | this->write_side_fd = fds[1]; 107 | file_flags_change(this->write_side_fd, -1, O_NONBLOCK); 108 | fprintf(stderr, "eventfd_emulation:eventfd_create:success\n"); 109 | return 0; 110 | } 111 | 112 | static 113 | void eventfd_release(struct shm_fifo_eventfd_storage *this) 114 | { 115 | close(this->fd); 116 | close(this->write_side_fd); 117 | } 118 | 119 | #else /* !USE_EVENTFD_EMULATION */ 120 | 121 | static 122 | int eventfd_create(struct shm_fifo_eventfd_storage *this) 123 | { 124 | int fd = eventfd(0, 0); 125 | if (fd < 0) { 126 | perror("eventfd"); 127 | return fd; 128 | } 129 | #if EVENTFD_NONBLOCKING 130 | file_flags_change(fd, -1, O_NONBLOCK); 131 | #endif 132 | this->fd = fd; 133 | fprintf(stderr, "eventfd:eventfd_create:success\n"); 134 | return 0; 135 | } 136 | 137 | static 138 | void eventfd_release(struct shm_fifo_eventfd_storage *this) 139 | { 140 | close(this->fd); 141 | } 142 | #endif /* !USE_EVENTFD_EMULATION */ 143 | #endif /* USE_EVENTFD */ 144 | 145 | int fifo_create(struct shm_fifo **ptr) 146 | { 147 | int err = posix_memalign((void **)ptr, 4096, FIFO_TOTAL_SIZE); 148 | struct shm_fifo *fifo = *ptr; 149 | if (!err) { 150 | memset(fifo, 0, offsetof(struct shm_fifo, data)); 151 | #if USE_EVENTFD 152 | int rv; 153 | rv = eventfd_create(&fifo->head_eventfd); 154 | if (rv) 155 | goto out_free; 156 | rv = eventfd_create(&fifo->tail_eventfd); 157 | if (rv) { 158 | eventfd_release(&fifo->head_eventfd); 159 | out_free: 160 | free(fifo); 161 | return 0; 162 | } 163 | #else 164 | fprintf(stderr, "fifo_create: using futex implementation\n"); 165 | #endif // USE_EVENTFD 166 | } 167 | fifo->head_wait = fifo->tail_wait = 0xffffffff; 168 | 169 | return err; 170 | } 171 | 172 | static 173 | int common_fifo_window_init(struct shm_fifo *fifo, struct fifo_window *window, 174 | unsigned min_length, unsigned pull_length, int reader) 175 | { 176 | window->fifo = fifo; 177 | window->reader = reader; 178 | window->len = 0; 179 | if (min_length > pull_length) 180 | pull_length = min_length; 181 | window->min_length = min_length; 182 | window->pull_length = pull_length; 183 | return 0; 184 | } 185 | 186 | int fifo_window_init_reader(struct shm_fifo *fifo, struct fifo_window *window, 187 | unsigned min_length, unsigned pull_length) 188 | { 189 | window->start = fifo->tail % FIFO_SIZE; 190 | return common_fifo_window_init(fifo, window, min_length, pull_length, 1); 191 | } 192 | 193 | int fifo_window_init_writer(struct shm_fifo *fifo, struct fifo_window *window, 194 | unsigned min_length, unsigned pull_length) 195 | { 196 | window->start = fifo->head % FIFO_SIZE; 197 | return common_fifo_window_init(fifo, window, min_length, pull_length, 0); 198 | } 199 | 200 | #if USE_EVENTFD && USE_EVENTFD_EMULATION 201 | static 202 | void eventfd_wait(struct shm_fifo_eventfd_storage *eventfd, unsigned *addr, unsigned wait_value) 203 | { 204 | AO_nop_full(); 205 | if (*addr != wait_value) 206 | return; 207 | eventfd_t buf[8]; 208 | int rv; 209 | again: 210 | rv = read(eventfd->fd, buf, sizeof(buf)); 211 | if (rv < 0) { 212 | if (errno == EINTR) 213 | goto again; 214 | perror("eventfd_wait:read"); 215 | abort(); 216 | } 217 | } 218 | 219 | static 220 | void eventfd_wake(struct shm_fifo_eventfd_storage *eventfd) 221 | { 222 | eventfd_t value = 1; 223 | write(eventfd->write_side_fd, &value, sizeof(value)); 224 | } 225 | 226 | #elif USE_EVENTFD 227 | static 228 | void eventfd_wait(struct shm_fifo_eventfd_storage *eventfd, unsigned *addr, unsigned wait_value) 229 | { 230 | AO_nop_full(); 231 | if (*addr != wait_value) 232 | return; 233 | int fd = eventfd->fd; 234 | #if EVENTFD_NONBLOCKING 235 | struct pollfd wait; 236 | wait.fd = fd; 237 | wait.events = POLLIN; 238 | poll(&wait, 1, -1); 239 | #endif 240 | eventfd_t tmp; 241 | read(fd, &tmp, sizeof(eventfd_t)); 242 | } 243 | 244 | static 245 | void eventfd_wake(struct shm_fifo_eventfd_storage *eventfd) 246 | { 247 | eventfd_t value = 1; 248 | write(eventfd->fd, &value, sizeof(value)); 249 | } 250 | 251 | #else /* !USE_EVENTFD */ 252 | static 253 | int futex(int *uaddr, int op, int val, const struct timespec *timeout, 254 | int *uaddr2, int val3) 255 | { 256 | return syscall(__NR_futex, uaddr, op, val, timeout, uaddr2, val3); 257 | } 258 | 259 | static 260 | void futex_wait(void *addr, unsigned value) 261 | { 262 | int rv; 263 | do { 264 | rv = futex(addr, FUTEX_WAIT | FUTEX_PRIVATE_FLAG, value, 0, 0, 0); 265 | } while (rv && errno == EINTR); 266 | if (rv) { 267 | if (errno == EWOULDBLOCK) 268 | return; 269 | perror("futex_wait"); 270 | exit(1); 271 | } 272 | } 273 | 274 | static 275 | void futex_wake(void *addr) 276 | { 277 | int rv; 278 | rv = futex(addr, FUTEX_WAKE | FUTEX_PRIVATE_FLAG, 1, 0, 0, 0); 279 | if (rv < 0) { 280 | perror("futex_wake"); 281 | exit(1); 282 | } 283 | } 284 | #endif 285 | 286 | void fifo_window_reader_wait(struct fifo_window *window) 287 | { 288 | struct shm_fifo *fifo = window->fifo; 289 | unsigned count; 290 | unsigned tail; 291 | unsigned head; 292 | 293 | if (!window->reader) 294 | abort(); 295 | 296 | tail = fifo->tail; 297 | head = fifo->head; 298 | if (head - tail != window->len) 299 | return; 300 | 301 | fifo_reader_wait_calls++; 302 | 303 | for (count = SPIN_COUNT; count > 0; count--) { 304 | AO_nop_full(); 305 | if (fifo->head != head) { 306 | fifo_reader_wait_spins += SPIN_COUNT - count + 1; 307 | return; 308 | } 309 | } 310 | 311 | fifo_reader_wait_spins += SPIN_COUNT - count; 312 | 313 | fifo->head_wait = head; 314 | do { 315 | #if USE_EVENTFD 316 | eventfd_wait(&fifo->head_eventfd, &fifo->head, head); 317 | #else 318 | futex_wait(&fifo->head, head); 319 | #endif 320 | } while (head == fifo->head); 321 | } 322 | 323 | void fifo_window_writer_wait(struct fifo_window *window) 324 | { 325 | struct shm_fifo *fifo = window->fifo; 326 | unsigned count; 327 | unsigned tail; 328 | unsigned head; 329 | 330 | if (window->reader) 331 | abort(); 332 | 333 | tail = fifo->tail; 334 | head = fifo->head; 335 | if (tail + FIFO_SIZE - head != window->len) 336 | return; 337 | 338 | fifo_writer_wait_calls++; 339 | 340 | for (count = SPIN_COUNT; count > 0; count--) { 341 | AO_nop_full(); 342 | if (fifo->tail != tail) { 343 | fifo_writer_wait_spins += SPIN_COUNT - count + 1; 344 | return; 345 | } 346 | } 347 | fifo_writer_wait_spins += SPIN_COUNT - count; 348 | fifo->tail_wait = tail; 349 | do { 350 | #if USE_EVENTFD 351 | eventfd_wait(&fifo->tail_eventfd, &fifo->tail, tail); 352 | #else 353 | futex_wait(&fifo->tail, tail); 354 | #endif 355 | } while (tail == fifo->tail); 356 | } 357 | 358 | static 359 | void shm_fifo_notify_reader(struct shm_fifo *fifo, unsigned old_head) 360 | { 361 | AO_nop_full(); 362 | if (fifo->head_wait == old_head) { 363 | fifo_reader_wake_count++; 364 | #if USE_EVENTFD 365 | eventfd_wake(&fifo->head_eventfd); 366 | #else 367 | futex_wake(&fifo->head); 368 | #endif 369 | } 370 | } 371 | 372 | static 373 | void shm_fifo_notify_writer(struct shm_fifo *fifo, unsigned old_tail) 374 | { 375 | AO_nop_full(); 376 | if (fifo->tail_wait == old_tail) { 377 | fifo_writer_wake_count++; 378 | #if USE_EVENTFD 379 | eventfd_wake(&fifo->tail_eventfd); 380 | #else 381 | futex_wake(&fifo->tail); 382 | #endif 383 | } 384 | } 385 | 386 | static 387 | void fifo_notify_invalid_window(struct fifo_window *window, int reader) 388 | { 389 | fprintf(stderr, "window %p (reader = %d) (arg-reader = %d) is invalid. len = %u\n", (void *)window, window->reader, reader, window->len); 390 | __builtin_trap(); 391 | } 392 | 393 | static inline 394 | unsigned check_window_free_count(struct fifo_window *window, unsigned old_start, int reader) 395 | { 396 | unsigned start = window->start; 397 | unsigned free_count = start - old_start; 398 | if (unlikely(free_count > FIFO_SIZE)) { 399 | fifo_notify_invalid_window(window, reader); 400 | abort(); 401 | } 402 | return free_count; 403 | } 404 | 405 | void fifo_window_exchange_reader(struct fifo_window *window) 406 | { 407 | unsigned len; 408 | again: 409 | len = window->len; 410 | struct shm_fifo *fifo = window->fifo; 411 | unsigned tail = fifo->tail; 412 | unsigned free_count = check_window_free_count(window, tail, 1); 413 | unsigned old_tail = tail; 414 | 415 | tail += free_count; 416 | fifo->tail = tail; 417 | window->start = tail; 418 | 419 | if (len < window->pull_length) 420 | len = window->len = fifo->head - tail; 421 | 422 | if (len > FIFO_SIZE) { 423 | fifo_notify_invalid_window(window, 1); 424 | fifo->tail = fifo->head; 425 | window->len = 0; 426 | window->start = tail % FIFO_SIZE; 427 | } 428 | 429 | shm_fifo_notify_writer(fifo, old_tail); 430 | 431 | if (unlikely(len < window->min_length)) { 432 | fifo_window_reader_wait(window); 433 | goto again; 434 | } 435 | 436 | fifo_reader_exchange_count++; 437 | } 438 | 439 | void fifo_window_exchange_writer(struct fifo_window *window) 440 | { 441 | unsigned len; 442 | again: 443 | len = window->len; 444 | struct shm_fifo *fifo = window->fifo; 445 | unsigned head = fifo->head; 446 | unsigned free_count = check_window_free_count(window, head, 0); 447 | unsigned old_head = head; 448 | 449 | head += free_count; 450 | fifo->head = head; 451 | window->start = head; 452 | 453 | if (len < window->pull_length) 454 | len = window->len = fifo->tail + FIFO_SIZE - head; 455 | 456 | if (len > FIFO_SIZE) { 457 | fifo_notify_invalid_window(window, 0); 458 | fifo->head = fifo->tail; 459 | window->len = FIFO_SIZE; 460 | window->start = head % FIFO_SIZE; 461 | } 462 | 463 | shm_fifo_notify_reader(fifo, old_head); 464 | 465 | if (unlikely(len < window->min_length)) { 466 | fifo_window_writer_wait(window); 467 | goto again; 468 | } 469 | 470 | fifo_writer_exchange_count++; 471 | } 472 | -------------------------------------------------------------------------------- /fifo.h: -------------------------------------------------------------------------------- 1 | #ifndef SHM_FIFO_H 2 | #define SHM_FIFO_H 3 | #include 4 | 5 | struct shm_fifo_eventfd_storage { 6 | int fd; 7 | int write_side_fd; 8 | }; 9 | 10 | struct shm_fifo { 11 | unsigned head; 12 | unsigned head_wait; 13 | struct shm_fifo_eventfd_storage head_eventfd; 14 | 15 | __attribute__((aligned(128))) 16 | unsigned tail; 17 | unsigned tail_wait; 18 | struct shm_fifo_eventfd_storage tail_eventfd; 19 | 20 | __attribute__((aligned(128))) 21 | char data[0]; 22 | }; 23 | 24 | /* fifo_window reflects portion of fifo currently owned by reader or 25 | * writer. Start of window can be advanced by fifo_window_eat_span 26 | * (but note it won't be passed to reader/writer until next exchange 27 | * call). Actual pointer is retrieved by calling either 28 | * fifo_window_peek_span or fifo_window_get_span */ 29 | struct fifo_window { 30 | struct shm_fifo *fifo; 31 | unsigned start, len; 32 | unsigned min_length, pull_length; 33 | int reader; 34 | }; 35 | 36 | #define FIFO_TOTAL_SIZE (65536+sizeof(struct shm_fifo)) 37 | 38 | #define FIFO_SIZE (FIFO_TOTAL_SIZE-sizeof(struct shm_fifo)) 39 | 40 | /* returns pointer and length of linear portion of window that's 41 | * available for consuming or producing */ 42 | static inline 43 | void *fifo_window_peek_span(struct fifo_window *window, unsigned *span_len) 44 | { 45 | unsigned start = window->start % FIFO_SIZE; 46 | char *p = &(window->fifo->data[start]); 47 | if (span_len) { 48 | unsigned len = window->len; 49 | unsigned end = start + len; 50 | end = (end > FIFO_SIZE) ? FIFO_SIZE : end; 51 | len = end - start; 52 | *span_len = len; 53 | } 54 | return p; 55 | } 56 | 57 | /* advanced start of window by span_len. I.e. next call to peek will 58 | * point to byte after span_len in window. Note, that 59 | * produced/consumed portion released through this call _will not be_ 60 | * passed back to fifo until next call to 61 | * fifo_window_exchange_{writer,reader} */ 62 | static inline 63 | void fifo_window_eat_span(struct fifo_window *window, unsigned span_len) 64 | { 65 | unsigned start = window->start + span_len; 66 | window->start = start; 67 | window->len -= span_len; 68 | } 69 | 70 | /* combines peek and eat in convenient single call. Note: next call to 71 | * fifo_window_exchange_{reader,writer} will pass data returned by 72 | * this call to fifo, essentially invalidating it */ 73 | static inline 74 | void *fifo_window_get_span(struct fifo_window *window, unsigned *span_len) 75 | { 76 | unsigned len; 77 | void *rv; 78 | rv = fifo_window_peek_span(window, &len); 79 | fifo_window_eat_span(window, len); 80 | if (span_len) 81 | *span_len = len; 82 | return rv; 83 | } 84 | 85 | extern int64_t fifo_reader_exchange_count; 86 | extern int64_t fifo_writer_exchange_count; 87 | extern int64_t fifo_reader_wake_count; 88 | extern int64_t fifo_writer_wake_count; 89 | extern int64_t fifo_reader_wait_spins; 90 | extern int64_t fifo_writer_wait_spins; 91 | extern int64_t fifo_reader_wait_calls; 92 | extern int64_t fifo_writer_wait_calls; 93 | 94 | int fifo_create(struct shm_fifo **ptr); 95 | 96 | /* inits window. min_length arg is size of window below which it'll 97 | * automatically wait for more in exchange call. pull_length arg is 98 | * size of window below which it'll attempt to grab all available 99 | * data/free-space in exchange call */ 100 | int fifo_window_init_reader(struct shm_fifo *fifo, 101 | struct fifo_window *window, 102 | unsigned min_length, 103 | unsigned pull_length); 104 | 105 | int fifo_window_init_writer(struct shm_fifo *fifo, 106 | struct fifo_window *window, 107 | unsigned min_length, 108 | unsigned pull_length); 109 | 110 | /* waits until more data or space is available for consuming or 111 | * producing. Note: it won't actually advance len of window, it has to 112 | * be done via call to exchange below */ 113 | void fifo_window_reader_wait(struct fifo_window *window); 114 | void fifo_window_writer_wait(struct fifo_window *window); 115 | 116 | /* releases "eaten" (i.e. consumed by consumer or produced by 117 | * producer) portion of window back to fifo and (depending on window 118 | * pull_length and min_length options) gets fresh data/free-space from 119 | * fifo */ 120 | void fifo_window_exchange_writer(struct fifo_window *window); 121 | void fifo_window_exchange_reader(struct fifo_window *window); 122 | 123 | extern char *fifo_implementation_type; 124 | 125 | #endif 126 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #include "fifo.h" 14 | 15 | #ifdef JUST_MEMCPY 16 | #define nrand48(dummy) 0 17 | #endif 18 | 19 | #define READER_BATCH 1024 20 | #define WRITER_BATCH 1024 21 | #define SEND_WORDS 2000000000U 22 | 23 | static 24 | struct shm_fifo *fifo; 25 | 26 | static 27 | int setaffinity; 28 | static 29 | int serialize; 30 | 31 | #define SERIALIZE 0 32 | 33 | static 34 | sem_t reader_sem; 35 | static 36 | sem_t writer_sem; 37 | 38 | int done_flag; 39 | 40 | static 41 | void fatal_perror(char *arg) 42 | { 43 | perror(arg); 44 | exit(1); 45 | } 46 | 47 | static 48 | pid_t gettid(void) 49 | { 50 | return syscall(__NR_gettid); 51 | } 52 | 53 | static 54 | void move_to_cpu(int number) 55 | { 56 | pid_t tid = gettid(); 57 | cpu_set_t set; 58 | int rv; 59 | 60 | CPU_ZERO(&set); 61 | CPU_SET(number, &set); 62 | rv = sched_setaffinity(tid, sizeof(set), &set); 63 | if (rv < 0) 64 | fatal_perror("sched_setaffinity"); 65 | } 66 | 67 | 68 | static 69 | void *reader_thread(void *dummy) 70 | { 71 | struct fifo_window window; 72 | unsigned long long count=0; 73 | int sum=0; 74 | unsigned short xsubi[3]; 75 | memset(xsubi, 0, sizeof(xsubi)); 76 | 77 | printf("reader's pid is %d\n", gettid()); 78 | if (setaffinity) 79 | move_to_cpu(0); 80 | 81 | if (serialize) 82 | sem_wait(&reader_sem); 83 | 84 | fifo_window_init_reader(fifo, &window, 0, READER_BATCH*sizeof(int)*2); 85 | while (1) { 86 | int *ptr; 87 | unsigned len, i; 88 | 89 | if (serialize) { 90 | sem_post(&writer_sem); 91 | sem_wait(&reader_sem); 92 | } 93 | 94 | fifo_window_exchange_reader(&window); 95 | 96 | if (window.len == 0) { 97 | if (done_flag) 98 | break; 99 | fifo_window_reader_wait(&window); 100 | continue; 101 | } 102 | 103 | ptr = fifo_window_peek_span(&window, &len); 104 | len /= sizeof(int); 105 | if (len > READER_BATCH) 106 | len = READER_BATCH; 107 | fifo_window_eat_span(&window, len*sizeof(int)); 108 | 109 | for (i = 0; i < len; i++) 110 | sum |= *ptr++ ^ nrand48(xsubi); 111 | count += i; 112 | } 113 | printf("sum = 0x%08x\ncount = %lld\n", sum, count); 114 | return (void *)(intptr_t)sum; 115 | } 116 | 117 | 118 | static 119 | void *writer_thread(void *dummy) 120 | { 121 | struct fifo_window window; 122 | unsigned count = 0; 123 | unsigned short xsubi[3]; 124 | memset(xsubi, 0, sizeof(xsubi)); 125 | 126 | printf("writer's pid is %d\n", gettid()); 127 | if (setaffinity) 128 | move_to_cpu(1); 129 | 130 | if (serialize) 131 | sem_wait(&writer_sem); 132 | 133 | fifo_window_init_writer(fifo, &window, 4, WRITER_BATCH*sizeof(int)*2); 134 | while (count < SEND_WORDS) { 135 | int *ptr; 136 | unsigned len, i; 137 | fifo_window_exchange_writer(&window); 138 | 139 | if (serialize) { 140 | sem_post(&reader_sem); 141 | sem_wait(&writer_sem); 142 | } 143 | 144 | ptr = fifo_window_peek_span(&window, &len); 145 | len /= sizeof(int); 146 | if (len > WRITER_BATCH) 147 | len = WRITER_BATCH; 148 | count += len; 149 | if (count > SEND_WORDS) { 150 | count -= len; 151 | len = SEND_WORDS - count; 152 | count += len; 153 | } 154 | fifo_window_eat_span(&window, len*sizeof(int)); 155 | for (i=0;i= 0) { 188 | switch (optchar) { 189 | case 'a': 190 | setaffinity = 1; 191 | break; 192 | case 's': 193 | serialize = 1; 194 | break; 195 | default: 196 | usage(argv); 197 | exit(1); 198 | } 199 | } 200 | 201 | printf("FIFO_SIZE = %d\n", FIFO_SIZE); 202 | printf("sizeof(struct shm_fifo) = %d\n", sizeof(struct shm_fifo)); 203 | 204 | if (serialize) { 205 | rv = sem_init(&reader_sem, 0, 0); 206 | if (rv) 207 | fatal_perror("sem_init(&reader_sem,...)"); 208 | rv = sem_init(&writer_sem, 0, 0); 209 | if (rv) 210 | fatal_perror("sem_init(&writer_sem,...)"); 211 | } 212 | 213 | rv = fifo_create(&fifo); 214 | if (rv) 215 | fatal_perror("fifo_create"); 216 | 217 | rv = pthread_create(&reader, 0, reader_thread, 0); 218 | if (rv) 219 | fatal_perror("phread_create(&reader)"); 220 | 221 | rv = pthread_create(&writer, 0, writer_thread, 0); 222 | if (rv) 223 | fatal_perror("pthread_create(&writer)"); 224 | 225 | if (serialize) 226 | sem_post(&writer_sem); 227 | 228 | pthread_join(reader, 0); 229 | pthread_join(writer, 0); 230 | 231 | printf("fifo_writer_exchange_count = %d\n", fifo_writer_exchange_count); 232 | printf("fifo_writer_wake_count = %d\n", fifo_writer_wake_count); 233 | printf("fifo_reader_exchange_count = %d\n", fifo_reader_exchange_count); 234 | printf("fifo_reader_wake_count = %d\n", fifo_reader_wake_count); 235 | printf("fifo_reader_wait_spins = %d\n", fifo_reader_wait_spins); 236 | printf("fifo_writer_wait_spins = %d\n", fifo_writer_wait_spins); 237 | printf("fifo_reader_wait_calls = %d\n", fifo_reader_wait_calls); 238 | printf("fifo_writer_wait_calls = %d\n", fifo_writer_wait_calls); 239 | 240 | return 0; 241 | } 242 | -------------------------------------------------------------------------------- /main_pipe.c: -------------------------------------------------------------------------------- 1 | #define _GNU_SOURCE 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | #ifdef JUST_MEMCPY 14 | #define nrand48(dummy) 0 15 | #endif 16 | 17 | static 18 | int setaffinity; 19 | 20 | #define BUFFERSIZE 32768 21 | #define SEND_BYTES 2000000000U 22 | 23 | int reader_buffer[BUFFERSIZE/sizeof(int)]; 24 | int writer_buffer[BUFFERSIZE/sizeof(int)]; 25 | 26 | int read_fd, write_fd; 27 | 28 | static 29 | void fatal_perror(char *arg) 30 | { 31 | perror(arg); 32 | exit(1); 33 | } 34 | 35 | static 36 | pid_t gettid(void) 37 | { 38 | return syscall(__NR_gettid); 39 | } 40 | 41 | static 42 | void move_to_cpu(int number) 43 | { 44 | pid_t tid = gettid(); 45 | cpu_set_t set; 46 | int rv; 47 | 48 | CPU_ZERO(&set); 49 | CPU_SET(number, &set); 50 | rv = sched_setaffinity(tid, sizeof(set), &set); 51 | if (rv < 0) 52 | fatal_perror("sched_setaffinity"); 53 | } 54 | 55 | static 56 | void *reader_thread(void *dummy) 57 | { 58 | unsigned long long count=0; 59 | int sum=0; 60 | unsigned short xsubi[3]; 61 | memset(xsubi, 0, sizeof(xsubi)); 62 | 63 | printf("reader's pid is %d\n", gettid()); 64 | if (setaffinity) 65 | move_to_cpu(0); 66 | 67 | while (1) { 68 | unsigned len, i; 69 | 70 | len = read(read_fd, reader_buffer, BUFFERSIZE); 71 | if (len == 0) 72 | break; 73 | 74 | len /= sizeof(int); 75 | 76 | for (i = 0; i < len; i++, count++) 77 | sum |= reader_buffer[i] ^ nrand48(xsubi); 78 | } 79 | printf("sum = 0x%08x\ncount = %lld\n", sum, count); 80 | return (void *)(intptr_t)sum; 81 | } 82 | 83 | static 84 | void *writer_thread(void *dummy) 85 | { 86 | unsigned count = 0; 87 | unsigned short xsubi[3]; 88 | memset(xsubi, 0, sizeof(xsubi)); 89 | 90 | printf("writer's pid is %d\n", gettid()); 91 | if (setaffinity) 92 | move_to_cpu(1); 93 | 94 | while (count < SEND_BYTES) { 95 | unsigned len, i; 96 | 97 | len = BUFFERSIZE/sizeof(int); 98 | len = (len > SEND_BYTES - count) ? SEND_BYTES - count : len; 99 | count += len; 100 | for (i=0;i 0) { 104 | len = write(write_fd, writer_buffer, i); 105 | if (len < 0) { 106 | if (errno == EINTR) 107 | continue; 108 | fatal_perror("write"); 109 | } 110 | i -= len; 111 | } 112 | } 113 | close(write_fd); 114 | return 0; 115 | } 116 | 117 | static 118 | char *usage_text = 119 | "Usage: %s [options]\n" 120 | "Benchmark in-kernel pipe fifo implementation.\n" 121 | " -a\tset affinity for dual- core or CPU machine\n" 122 | "\n"; 123 | 124 | static 125 | void usage(char **argv) 126 | { 127 | fprintf(stderr, usage_text, argv[0]); 128 | } 129 | 130 | int main(int argc, char **argv) 131 | { 132 | int rv; 133 | pthread_t reader, writer; 134 | int pipes[2]; 135 | int optchar; 136 | 137 | while ((optchar = getopt(argc, argv, "a")) >= 0) { 138 | switch (optchar) { 139 | case 'a': 140 | setaffinity = 1; 141 | break; 142 | default: 143 | usage(argv); 144 | exit(1); 145 | } 146 | } 147 | 148 | rv = pipe(pipes); 149 | if (rv) 150 | fatal_perror("pipe"); 151 | read_fd = pipes[0]; 152 | write_fd = pipes[1]; 153 | 154 | rv = pthread_create(&reader, 0, reader_thread, 0); 155 | if (rv) 156 | fatal_perror("phread_create(&reader)"); 157 | 158 | rv = pthread_create(&writer, 0, writer_thread, 0); 159 | if (rv) 160 | fatal_perror("pthread_create(&writer)"); 161 | 162 | pthread_join(reader, 0); 163 | pthread_join(writer, 0); 164 | 165 | return 0; 166 | } 167 | --------------------------------------------------------------------------------