Python中int()处理memoryview是否产生字节副本及相关技术问询
关于memoryview调用int()的零拷贝疑问
我对零拷贝操作非常关注(同时遗憾memoryview不具备bytes的全部成员方法),我的应用在这方面需求很高。
考虑以下Python代码,用于从字节/缓冲区提取整数:
>>> b: bytes = b'987654' >>> mv: memoryview = memoryview(b) >>> type(mv) <class 'memoryview'> >>> isinstance(mv, bytes) False >>> int(b) # 符合预期 987654 >>> int(mv) # 在Python 3.12中可行,但是否会生成临时bytes副本? 987654
如果每次整数提取都生成中间/临时字节副本,大量执行这类操作时会产生显著影响。
我有以下问题:
- 调用的是哪个
int()构造函数? - 有什么好的测试方法可以确定/验证这一点?我考虑通过猴子补丁修改
mv.__some_member_function__为自定义打印函数,但该从何处入手? - 调用
int()时是否会生成中间字节副本?
我的环境信息:
>>> import sys >>> sys.version '3.12.9 (main, Feb 6 2025, 14:33:23) [GCC 14.2.0 64 bit (AMD64)]'
解答
1. 调用的是哪个int()构造函数?
Python 3.12+中,int()可以接受实现了__int__()或符合缓冲协议的对象。memoryview没有定义__int__()方法,而是通过缓冲协议被int()处理——底层实现会先检查传入对象是否支持缓冲协议,若支持则直接读取缓冲数据,无需先转换为bytes。
2. 测试是否产生临时副本的方法
方法一:用tracemalloc追踪内存分配
tracemalloc可以精准对比两种调用方式的内存变化,判断是否有额外的字节副本分配:
import tracemalloc def test_int_with_bytes(): b = b'987654' * 1000 tracemalloc.start() snapshot1 = tracemalloc.take_snapshot() for _ in range(1000): int(b) snapshot2 = tracemalloc.take_snapshot() tracemalloc.stop() stats = snapshot2.compare_to(snapshot1, 'lineno') print("使用bytes的内存变化:") for stat in stats[:5]: print(stat) def test_int_with_memoryview(): b = b'987654' * 1000 mv = memoryview(b) tracemalloc.start() snapshot1 = tracemalloc.take_snapshot() for _ in range(1000): int(mv) snapshot2 = tracemalloc.take_snapshot() tracemalloc.stop() stats = snapshot2.compare_to(snapshot1, 'lineno') print("\n使用memoryview的内存变化:") for stat in stats[:5]: print(stat) test_int_with_bytes() test_int_with_memoryview()
如果memoryview测试组没有额外的大内存分配记录,说明没有生成临时bytes副本。
方法二:猴子补丁拦截缓冲协议方法
memoryview的缓冲协议实现依赖__buffer__()方法,替换该方法并添加日志,即可验证int()是否直接通过缓冲协议读取数据:
original_buffer = memoryview.__buffer__ def traced_buffer(self, flags): print("__buffer__方法被调用!") return original_buffer(self, flags) memoryview.__buffer__ = traced_buffer mv = memoryview(b'987654') int(mv)
若运行时打印出__buffer__方法被调用!,说明int()直接读取缓冲数据;如果想验证是否触发bytes转换,可以同样补丁__bytes__()方法,若该方法未被调用,则证明没有生成临时副本。
3. 是否会生成中间字节副本?
在Python 3.12及以上版本中,int()处理memoryview时不会生成临时bytes副本。CPython底层的PyLong_FromString函数已支持直接从缓冲对象读取数据,跳过了转换为bytes的步骤,完全符合零拷贝需求。
内容的提问来源于stack exchange,提问作者JimB
相关产品推荐
相关产品推荐

