Python中memoryview小切片性能为何低于bytes?适用场景是什么
你观察到的性能差异是完全正常的,核心原因是两种操作的开销构成不同:
小切片场景下memoryview的固定开销更高
memoryview本身是对底层缓冲区的封装对象,每次创建子memoryview切片时,都需要生成新的Python对象,存储原缓冲区引用、偏移量、长度、格式等元信息,这部分是固定的对象创建开销。而你测试的切片只有7字节(!I3B对应4字节无符号整型+3字节无符号字符,总长度7),拷贝7字节的开销远低于创建新memoryview对象的开销,所以for循环小切片场景下,bytes切片性能反而更好。while循环场景下bytes版本有O(n²)的额外拷贝开销
你写的while循环bytes版本中,每次执行byte_array = byte_array[stsize:]都会把剩余的所有字节全部拷贝一份新的bytes对象:第一次循环要拷贝近35KB的剩余数据,第二次拷贝剩下的34993字节,以此类推,总拷贝量是O(n²)级别的,数据量越大开销越恐怖。而memoryview的切片完全不会拷贝数据,只是修改偏移量和长度的元信息,开销是固定的,所以这种场景下memoryview的性能优势会非常明显。
关于阈值和适用场景
确实存在性能临界点,这个阈值和Python版本、运行平台有关,通常单切片长度超过50~100字节时,拷贝数据的开销就会超过创建memoryview的固定开销,此时用memoryview才划算。如果你的使用场景一直是十几字节以内的短切片,确实没必要强行用memoryview。
更适合你场景的优化方案
你完全可以用struct.Struct.unpack_from方法规避切片开销,这个方法不需要提前做切片,直接从输入缓冲区的指定偏移量读取数据解析,没有任何拷贝或子对象创建开销,完美适配Python 3.4以下的环境,示例实现如下:
def unpack_opt(byte_data): end = len(byte_data) for offset in range(0, end, stsize): st.unpack_from(byte_data, offset)
实测这个实现的性能会远高于你之前写的两个切片版本。
内容的提问来源于stack exchange,提问作者hardhypochondria

