首页 > 代码库 > 使用XCB编写X Window程序(06):XCB取代Xlib的理由

使用XCB编写X Window程序(06):XCB取代Xlib的理由

   我经常访问Xorg的官网,希望能找到一些对理解Linux和X Window有用的东西。结果也确实是偶有所得。比如,在Xorg的官网中就建议大家不用Xlib了,改用XCB。不可否认,Xlib确实是一个比较老的东西,老到最新的一本关于Xlib的书都已经是N多年前出版的了。有了Xorg官方的指导,我自然不用学Xlib了,直接上XCB。

  经过这一段时间的学习,对XCB有了一定的了解。我的学习是根据XCB官方的教程来的,当然,如果有一点点在GUI编程领域的经验和悟性学习起来会更加事半功倍。在XCB的官方教程中,一开篇就提出XCB比Xlib先进的地方,同时给出一个示例程序证明XCB比Xlib快多少。但是,对于没有一点Xlib和XCB基础的人来说,要理解这个程序确实有点难。但是现在回过头去看这个教程的开头,一切都是那么一目了然。

  XCB取代Xlib大致上有这样一些理由:

  1、Xlib太重量级了,不易转型,而XCB则轻量级得多;

  2、虽然说Xlib是最靠近X协议的底层开发库,但是其实它还不够底层,因为它提供了缓存机制,而XCB没有缓存,一切都是直接操作X协议,比Xlib还要底层;

  3、据说Xlib对新的X扩展支持不好;

  4、Xlib对多线程编程支持不好,虽然它努力过,但是基础没打好,所以怎么努力也干不好这件事,而XCB没有历史负担,适用于多线程编程;

  5、Xlib和X Server的通讯时同步的,而XCB是异步的,所以XCB比Xlib要快得多。

 

  我这里重点展示第5点。在前面一篇中我对XCB的异步机制有具体的展示。XCB的异步机制其实就是向服务器提交一个请求后,不用等待服务器的答复就可以继续提交后面的请求,因此,不会因为数据在网络上传输的时间而影响程序的效率。而Xlib向X Server提交一个请求后,必须等到服务器答复,才能提交下一个请求。

  下面是一个示例程序,在这个程序中,分别以好的方式和坏的方式使用XCB和Xlib,请求服务器创建500个Atom,比较XCB和Xlib分别花的时间。很幸运,关于Atom上一篇中也提到了,所以不陌生。使用Xlib的坏的方式是一个一个地调用XInternAtom()函数,每次都要等服务器返回后才能创建下一个Atom,所以这是最慢的方式,而使用Xlib的好的方式是使用XInternAtoms()函数一次性创建500个Atom。而使用XCB的坏的方式是,每次调用xcb_intern_atom()函数后,再调用xcb_intern_atom_reply(),确认服务器答复后再创建下一个Atom,很显然这是一个较慢的方式,而且,由于和服务器的一次对话需要调用两个函数,所以使用起来比Xlib还要麻烦。而使用XCB的好的方式,当然是一股脑调用500次xcb_intern_atom()函数,这500次调用完成之后再来关注服务器的答复。示例代码如下,我基本照抄了XCB教程中的代码,只改了最后的几句printf。

  1 /* It‘s a good idea to paste this and other long code examples  2    into a text editor for easier reading */  3   4 #include <stdlib.h>  5 #include <stdio.h>  6 #include <string.h>  7 #include <sys/time.h>  8 #include <xcb/xcb.h>  9 #include <X11/Xlib.h> 10 #define NUM_NAMES 500 11 /* 12     NOTE: For concision, we‘re going to be cheesy and use arrays where real code 13     would use points and memory allocation.s 14 */ 15 #ifndef __GNUC__ 16 char* strdup(const char* s) { 17     int n = strlen(s) + 1; 18  19     char *dup = malloc(n); 20  21     if(dup)  22         strcpy(dup, s); 23  24     return dup; 25 } 26 #endif 27  28 /*  29     return interval of time (uses time.h)  30 */ 31 double 32 get_time (void) { 33     struct timeval timev;            34     gettimeofday(&timev, NULL); 35     return (double)timev.tv_sec + (((double)timev.tv_usec) / 1000000); 36 } 37  38 /* 39  40 */ 41 void 42 useXlib (char **names, 43          Display *display ) { 44  45     Atom atoms[NUM_NAMES]; 46     for (int i = 0; i < NUM_NAMES; ++i) { 47         atoms[i] = XInternAtom(display, names[i], 0); 48     } 49 } 50  51 /* 52 Request all atoms at once. 53 */ 54 void 55 useXlibProperly (char **names, 56          Display *display ) { 57  58     Atom atoms[NUM_NAMES]; 59     if(!XInternAtoms(display, names, NUM_NAMES, 0, atoms)) 60         fprintf(stderr, "XInternAtoms failed\n"); 61 } 62  63 /* 64  65 */ 66 void 67 useXCBPoorly (char **names, 68              xcb_connection_t *connection ) { 69     xcb_atom_t              atoms[NUM_NAMES]; 70     // in this bad use of xcb, we use the cookie immediately after posting the request with xcb_intern_atom  71     for (int i = 0; i < NUM_NAMES; ++i) { 72         /* make request */ 73         xcb_intern_atom_cookie_t cookie = xcb_intern_atom (connection,  74                                                             0,  75                                                             strlen(names[i]), 76                                                             names[i] ); 77         /* get response */ 78         xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply (connection,  79                                                                 cookie,  80                                                                 NULL ); // normally a pointer to receive error, but we‘ll  81  82 just ignore error handling  83         if (reply) { 84             atoms[i] = reply->atom; 85             free (reply); 86         } 87     } 88     // now we have our atoms (replies), but this is just a demo, so we do nothing with them 89 } 90  91 /* 92 */ 93 void 94 useXCBProperly (char **names, 95                 xcb_connection_t *connection ) { 96     xcb_atom_t               atoms[NUM_NAMES]; 97     xcb_intern_atom_cookie_t    cookies[NUM_NAMES]; 98     // in this good example, we make all our requests before checking for 99     // replies because it‘s best to queue requests when we have many at once    100     /* make requests */101     for (int i = 0; i < NUM_NAMES; ++i) {102         cookies[i] = xcb_intern_atom (connection, 103                                      0, 104                                      strlen (names[i]), 105                                      names[i] );106     }107     /* get responses */108     for (int i = 0; i < NUM_NAMES; ++i) {109         xcb_intern_atom_reply_t *reply = xcb_intern_atom_reply (connection, 110                                                                 cookies[i], 111                                                                 NULL ); // normally a pointer to receive errors, but we‘ll 112 113 just ignore error handling114         if (reply) {115             atoms[i] = reply->atom;116             free (reply);117         }118     }119     // now we have our atoms (replies), but this is just a demo, so we do nothing with them120 }121 122 int123 main () {124     /* setup names for tests */125     char (**names) = malloc(NUM_NAMES*sizeof(*names));126     // init names to "NAME0", "NAME1", "NAME2" ... and so on127     for (int i = 0; i < NUM_NAMES; ++i) {128         char buf[100];129         sprintf (buf, "NAME%d", i);130         names[i] = strdup (buf);131     }132 133     /* do tests */134     double start, XlibTime, XlibGoodTime, XCBBadTime, XCBGoodTime;135 136     /* test Xlib */137     Display *display = XOpenDisplay (NULL);138     start = get_time ();139     useXlib (names, display);140     XlibTime = get_time () - start;141     start = get_time ();142     useXlibProperly (names, display);143     XlibGoodTime = get_time () - start;144     XCloseDisplay (display);145 146     /* test XCB */147     xcb_connection_t *connection = xcb_connect (NULL, NULL);148     start = get_time ();149     useXCBPoorly (names, connection);150     XCBBadTime = get_time () - start;   151     start = get_time ();152     useXCBProperly (names, connection);153     XCBGoodTime = get_time () - start;154     xcb_disconnect (connection);155 156     /* report times */157     printf ("Bad Xlib time : %f\n", XlibTime);158     printf ("Good Xlib time : %f\n", XlibGoodTime);159     printf ("Bad xcb time : %f\n", XCBBadTime);160     printf ("Good xcb time : %f\n", XCBGoodTime);161     printf ("ratio of bad xcb time to good xcb time: %f\n", XCBBadTime / XCBGoodTime);162     printf ("ratio of bad Xlib time to good Xlib time: %f\n", XlibGoodTime / XlibTime);163     printf ("ratio of bad Xlib time to good XCB time: %f\n", XlibTime / XCBGoodTime);164     printf ("ratio of good Xlib time to good xcb time: %f\n", XlibGoodTime / XCBGoodTime);165     return 0;166 }    


  运行效果如下图:

  从图中可以看出,XCB的异步方式比同步的方式要快几十倍,Xlib好的方式比坏的方式要快100多倍。由于XCB默认就是异步的,而Xlib天生是同步的,所以最好的和最坏的比较,效率相差300多倍。这就是XCB取代Xlib的理由。

 

(京山游侠于2014-08-01发布于博客园,转载请注明出处。)