【问题标题】:Cython "Memory Efficient Doubly Linked List"Cython“内存高效双向链表”
【发布时间】:2019-10-17 11:36:50
【问题描述】:

作为练习,我将使用指针上的 XOR 组合一个双向链表,以方便每个节点只需存储一个值。我正在尝试使用对我来说是新的 Cython,我很快就制定了以下模式,但不知道 cython 的功能是否会按预期工作

cdef class Node:
    def __init__(self, object val, void* prev_xor_next=NULL):
        self.prev_xor_next=prev_xor_next
        self.val=val

    def __repr__(self):
        return str(self.val)

cdef class CurrentNode(Node):
    def __init__(self, Node node, void* prev_ptr=NULL):
        self.node=node
        self.prev_ptr=prev_ptr

    cpdef CurrentNode forward(self):
        if self.node.prev_xor_next:
            return CurrentNode((self.node.prev_xor_next^self.prev_ptr)[0], &self.node)

    cpdef CurrentNode backward(self):
        if self.prev_ptr:
            return CurrentNode(self.prev_ptr[0], (&self.node)^(self.prev_ptr[0]).prev_xor_next)

cdef class XORList:
    cdef Node first, last, current

    cpdef append(self, object val):
        if not first:
            self.first=CurrentNode(Node(val))
            self.last=self.first
            self.current=self.first
        else:
            if last==first:
                self.last=CurrentNode(Node(val))
                self.first.node.prev_xor_next=(&self.last.node)^self.first.prev_ptr
                self.first=self.first.node
                self.last.prev_ptr=&self.first
            else:
                temp=CurrentNode(Node(val))
                self.last.node.prev_xor_next=(&self.temp.node)^self.last.prev_ptr
                self.temp.prev_ptr=&self.last
                self.last=temp

    cpdef reverse(self):
        temp=self.last
        self.last=self.first
        self.first=temp

    def __iter__(self):
        return self

    def __next__(self):
        if not self.current or not self.current.forward():
            self.current=CurrentNode(self.first)
            raise StopIteration()
        ret = self.current
        self.current=self.current.forward()
        return ret

    def __repr__(self):
        cdef str ret =''
        for i in self:
            ret+='<=>'+str(i)
        return ret

我遇到的问题包括获取指向扩展类型的指针和将指针传递给构造函数。我发现第二个问题是通过使用静态工厂方法解决的,但我把它留在这里,希望能保持我正在尝试的模式更清晰。

我尝试用结构替换 Node,但这让我无法将 python 对象作为 Node 的值。

我研究过 PyObject 和 PyCapsule,但我对它们都没有运气,这可能只是由于对它们的用法缺乏了解,尽管它们的基本用途似乎很清楚。

有没有一种方法可以使用 Cython 实现这种模式?

请原谅代码中的任何逻辑错误。我没有测试过,这只是一个练习。

【问题讨论】:

  • 所以一般的问题是任何defcpdef(包括构造函数)都必须可以从 Python 调用,这意味着您不能传递不可表示为 PyObject 的类型。因此cdef 静态构造函数是要走的路。我很确定会有重复的问题。 然而指针只是特殊的整数,所以你可以将它们作为整数传递给 Python(然后强制转换)
  • 这看起来也像是一个可能的引用计数灾难(尽管我不确定我是否遵循得足够好来确定)。每个cdef 类都被引用计数,但异或点不会保持对它的引用......
  • @DavidW 我也尝试过使用 uintptr_t 并在仍然使用 init 的同时进行强制转换,但是我更喜欢使用静态方法的解决方案。另外,我的理解是特殊方法必须声明为def。如果您查看CurrentNode,您会看到我想要一个指向Node 的实际指针。这是我无法通过任何其他更改解决的当前问题
  • 是的,特殊方法必须是def。您应该能够使用尖括号语法将指针投射到节点:&lt;Node&gt;。但是引用计数仍然是个问题。
  • @DavidW 您能否详细说明您对投射到节点的想法?最初的问题是无法获得指向 Node 对象的指针,那么还有什么可以转换的呢?

标签: python pointers linked-list cython


【解决方案1】:

您可以使用尖括号转换语法将扩展类型转换为指针。通常值得使用PyObject* 类型,您可以使用cimport from cpython.object,但是您也可以直接转到void*

from cpython.object cimport PyObject

cdef Node n = Node()
cdef PyObject* nptr1 = <PyObject*>n
cdef void* nptr2 = <void*>n
cdef void* nptr3 = <void*>nptr1

返回 cdef 类型:

cdef Node new_node = <Node>nptr1

Cython 不允许你做的一件事是:

# ERROR: Storing unsafe C derivative of temporary Python reference
cdef PyObject* bad = <PyObject*>Node()

它意识到新的Node 将在创建后几乎立即不存在,因此指针立即无效。相比之下,nptr1nptr2nptr3 至少在 n 未被重新分配时有效。


请注意,您必须自己处理这些引用计数。您可以再次使用cimport 访问相关功能:from cpython.ref cimport Py_XINCREF, Py_XDECREF。这些函数采用PyObject*,因此使用它很有用。如果您打算存储这些指针之一,则需要增加引用计数,并在完成后减少引用计数。

我不确切知道应该为您的异或列表进行哪些引用计数,但您需要这样做

这里还有一个潜在的更微妙的引用计数问题:如果你有循环引用(例如,如果Node 包含指向XOrList 的链接)那么它永远不会被释放。 Cython 尝试生成处理 cdef 类的循环引用的函数,但是它不(也不能)理解您对指针所做的事情,因此它们将被排除在外;覆盖这种行为并不容易。

【讨论】:

  • 这是一个非常好的答案。谢谢你。我会 +1 并稍后测试并接受。您还回答了有关 PyObject 的另一个问题。我曾尝试使用 PyObject 和 PyCapsule 进行类似的操作。不过这更有意义
  • 您的解决方案有效,我对它的整体表现感到满意,但我觉得我错过了一些东西。我还没有测试内存泄漏,但性能不足。也许你可以帮助stackoverflow.com/q/58617519/7907717
猜你喜欢
  • 2016-06-20
  • 2021-07-24
  • 1970-01-01
  • 1970-01-01
  • 2015-12-27
  • 2016-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多