I have no final answer for you, but I have a theory.
The Cython shells you wrote are unusual because they wrap a C ++ object directly, rather than a pointer to it.
The following code is particularly inefficient:
cdef setThis(self, _Graph other): self._this = other return self
The reason is because your _Graph class contains several STL vectors and you need to copy them. That way, when your other object is assigned to self._this , memory usage is effectively doubled (or, worse, because STL allocators can sum for performance reasons).
I wrote a simple test that matches yours and added all the information to the log to see how objects are created, copied or destroyed. I canβt find any problems there. Copies occur, but after completing the assignment, I see that only one object remains.
So my theory is that the extra memory you see is related to the STL allocator logic in vectors. All this extra memory must be attached to the final object after the copies.
My recommendation is that you switch to more standard pointer-based packaging. Your _Graph wrapper should be defined more or less as follows:
cdef class Graph: """An undirected, optionally weighted graph""" cdef _Graph* _this def __cinit__(self, n=None): if n is not None: self._this = new _Graph(n) else: self._this = 0 cdef setThis(self, _Graph* other): del self._this self._this = other return self def __dealloc__(self): del self._this
Please note that I need to delete _this because it is a pointer.
Then you need to change your METISGraphReader::read() method to return the selected Graph heap. The prototype of this method should be changed to:
Graph* METISGraphReader::read(std::string path);
Then the Cython shell for it can be written as:
def read(self, path): pathbytes = path.encode("utf-8")
If you do this, there is only one object that is created on the read() heap. A pointer to this object is returned to the read() shell of Cython, which then sets it to a new instance of Graph() . The only thing that is copied is 4 or 8 bytes of the pointer.
Hope this helps!