You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.14 02:23:17