为何X11 XServer绘制直线比直接写缓冲区更快?
技术问询
测试直线绘制性能时发现,使用X11 API绘制直线比直接写入内存缓冲区更快,具体情况如下:
X11实现代码
// 使用X11 API for (int i = (xrectw >> 1) + cc; i < CANVAS_WIDTH; i += xrectw) { for (int j = (yrectw >> 1) + cc; j < CANVAS_HEIGHT; j += yrectw) { XDrawLine(sdd->display, sdd->pixmap, sdd->gc, i - 17, j - 13, i + 17, j + 13); XDrawLine(sdd->display, sdd->pixmap, sdd->gc, i + 17, j - 13, i - 17, j + 13); } }
直接写入缓冲区实现代码
// 绘制直线的Bresenham算法 void draw_line(uint32_t *canvas, int x0, int y0, int x1, int y1, uint32_t c) { int dx = abs(x1 - x0); int dy = abs(y1 - y0); int sx = (x0 < x1) ? 1 : -1; int sy = (y0 < y1) ? 1 : -1; int err = dx - dy; int width = CANVAS_WIDTH; int height = CANVAS_HEIGHT; uint32_t (*arrayPtr)[CANVAS_HEIGHT] = (uint32_t(*)[CANVAS_HEIGHT])canvas; while (x0 != x1 && y0 != y1) { if (x0 >= 0 && y0 >= 0 && x0 < width && y0 < height) arrayPtr[y0][x0] = c; int e2 = (err << 1); if (e2 > -dy) { err -= dy; x0 += sx; } if (e2 < dx) { err += dx; y0 += sy; } } } _apixmap = (uint32_t*)malloc(sizeof(uint32_t) * CANVAS_WIDTH * CANVAS_HEIGHT); memset(_apixmap, 1, sizeof(uint32_t) * CANVAS_WIDTH * CANVAS_HEIGHT); for (int i = (xrectw >> 1) + cc; i < CANVAS_WIDTH; i += xrectw) { for (int j = (yrectw >> 1) + cc; j < CANVAS_HEIGHT; j += yrectw) { draw_line_simd(_apixmap, i - 17, j - 13, i + 17, j + 13, c1); draw_line_simd(_apixmap, i + 17, j - 13, i - 17, j + 13, c1); } }
测试数据
CANVAS_WIDTH和CANVAS_HEIGHT均为2048,绘制208080条直线时:
- 使用X11 API仅耗时135ms
- 直接写入缓冲区耗时330ms
测试条件与疑问
直接写入缓冲区不存在Socket I/O开销,但性能却更差,且满足以下测试条件:
- 在无GPU的PC上测试,结果一致
- 仅使用单个CPU核心,无多线程参与
- 在3台以上PC及虚拟机中测试,结果相同
- 使用-O2、-O3、-Ofast编译优化,耗时无变化
特此问询:X11 XServer是如何实现比直接写缓冲区更快的直线绘制的?
问题解答
X11 XServer能实现更快的直线绘制,核心原因集中在以下几点:
1. 底层实现的极致优化
XServer的直线绘制逻辑是经过数十年迭代的高度优化代码:
- 它的直线算法(如Bresenham或变体)在循环分支、内存访问模式上做了精细打磨,大幅降低分支预测失败的概率;
- 针对不同像素格式(如32位RGB)提供了汇编级优化,甚至直接利用CPU的SIMD指令批量处理像素写入,效率远高于手写C实现。
2. 内存访问的局部性优化
你的代码中,arrayPtr[y0][x0]是逐像素离散写入,内存访问局部性差,容易触发缓存未命中;而XServer:
- 会对连续像素区域做批量写入,充分利用CPU缓存行特性;
- 对Pixmap的内存布局做了对齐优化,确保像素写入是对齐内存操作,避免非对齐访问带来的性能损耗。
3. 边界检查的高效处理
你的实现中,每个像素写入前都要做一次边界判断,这会带来大量分支开销;XServer则会提前计算直线的有效像素范围,一次性过滤掉超出画布的部分,避免了循环内的重复判断。
4. 编译与平台适配的极致性
XServer的代码针对目标平台做了深度适配:
- 使用了平台特定的编译选项和架构优化(如x86-64的AVX指令集);
- 部分核心逻辑用汇编实现,编译器无法自动将普通C代码优化到这个级别,即使开启-O3也难以企及。
5. 函数调用与逻辑紧凑性
你的代码通过draw_line_simd函数调用完成绘制,即便编译器做了内联优化,逻辑复杂度依然高于XServer的紧凑实现;XServer的绘制逻辑直接在服务端内存区域操作,没有额外的调用开销和逻辑冗余。
内容的提问来源于stack exchange,提问作者A Mr
相关产品推荐
相关产品推荐

