pycairo中每次调用show_text性能逐次下降问题排查
问题:pycairo show_text() 耗时随调用次数递增的异常问题
我使用pycairo 1.23.0渲染至cairo.ImageSurface,再通过pygame显示位图。多数绘图操作耗时稳定,但cairo.Context.show_text()的调用耗时随每次调用持续增加,尤其在与2的幂相关的帧数时耗时异常。怀疑是内存管理或内部数据结构出现异常,附上可运行示例代码。已知这是“toy”字体API,但引入pango等库对该应用来说过于冗余。

示例代码:
import pygame import cairo import numpy as np from time import perf_counter pygame.init() screen = pygame.display.set_mode((1280, 720), pygame.SCALED, vsync=1) while True: img_surf = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1280, 720) ctx = cairo.Context(img_surf) ctx.set_source_rgb(.7, .7, .7) ctx.paint() start = perf_counter() ctx.set_source_rgb(.0, .0, .0) ctx.set_font_size(30) ctx.move_to(20, 50) ctx.show_text("Not very fast") stop = perf_counter() print(f"{stop - start:.07f}") img_surf.flush() result = np.copy(np.asarray(img_surf.get_data())) result = result.reshape((720, 1280, 4))[..., :3][..., ::-1] surface = pygame.surfarray.make_surface(np.transpose(result, (1, 0, 2))) screen.blit(surface, (0, 0)) img_surf.finish() pygame.display.flip() list(pygame.event.get())
问题根源
你的代码存在两个核心问题,直接导致了耗时递增和内存异常:
- 循环内重复创建Surface与Context:每次循环都新建
cairo.ImageSurface和cairo.Context,即便调用了finish(),pycairo的垃圾回收也无法及时清理底层C语言层面的资源,造成内存累积,进而拖慢show_text()的字体缓存效率。 - 冗余内存拷贝与转换:
np.copy(np.asarray(img_surf.get_data()))会创建额外内存副本,加重内存负担;颜色通道和数组转置的操作也存在优化空间,额外消耗了运行时间。
修复方案
1. 复用Surface和Context
将cairo.ImageSurface和cairo.Context的初始化移到循环外,避免重复创建销毁带来的资源开销:
import pygame import cairo import numpy as np from time import perf_counter pygame.init() screen = pygame.display.set_mode((1280, 720), pygame.SCALED, vsync=1) # 仅初始化一次Surface和Context img_surf = cairo.ImageSurface(cairo.FORMAT_ARGB32, 1280, 720) ctx = cairo.Context(img_surf) ctx.set_font_size(30) # 提前设置字体大小,避免重复操作 while True: # 重置画布背景 ctx.set_source_rgb(.7, .7, .7) ctx.paint() start = perf_counter() ctx.set_source_rgb(.0, .0, .0) ctx.move_to(20, 50) ctx.show_text("Now it's fast") stop = perf_counter() print(f"{stop - start:.07f}") img_surf.flush() # 优化数据转换逻辑 data = np.asarray(img_surf.get_data()).reshape((720, 1280, 4)) data = data[..., [2,1,0]] # 直接转换ARGB到RGB格式,避免冗余拷贝 surface = pygame.surfarray.make_surface(np.transpose(data, (1, 0, 2))) screen.blit(surface, (0, 0)) pygame.display.flip() # 正确处理退出事件 for event in pygame.event.get(): if event.type == pygame.QUIT: pygame.quit() exit() # 程序结束时再清理资源 img_surf.finish()
2. 优化数据转换流程
- 移除
np.copy(),直接使用np.asarray()获取内存视图(img_surf.get_data()返回的是缓冲区,np.asarray()不会产生额外拷贝) - 用
data[..., [2,1,0]]直接调整颜色通道顺序,比切片反转更高效 - 保留必要的数组转置,适配cairo与pygame的内存布局差异
3. 完善事件处理
原代码仅清空事件队列,未处理退出事件,修复后添加pygame.QUIT事件的响应逻辑,保证程序能正常退出。
效果验证
修改后,show_text()的耗时会保持稳定,不会再出现随调用次数递增的情况,也不会有2的幂帧数的异常波动。这是因为底层字体缓存和Surface资源被复用,内存泄漏问题得到解决。
内容的提问来源于stack exchange,提问作者Maximilian Schier
相关产品推荐
相关产品推荐

