Chunk快速创建性能优化求助:现有Python实现速度慢,求改进方案
我正在实现Chunk创建功能,目前功能可用但运行速度较慢,以下是具体实现步骤:
1. 定义立方体基础数据
vertices = [(-block_size / 2, -block_size / 2, block_size / 2), (block_size / 2, -block_size / 2, block_size / 2), (block_size / 2, block_size / 2, block_size / 2), (-block_size / 2, block_size / 2, block_size / 2), (-block_size / 2, block_size / 2, -block_size / 2), (-block_size / 2, -block_size / 2, -block_size / 2), (block_size / 2, -block_size / 2, -block_size / 2), (block_size / 2, block_size / 2, -block_size / 2)] indices = [(0, 2, 3), (0, 1, 2), (1, 7, 2), (1, 6, 7), (6, 5, 4), (4, 7, 6), (3, 4, 5), (3, 5, 0), (3, 7, 4), (3, 2, 7), (0, 6, 1), (0, 5, 6)] tex_coord_vertices = [(0, 0), (1, 0), (1, 1), (0, 1)] tex_coord_indices = [(0, 2, 3), (0, 1, 2), (0, 2, 3), (0, 1, 2), (0, 1, 2), (2, 3, 0), (2, 3, 0), (2, 0, 1), (0, 2, 3), (0, 1, 2), (3, 1, 2), (3, 0, 1), ] normals = [(0, 0, 1) * 6, (1, 0, 0) * 6, (0, 0, -1) * 6, (-1, 0, 0) * 6, (0, 1, 0) * 6, (0, -1, 0) * 6, ] normals = np.array(normals, dtype='f4').reshape(36, 3)
2. 生成2f 3f 3f格式的数据集
def get_data(vertices, indices): data = [vertices[ind] for triangle in indices for ind in triangle] return np.array(data, dtype='f4')
vertex_data_local = self.get_data(vertices, indices) tex_coord_data = self.get_data(tex_coord_vertices, tex_coord_indices) vertex_data_local = np.hstack([normals, vertex_data_local]) vertex_data_local = np.hstack([tex_coord_data, vertex_data_local])
3. 创建Chunk中立方体中心坐标数组
chunk_size = Settings.CHUNK_SIZE * Settings.CHUNK_SIZE * Settings.CHUNK_SIZE coords = np.empty([chunk_size, 8]) index = 0 for x in range(Settings.CHUNK_SIZE): for y in range(Settings.CHUNK_SIZE): for z in range(Settings.CHUNK_SIZE): if self.m_pBlocks[x][y][z].is_active(): coords[index] = (0, 0, 0, 0, 0, x * block_size, y * block_size, z * block_size) index = index + 1
4. 生成Chunk数据数组
vertex_data = np.empty([chunk_size * 36, 8]) index = 0 for idx, obj in enumerate(coords): for ind, obj_row in enumerate(vertex_data_local): # print(np.add(arr, obj_row)) vertex_data[index] = np.add(obj, obj_row) index = index + 1 vertex_data = np.array(vertex_data, dtype='f4')
目前已知可通过编写C语言库进行优化,请问还有其他可行的性能优化思路吗?
可行的性能优化思路
1. 面剔除:只生成可见面的顶点数据
当前实现会生成每个立方体的全部6个面,但多数面会被相邻的active方块遮挡。遍历每个active方块时,检查上下左右前后六个方向是否存在相邻的active方块,仅生成无遮挡的可见面数据,能直接减少50%-70%的顶点数据量,从根源降低计算和内存开销。
2. 用numpy向量化操作替代Python嵌套循环
Python循环的开销远高于numpy的底层C实现,重构步骤3和4:
- 步骤3优化:利用
meshgrid生成坐标网格,结合布尔索引筛选active方块,避免手动循环计数:
# 生成Chunk内所有方块的坐标网格 x_coords = np.arange(Settings.CHUNK_SIZE) * block_size y_coords = np.arange(Settings.CHUNK_SIZE) * block_size z_coords = np.arange(Settings.CHUNK_SIZE) * block_size xx, yy, zz = np.meshgrid(x_coords, y_coords, z_coords, indexing='ij') # 将方块active状态转为numpy布尔数组 active_mask = np.array([[[self.m_pBlocks[x][y][z].is_active() for z in range(Settings.CHUNK_SIZE)] for y in range(Settings.CHUNK_SIZE)] for x in range(Settings.CHUNK_SIZE)]) # 构造active方块的coords数组 active_xx = xx[active_mask] active_yy = yy[active_mask] active_zz = zz[active_mask] coords = np.zeros((len(active_xx), 8), dtype='f4') coords[:,5] = active_xx coords[:,6] = active_yy coords[:,7] = active_zz
- 步骤4优化:利用numpy广播机制,一次性完成所有坐标的加法运算,彻底移除嵌套循环:
# 扩展维度后广播相加,再展平为最终数组 vertex_data = (coords[:, np.newaxis, :] + vertex_data_local[np.newaxis, :, :]).reshape(-1, 8).astype('f4')
3. 预计算静态数据
vertex_data_local仅与block_size相关,无需每次生成Chunk时重复计算。在程序初始化阶段预计算并缓存该数组,后续直接复用,省去get_data调用和数组拼接的开销。
4. 优化方块状态存储
将self.m_pBlocks的三维对象数组替换为numpy布尔数组(np.bool_类型),直接通过active_blocks[x,y,z]判断方块是否激活,避免频繁的对象属性访问开销。若追求极致内存效率,还可使用位掩码(用整数的每个bit表示一个方块的激活状态)。
5. 多核并行处理
对于大尺寸Chunk,可将其划分为多个子区域,用multiprocessing或concurrent.futures实现并行生成数据,充分利用CPU多核资源。注意需与numpy自身的多线程优化(如MKL/OpenBLAS)做好兼容,避免资源竞争。
6. 按需分配内存
步骤3中预先分配整个Chunk大小的coords数组会浪费内存(多数方块可能未激活),改为先收集激活方块的坐标,再按需分配内存,或用列表临时存储后转numpy数组(需权衡列表转数组的开销)。
内容的提问来源于stack exchange,提问作者Sergey Tyutyuma

