【问题标题】:Underlying mechanism of Python's memoryviewPython的memoryview底层机制
【发布时间】:2017-08-31 22:46:29
【问题描述】:

据说 Python 的 memoryview 在切片时不会复制数据。已经完成了许多基准测试,其中一些在 stackoverflow 上“证明”了这种行为。

在试图惹他们的时候,我遇到了一个我无法解释的奇怪行为:

>>> arr = bytearray(range(0,15))
>>> mem = memoryview(arr)
>>> mem[5:15] = mem[0:10]
>>> arr
bytearray(b'\x00\x01\x02\x03\x04\x00\x01\x02\x03\x04\x05\x06\x07\x08\t')

一方面,memoryview“不”复制数据。另一方面,这非常有效!

虽然我很高兴它“有效”,但我对它有效的事实感到难过。嗯...因为它不应该。

如果 Python 有 1 个字符的缓冲区,结果应该是这样的:

bytearray(b'\x00\x01\x02\x03\x04\x00\x01\x02\x03\x04\x00\x01\x02\x03\x04')

基本上,在写入第 5 个字符时,它应该已经重叠并读取了之前写入的第 1 个字符。这种天真的方法的一个例子:

>>> for i in range(10):
...    m[i+5] = m[i]
>>> a
bytearray(b'\x00\x01\x02\x03\x04\x00\x01\x02\x03\x04\x00\x01\x02\x03\x04')

我尝试将 memoryview 的大小增加到很大,但它仍然有效,这意味着 python 在后台复制数据,使 memoryview 对象变得毫无意义。

这里有什么地方我错了吗?有什么解释吗?那么 memoryview 是如何工作的呢?

【问题讨论】:

    标签: python cpython memoryview


    【解决方案1】:

    It checks for that:

        if (dptr + size < sptr || sptr + size < dptr)
            memcpy(dptr, sptr, size); /* no overlapping */
        else
            memmove(dptr, sptr, size);
    

    memmove 被指定为对重叠的源和目标是安全的。它如何确保安全性因案例和实施而异,但如果从左到右会覆盖尚未复制的数据,则一种技术是从右到左而不是从左到右工作。

    【讨论】:

    • 小子,你速度很快。但它提出了另一个问题:为什么不总是使用 memmove? memmove 已经对重叠进行了检查,那么为什么要检查两次呢?检查两次不是效率低吗?我相信 memmove 如果不重叠,将求助于 memcopy。也许是因为新的堆栈帧开销?
    • @Bharel:不知道。如果dptrsptr 没有指向同一个数组,他们所做的检查甚至是未定义的行为,所以看起来他们应该把检查留给memmove,它可以在没有UB 的情况下执行检查。也许他们测量了某些实现的实际性能差异,或者他们只是不信任 memmove。可能是某些编译器内联 memcpy 而不是 memmove。
    • 如果它们不指向同一个数组,则超出大小范围(之前已检查过),因此它将求助于 memcpy。
    • @Bharel:CPython 源代码在official Github repo 中。大多数内置对象类型都是在Objects 目录下实现的,在其中的文件中,memoryobject.c 听起来像是实现了memoryview...
    • 从那里,熟悉 C API 和相关的命名和注释约定指向 memory_ass_sub 作为实现 memoryview 切片分配(和其他下标分配)的函数,从那里开始,这是一个问题跟踪代码路径直到数据复制发生的地方。
    猜你喜欢
    • 1970-01-01
    • 2013-12-27
    • 2015-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-14
    • 2021-04-06
    • 1970-01-01
    相关资源
    最近更新 更多