- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
上下文:我构建了一个树数据结构,在 cython 的节点中存储单个字符。现在我想知道如果我实习所有这些角色是否可以节省内存。以及我是否应该使用 Py_UNICODE 作为变量类型或常规 str。这是我使用 Py_UNICODE 的精简 Node 对象:
from libc.stdint cimport uintptr_t
from cpython cimport PyObject
cdef class Node():
cdef:
public Py_UNICODE character
def __init__(self, Py_UNICODE character):
self.character = character
def memory(self):
return <uintptr_t>&self.character
首先尝试查看字符是否被自动保留。如果我在 Python 中导入该类并创建具有不同或相同字符的多个对象,这些是我得到的结果:
a = Node("a")
a_py = a.character
a2 = Node("a")
b = Node("b")
print(a.memory(), a2.memory(), b.memory())
# 140532544296704 140532548558776 140532544296488
print(id(a.character), id(a2.character), id(b.character), id(a_py))
# 140532923573504 140532923573504 140532923840528 140532923573504
因此,我得出的结论是 Py_UNICODE 不会自动实习,并且在 python 中使用 id() 不会给我实际的内存地址,而是一个副本的地址(我想 python 会自动实习单个 unicode 字符,然后只是将其内存地址返回给我)。
接下来我尝试在使用 str 时做同样的事情。只需将 Py_UNICODE 替换为 str didn't work ,这就是我现在尝试执行的操作:
%%cython
from libc.stdint cimport uintptr_t
from cpython cimport PyObject
cdef class Node():
cdef:
public str character
def __init__(self, str character):
self.character = character
def memory(self):
return <uintptr_t>(<PyObject*>self.character)
这些是我得到的结果:
...
print(a.memory(), a2.memory(), b.memory())
# 140532923573504 140532923573504 140532923840528
print(id(a.character), id(a2.character), id(b.character), id(a_py))
# 140532923573504 140532923573504 140532923840528 140532923573504
基于此,我首先认为单字符 str 也被驻留在 cython 中,并且 cython 不需要从 python 复制字符,这解释了为什么 id() 和 .memory() 给出相同的地址。但后来我尝试使用更长的字符串得到了相同的结果,从中我可能不想得出结论,更长的字符串也会被自动保留?如果我使用 Py_UNICODE,我的树使用的内存也会更少,因此如果 str 被保留,但 Py_UNICODE 不是,则没有多大意义。有人可以向我解释这种行为吗?我该如何去实习?
(我正在 Jupyter 中对此进行测试,以防产生影响)
编辑:删除了不必要的节点 ID 比较,而不是字符比较。
最佳答案
您这边有误会。 PY_UNICODE
不是一个 python 对象 - 它是一个 typedef for wchar_t
.
只有字符串对象(至少其中一些)会被保留,而不是 wchar_t
类型的简单 C 变量(或者事实上任何 C 类型)。这也不是没有任何意义:wchar_t
很可能是 32 位大,而保留指向内部对象的指针则需要 64 位。
因此,只要 self
不同,变量 self.character
(类型为 PY_UNICODE
)的内存地址就永远不会相同对象(无论 self.character 具有哪个值)。
另一方面,当您在纯 python 中调用 a.character
时,Cython 知道该变量不是简单的 32 位整数,并会自动转换它(character
是产权吗?)通过 PyUnicode_FromOrdinal
到 unicode 对象。返回的字符串(即 a_py
)可能是“interned”,也可能不是。
当该字符的代码点 is less than 256 (即 latin1),它得到 kind of interned - 否则不会。仅包含一个字符的前 256 个 unicode 对象具有 special place - 不是the same as other interned strings (因此在上一节中使用了“interned”)。
考虑:
>>> a="\u00ff" # ord(a) = 255
>>> b="\u00ff"
>>> a is b
# True
但是
>>> a="\u0100" # ord(a) = 256
>>> b="\u0100"
>>> a is b
# False
<小时/>
这里的关键是:使用 PY_UNICODE
- 即使没有实习,它也比实习字符串/unicode对象更便宜(4字节)(8字节用于引用+一旦内存用于内部对象)并且比非内部对象便宜得多(这可能发生)。
或者更好,正如@user2357112所指出的,使用Py_UCS4
确保 4 字节的大小得到保证(需要能够支持所有可能的 unicode 字符) - wchar_t
可以小至 1 字节(即使这在现在可能很不寻常) )。如果您对所使用的字符了解更多,则可以回退到 Py_UCS2
或 Py_UCS1
。
但是,当使用 Py_UCS2
或 Py_USC1
时,必须考虑到,Cython 将不支持与 Py_UCS4 的情况一样从/到 unicode 的转换
(或已弃用的 Py_UNICODE
),并且必须手动完成,例如:
%%cython
from libc.stdint cimport uint16_t
# need to wrap typedef as Cython doesn't do it
cdef extern from "Python.h":
ctypedef uint16_t Py_UCS2
cdef class Node:
cdef:
Py_UCS2 character_
@property
def character(self):
# cython will do the right thing for Py_USC4
return <Py_UCS4>(self.character_)
def __init__(self, str character):
# unicode -> Py_UCS4 managed by Cython
# Py_UCS4 -> Py_UCS2 is a simple C-cast
self.character_ = <Py_UCS2><Py_UCS4>(character)
还应该确保使用 Py_USC2
确实节省了内存:CPython 使用 pymalloc它具有 8 个字节的对齐方式,这意味着例如的对象20字节仍将使用24字节(3*8)内存。另一个问题是来自 C 编译器的结构的对齐,对于
struct A{
long long int a;
long long int b;
char ch;
};
sizeof(A)
是 24,而不是 17(请参阅 live)。
如果确实在这两个字节之后,那么还有更大的鱼要炸:不要将节点设为 Python 对象,因为它会带来 16 个字节的开销,用于不需要的多态性和引用计数 - 这意味着整个数据结构应该用C编写,整个wrappend用Python编写。然而,这里还要确保以正确的方式分配内存:通常的 C 运行时内存分配器具有 32 或 64 字节对齐,即分配较小的大小仍然会导致使用 32/64 字节。
关于python - cython 中 str 和 Py_UNICODE 的驻留和内存地址,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56192032/
你信吗?我有一个这样的循环(请原谅任何错误,我不得不大量编辑大量信息和变量名称,相信我它有效)。 ...旧示例已删除,请参见下面的代码... 如果我将那些中间的 str = "Blah\(odat.c
我正在做一个本地测试来比较 C# 中 String 和 StringBuilder 的 Replace 操作性能,但是对于 String 我使用了以下代码: String str = "String
我想知道为什么str += "A"和 str = str + "A"有不同的表现。 在实践中, string str = "cool" for(int i = 0; i approximately
我有一个类型列表 [("['106.52.116.101']", 1), ("['45.136.108.85']", 1)] 并想将其转换为 [('106.52.116.101', 1), ('45.
我有一个类型列表 [("['106.52.116.101']", 1), ("['45.136.108.85']", 1)] 并想将其转换为 [('106.52.116.101', 1), ('45.
我正在遍历 HashMap并通过一些本地变量中的模式匹配将值放入其中。 委托(delegate)者 fn lyrics_no_bottles(song_template:&mut String){
如果字符串(短语)中只有元音,它(对我而言)说True;否则说 False。我不明白为什么它总是返回 False,因为 (x >= x) 总是返回 True。我感谢任何人检查此查询的解决方案。 (st
我有代码以某种方式转换字符串引用,例如取第一个字母 trait Tr { fn trim_indent(self) -> Self; } impl Tr for &'a str { f
我正在学习指针,这是我的代码。我定义了一个指向 char(实际上是字符串)的指针 *str 和一个指向 int *a 的指针,它们的定义方式相同。我认为 str 和 a 都应该是一个地址,但是当我试图
为什么我会收到错误消息?我已经正确添加了类型,对吗? Invalid index type "str" for "Union[str, Dict[str, str]]"; expected type
你知道下面两个函数是否等价吗? function validate(str) { return ( ['null','','undefined'].indexOf(str) [v, valida
我正在解决这里的 Dataquest 问题:https://app.dataquest.io/m/293/data-cleaning-basics/5/removing-non-digit-chara
我有一个字符串列表,如下所示: ["A TB", "A-R TB", "B TB", "B-R TB", "C TB", "C-R TB"...] 但字符串的顺序是随机的。我如何编写一个将元素配对的函
我正在尝试将此函数从使用 split 改为使用 str.extract (正则表达式)。 def bull_lev(x): spl = x.rsplit(None, 2)[-2].strip(
给定这样的数据结构: [{'a':1, 'b': 2}, {'c':3 }, {'a':4, 'c':9}, {'d':0}, {'d': 0, 'b':6}] 目标是解析数据以产生: {'a': 2
给定这样的数据结构: [{'a':1, 'b': 2}, {'c':3 }, {'a':4, 'c':9}, {'d':0}, {'d': 0, 'b':6}] 目标是解析数据以产生: {'a': 2
s = 'someString' s = QTreeWidgetItem(s) print(s.text(0)) # 0 being 'column' 输出: 's' 如果我对另一
黑白有什么区别: function(char* str ) function(char* str[] ) function(char str[] ) 它们是如何被调用的(通过什么类型的string/c
我试过谷歌搜索但找不到准确的答案,所以请允许我尝试在这里提问。如果问题看起来不合适,请告诉我,我会删除它。 在 JS 中,您可以通过三种不同的方式编写特定的内置功能: 字符串长度 str.toStri
我有这段代码(我的 strlen 函数) size_t slen(const char *str) { size_t len = 0; while (*str) {
我是一名优秀的程序员,十分优秀!