From 9e8bd36ec6e2bd963dd9db3de3a0034bc4f90810 Mon Sep 17 00:00:00 2001 From: Havoc Pennington Date: Sun, 29 Aug 2010 00:15:09 -0400 Subject: [PATCH 2/2] add hacky benchmark to compare dynamic and traditional request marshaling Benchmark includes the marshaling and xcb_send_request() but not writing to socket. Relies on a very unshippable hack to xcb_in.c to let us avoid writing to the socket. This is not a patch that should ever go in the real source tree, obviously. --- src/Makefile.am | 5 + src/benchmark-requests.c | 350 ++++++++++++++++++++++++++++++++++++++++++++++ src/xcb_out.c | 14 ++- 3 files changed, 368 insertions(+), 1 deletions(-) create mode 100644 src/benchmark-requests.c diff --git a/src/Makefile.am b/src/Makefile.am index 2606e0e..23f617a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -219,3 +219,8 @@ CLEANFILES = $(EXTSOURCES) $(EXTHEADERS) $(EXTSOURCES): c_client.py $(PYTHON) $(srcdir)/c_client.py -p $(XCBPROTO_XCBPYTHONDIR) $(XCBPROTO_XCBINCLUDEDIR)/$(@:.c=.xml) + +noinst_PROGRAMS = benchmark-requests + +benchmark_requests_SOURCES = benchmark-requests.c +benchmark_requests_LDADD = libxcb.la diff --git a/src/benchmark-requests.c b/src/benchmark-requests.c new file mode 100644 index 0000000..d287896 --- /dev/null +++ b/src/benchmark-requests.c @@ -0,0 +1,350 @@ +/* -*- mode: C; c-file-style: "k&r"; c-basic-offset: 4; indent-tabs-mode: nil; -*- */ +/* + * Copyright (C) 2010 Havoc Pennington + * All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the "Software"), + * to deal in the Software without restriction, including without limitation + * the rights to use, copy, modify, merge, publish, distribute, sublicense, + * and/or sell copies of the Software, and to permit persons to whom the + * Software is furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in + * all copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN + * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the names of the authors or their + * institutions shall not be used in advertising or otherwise to promote the + * sale, use or other dealings in this Software without prior written + * authorization from the authors. + */ +#include +#include +#include +#include +#include + +#include "xcbdyn.h" +#include "xproto.h" + +extern int (* _xcb_write_block) (xcb_connection_t *c, struct iovec *vector, int count); + +static int (* saved_write_block) (xcb_connection_t *c, struct iovec *vector, int count); + +static int +noop_write_block(xcb_connection_t *c, struct iovec *vector, int count) +{ + return 1; +} + +static unsigned char write_block_bytes[512]; +static size_t write_block_bytes_len; + +static int +memory_write_block(xcb_connection_t *c, struct iovec *vector, int count) +{ + while(count && write_block_bytes_len + vector[0].iov_len <= sizeof(write_block_bytes)) + { + memcpy(write_block_bytes + write_block_bytes_len, vector[0].iov_base, vector[0].iov_len); + write_block_bytes_len += vector[0].iov_len; + vector[0].iov_base = (char *) vector[0].iov_base + vector[0].iov_len; + vector[0].iov_len = 0; + ++vector, --count; + } + + return 1; +} + +static uint32_t +time_ms(void) +{ + struct timeval tv; + + gettimeofday (&tv, NULL); + + return tv.tv_sec * 1000 + tv.tv_usec / 1000; +} + +typedef xcb_void_cookie_t (* CreateWindowFunc) (xcb_connection_t *c /**< */, + uint8_t depth /**< */, + xcb_window_t wid /**< */, + xcb_window_t parent /**< */, + int16_t x /**< */, + int16_t y /**< */, + uint16_t width /**< */, + uint16_t height /**< */, + uint16_t border_width /**< */, + uint16_t _class /**< */, + xcb_visualid_t visual /**< */, + uint32_t value_mask /**< */, + const uint32_t *value_list /**< */); + +__attribute__((noinline)) +xcb_void_cookie_t xcb_dyn_create_window (xcb_connection_t *c /**< */, + uint8_t depth /**< */, + xcb_window_t wid /**< */, + xcb_window_t parent /**< */, + int16_t x /**< */, + int16_t y /**< */, + uint16_t width /**< */, + uint16_t height /**< */, + uint16_t border_width /**< */, + uint16_t _class /**< */, + xcb_visualid_t visual /**< */, + uint32_t value_mask /**< */, + const uint32_t *value_list /**< */); +__attribute__((noinline)) +xcb_void_cookie_t xcb_std_create_window (xcb_connection_t *c /**< */, + uint8_t depth /**< */, + xcb_window_t wid /**< */, + xcb_window_t parent /**< */, + int16_t x /**< */, + int16_t y /**< */, + uint16_t width /**< */, + uint16_t height /**< */, + uint16_t border_width /**< */, + uint16_t _class /**< */, + xcb_visualid_t visual /**< */, + uint32_t value_mask /**< */, + const uint32_t *value_list /**< */); + +#define xcb_generate_id(c) 0xdeadbeef + +static void +time_create_window(const char *description, + CreateWindowFunc func) +{ + xcb_connection_t *c; + const xcb_setup_t *setup; + xcb_screen_iterator_t screen_iter; + xcb_screen_t *screen; + int i; + uint32_t start, end; + + c = xcb_connect(0, 0); + setup = xcb_get_setup(c); + screen_iter = xcb_setup_roots_iterator(setup); + screen = screen_iter.data; + + /* now take away xcb's actual writing to the socket */ + _xcb_write_block = noop_write_block; + +#define N_ITERATIONS 2000000 + + start = time_ms(); + for (i = 0; i < N_ITERATIONS; ++i) + { + xcb_window_t window = xcb_generate_id(c); + uint32_t mask = XCB_CW_EVENT_MASK; + uint32_t values[1] = { XCB_EVENT_MASK_EXPOSURE }; + xcb_void_cookie_t cookie; + cookie = (*func) (c, + XCB_COPY_FROM_PARENT, + window, + screen->root, + 0, 0, + 150, 150, + 10, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, + mask, values); + } + end = time_ms(); + + printf("%s: %d ms for %d iterations\n", + description, + end - start, N_ITERATIONS); + + _xcb_write_block = saved_write_block; + + xcb_disconnect(c); +} + +static void +get_create_window_bytes(CreateWindowFunc func, unsigned char **bytes_p, size_t *len_p) +{ + xcb_connection_t *c; + const xcb_setup_t *setup; + xcb_screen_iterator_t screen_iter; + xcb_screen_t *screen; + int i; + uint32_t start, end; + + c = xcb_connect(0, 0); + setup = xcb_get_setup(c); + screen_iter = xcb_setup_roots_iterator(setup); + screen = screen_iter.data; + + /* intercept xcb's writing to socket */ + write_block_bytes_len = 0; + _xcb_write_block = memory_write_block; + + + { + xcb_window_t window = xcb_generate_id(c); + uint32_t mask = XCB_CW_EVENT_MASK; + uint32_t values[1] = { XCB_EVENT_MASK_EXPOSURE }; + xcb_void_cookie_t cookie; + cookie = (*func) (c, + XCB_COPY_FROM_PARENT, + window, + screen->root, + 0, 0, + 150, 150, + 10, + XCB_WINDOW_CLASS_INPUT_OUTPUT, + screen->root_visual, + mask, values); + } + + _xcb_write_block = saved_write_block; + + xcb_disconnect(c); + + *bytes_p = malloc(write_block_bytes_len); + memcpy(*bytes_p, &write_block_bytes[0], write_block_bytes_len); + *len_p = write_block_bytes_len; +} + +static void +compare_create_windows(const char *description_a, + CreateWindowFunc func_a, + const char *description_b, + CreateWindowFunc func_b) +{ + unsigned char *a_bytes; + unsigned char *b_bytes; + size_t a_len, b_len; + + get_create_window_bytes(func_a, &a_bytes, &a_len); + printf("%s: len=%u\n", description_a, a_len); + + get_create_window_bytes(func_b, &b_bytes, &b_len); + printf("%s: len=%u\n", description_b, b_len); + + assert(a_len == b_len); + + if (memcmp(a_bytes, b_bytes, a_len) != 0) + { + size_t i; + for (i = 0; i < a_len; ++i) { + printf("%2u: %x %x\n", + i, (unsigned int) a_bytes[i], (unsigned int) b_bytes[i]); + } + } + + assert(memcmp(a_bytes, b_bytes, a_len) == 0); +} + +int +main(int argc, const char **argv) +{ + saved_write_block = _xcb_write_block; + + compare_create_windows("xcb_std_create_window", xcb_std_create_window, + "xcb_dyn_create_window", xcb_dyn_create_window); + + time_create_window("xcb_std_create_window", xcb_std_create_window); + time_create_window("xcb_dyn_create_window", xcb_dyn_create_window); + time_create_window("xcb_std_create_window", xcb_std_create_window); + time_create_window("xcb_dyn_create_window", xcb_dyn_create_window); + + return 0; +} + +static const xcb_request_op_t create_window_ops[] = { + /* opcode */ XCB_CREATE_WINDOW, + /* isvoid */ 1, + XCB_REQUEST_OP_BUFS_COUNT, 2, + XCB_REQUEST_OP_START_BUF, sizeof(xcb_create_window_request_t) + XCB_PADDING(sizeof(xcb_create_window_request_t)), + XCB_REQUEST_OP_HEADER_WITH_PAD_FIELD, + XCB_REQUEST_OP_FIELD_32, + XCB_REQUEST_OP_FIELD_32, + XCB_REQUEST_OP_FIELD_16, + XCB_REQUEST_OP_FIELD_16, + XCB_REQUEST_OP_FIELD_16, + XCB_REQUEST_OP_FIELD_16, + XCB_REQUEST_OP_FIELD_16, + XCB_REQUEST_OP_FIELD_16, + XCB_REQUEST_OP_FIELD_32, + XCB_REQUEST_OP_FIELD_32_END_BUF_POPCOUNTED_ARRAY_32, + XCB_REQUEST_OP_NULL +}; + +xcb_void_cookie_t +xcb_dyn_create_window (xcb_connection_t *c /**< */, + uint8_t depth /**< */, + xcb_window_t wid /**< */, + xcb_window_t parent /**< */, + int16_t x /**< */, + int16_t y /**< */, + uint16_t width /**< */, + uint16_t height /**< */, + uint16_t border_width /**< */, + uint16_t _class /**< */, + xcb_visualid_t visual /**< */, + uint32_t value_mask /**< */, + const uint32_t *value_list /**< */) +{ + return xcb_send_request_dynamic(c, 0, NULL, + &create_window_ops[0], + depth, wid, parent, x, y, width, height, + border_width, _class, visual, value_mask, value_list); +} + +xcb_void_cookie_t +xcb_std_create_window (xcb_connection_t *c /**< */, + uint8_t depth /**< */, + xcb_window_t wid /**< */, + xcb_window_t parent /**< */, + int16_t x /**< */, + int16_t y /**< */, + uint16_t width /**< */, + uint16_t height /**< */, + uint16_t border_width /**< */, + uint16_t _class /**< */, + xcb_visualid_t visual /**< */, + uint32_t value_mask /**< */, + const uint32_t *value_list /**< */) +{ + static const xcb_protocol_request_t xcb_req = { + /* count */ 4, + /* ext */ 0, + /* opcode */ XCB_CREATE_WINDOW, + /* isvoid */ 1 + }; + + struct iovec xcb_parts[6]; + xcb_void_cookie_t xcb_ret; + xcb_create_window_request_t xcb_out; + + xcb_out.depth = depth; + xcb_out.wid = wid; + xcb_out.parent = parent; + xcb_out.x = x; + xcb_out.y = y; + xcb_out.width = width; + xcb_out.height = height; + xcb_out.border_width = border_width; + xcb_out._class = _class; + xcb_out.visual = visual; + xcb_out.value_mask = value_mask; + + xcb_parts[2].iov_base = (char *) &xcb_out; + xcb_parts[2].iov_len = sizeof(xcb_out); + xcb_parts[3].iov_base = 0; + xcb_parts[3].iov_len = -xcb_parts[2].iov_len & 3; + xcb_parts[4].iov_base = (char *) value_list; + xcb_parts[4].iov_len = xcb_popcount(value_mask) * sizeof(uint32_t); + xcb_parts[5].iov_base = 0; + xcb_parts[5].iov_len = -xcb_parts[4].iov_len & 3; + xcb_ret.sequence = xcb_send_request(c, 0, xcb_parts + 2, &xcb_req); + return xcb_ret; +} diff --git a/src/xcb_out.c b/src/xcb_out.c index fbce7a0..32920bb 100644 --- a/src/xcb_out.c +++ b/src/xcb_out.c @@ -55,6 +55,9 @@ static int write_block(xcb_connection_t *c, struct iovec *vector, int count) return _xcb_out_send(c, vector, count); } +/* we can replace this in test programs to test request generation */ +int (* _xcb_write_block) (xcb_connection_t *c, struct iovec *vector, int count) = write_block; + static void get_socket_back(xcb_connection_t *c) { while(c->out.return_socket && c->out.socket_moving) @@ -135,10 +138,15 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect uint32_t prefix[3] = { 0 }; int veclen = req->count; enum workarounds workaround = WORKAROUND_NONE; + int (* _debug_write_block) (xcb_connection_t *c, struct iovec *vector, int count); if(c->has_error) return 0; + /* we want the real write_block if we call xcb_get_maximum_request_length */ + _debug_write_block = _xcb_write_block; + _xcb_write_block = write_block; + assert(c != 0); assert(vector != 0); assert(req->count > 0); @@ -246,7 +254,9 @@ unsigned int xcb_send_request(xcb_connection_t *c, int flags, struct iovec *vect vector[0].iov_base = prefix + !prefix[0]; } - if(!write_block(c, vector, veclen)) + _xcb_write_block = _debug_write_block; + + if(!_xcb_write_block(c, vector, veclen)) { _xcb_conn_shutdown(c); request = 0; @@ -354,6 +364,8 @@ int _xcb_out_flush_to(xcb_connection_t *c, uint64_t request) vec.iov_len = c->out.queue_len; c->out.queue_len = 0; return _xcb_out_send(c, &vec, 1); + } else { + c->out.request_written = c->out.request; } while(c->out.writing) pthread_cond_wait(&c->out.cond, &c->iolock); -- 1.7.0.4