Files
2026-07-04 02:40:45 +08:00

3.3 KiB
Raw Permalink Blame History

EfficientWordCloud 并行优化设计文稿

1. 目标与背景

本设计针对 EfficientWordCloud 的两个性能瓶颈进行优化:

  • Python 侧:文本渲染与 bbox 计算属于 CPU 密集型,原流程在主循环中串行执行。
  • C++ 侧:可用位置搜索为线性扫描,且非线程安全,限制多核利用。

目标:

  • 在 Python 侧引入并行 bbox 预取,降低主循环阻塞。
  • 在 C++ 侧实现线程安全与分块并行搜索,提升多核性能。

2. 总体架构

2.1 并行策略

  • Python 侧Producer-Consumer 预取模式。
    • 使用 ProcessPoolExecutor 异步计算未来词语的 bbox。
    • 主循环消费预取结果;若字体缩小,则回退为同步计算。
  • C++ 侧Chunked Parallel Search。
    • 将候选坐标分块,每块由一个异步任务扫描。
    • 收集所有任务结果,选择最小索引对应的位置,以保持“中心优先”的排序策略。

2.2 线程安全模型

  • IntegralGrid 内部引入 std::shared_mutex
    • get_area_sum)在并行搜索期间不再加锁,假设搜索阶段只读且在生成坐标与积分图后保持快照。
    • update_rect_add)当前实现默认由单线程 Python 调用,移除了锁以减少开销;如需多线程更新需恢复写锁保护。
    • 重建积分图(rebuild_integral)只在写路径触发。

3. C++ 核心设计

3.1 数据结构扩展

  • IntegralGrid 内部新增:
    mutable std::shared_mutex mutex_;
    

3.2 并行搜索流程

  1. 计算线程数(hardware_concurrency),确定分块大小。
  2. valid_coords 分块后并行扫描,每块返回一个 SearchResult
  3. 主线程汇总所有结果,选取最小索引(全局最佳位置)。
  4. 在搜索期间释放 GIL
    Py_BEGIN_ALLOW_THREADS
    ...
    Py_END_ALLOW_THREADS
    

3.3 关键函数

  • search_range(start, end, box_h, box_w, step):扫描单块坐标。
  • find_spot_parallel(box_h, box_w, step):组织并行任务并聚合结果。
  • Grid_query_sorted:绑定层调用 find_spot_parallel

4. Python 侧设计

4.1 可序列化的测量函数

模块级函数保证可被 ProcessPoolExecutor 调度:

def measure_text(text, font_path, size, rotate) -> (int, int)

4.2 ParallelBBoxFetcher

职责:

  • 维护任务队列。
  • 提交预取任务。
  • 主循环中拉取结果(若无缓存则同步测量)。

接口:

  • prefetch(items)
  • get(key, text, size, rotate)

4.3 主流程集成

  • 预先生成 rotation_flags,确保预取与实际一致。
  • 预取前 N 个词的 bbox。
  • 逐词处理时,提前预取下一批的 bbox。
  • 字体缩小后回退同步,保证正确性。

5. 构建配置

  • 编译标准升级为 C++17 以使用 std::shared_mutex

6. 影响与兼容性

  • 对外 API 保持不变。
  • 性能提升取决于多核环境及词数量。
  • 并行搜索仍保证排序一致性。

7. 风险与对策

  • 多进程 bbox 测量开销:采用预取窗口控制并发规模。
  • 锁开销:只在必要时加锁,读操作使用共享锁。
  • 一致性:聚合结果按最小索引保证行为与原排序一致。