MicroPython 字符串实习¶
MicroPython 使用字符串实习来保存 RAM 和 ROM。这避免了必须存储相同字符串的重复副本。首先,这适用于代码中的标识符,因为函数或变量名称之类的东西很可能出现在代码的多个位置。在 MicroPython 中,内部字符串称为 QSTR(uniQue STRing)。
QSTR 值(类型为qstr
)是 QSTR 池链表的索引。QSTR 存储它们的长度和其内容的散列,以便在重复数据删除过程中进行快速比较。所有处理字符串的字节码操作都使用 QSTR 参数。
编译时 QSTR 生成¶
在 MicroPython C 代码中,应该在最终固件中驻留的任何字符串都写为MP_QSTR_Foo
. 在编译时,这将评估为qstr
指向 "Foo"
QSTR 池中索引的值。
中的多步骤过程 Makefile
使这项工作有效。概括起来,这个过程分为三个部分:
查找
MP_QSTR_Foo
代码中的所有标记。生成包含所有字符串数据(包括长度和哈希)的静态 QSTR 池。
用
MP_QSTR_Foo
相应的索引替换所有(通过预处理器)。
MP_QSTR_Foo
在两个来源中搜索令牌:
中引用的所有文件
$(SRC_QSTR)
。这是所有 C 代码(即py
,extmod
,ports/stm32
),但不包括第三方代码,例如lib
.附加
$(QSTR_GLOBAL_DEPENDENCIES)
(包括mpconfig*.h
)。
注意: (frozen_mpy.c
由mpy-tool.py生成)有自己的QSTR生成和池。
一些无法使用 MP_QSTR_Foo
语法表示的附加字符串(例如,它们包含非字母数字字符)在变量中
qstrdefs.h
并qstrdefsport.h
通过 $(QSTR_DEFS)
变量显式提供 。
处理发生在以下阶段:
qstr.i.last
是将每个输入文件通过 C 预处理器的串联。这意味着将删除任何有条件禁用的代码,并扩展宏。这意味着我们不会向池中添加不会在最终固件中使用的字符串。因为在这个阶段(由于NO_QSTR
添加了宏QSTR_GEN_CFLAGS
)没有定义MP_QSTR_Foo
它通过这个阶段不受影响。该文件还包括来自预处理器的注释,其中包括行号信息。请注意,此步骤仅使用已更改的文件,这意味着qstr.i.last
将仅包含自上次编译以来已更改的文件中的数据。qstr.split
是 在 qstr.i.last 上运行后创建的空文件。它只是用作依赖项来指示该步骤已运行。此脚本为每个输入 C 文件输出一个文件 ,仅包含匹配的 QSTR。每个 QSTR 都打印为. 此步骤是将现有文件与.makeqstrdefs.py split
genhdr/qstr/...file.c.qstr
,Q(Foo)
.qstr.i.last
qstrdefs.collected.h
是genhdr/qstr/*
使用连接的输出。现在这是在代码中找到的全套's,现在格式化为,每行一个,并带有重复项。仅当 qstrs 集已更改时,才会更新此文件。QSTR 数据的散列被写入另一个文件 ( ),这允许它跨构建跟踪更改。makeqstrdefs.py cat
MP_QSTR_Foo
Q(Foo)
qstrdefs.collected.h.hash
生成一个枚举,其中的每个条目都将 a 映射
MP_QSTR_Foo
到它对应的索引。它qstrdefs.collected.h
与连接qstrdefs*.h
,然后将每一行从 转换为Q(Foo)
,"Q(Foo)"
以便它们不变地通过预处理器。然后预处理器用于处理qstrdefs*.h
. 然后将转换还原为Q(Foo)
,并保存为qstrdefs.preprocessed.h
。qstrdefs.generated.h
是 的输出makeqstrdata.py
。对于Q(Foo)
qstrdefs.preprocessed.h 中的每个 (加上一些额外的硬编码),它输出 .QDEF(MP_QSTR_Foo, (const byte*)"hash" "Foo")
.
然后在主编译中,会发生两件事qstrdefs.generated.h
:
在 qstr.h 中,每个 QDEF 成为枚举中的一个条目,它
MP_QSTR_Foo
可供代码使用并等于 QSTR 表中该字符串的索引。在 qstr.c 中,实际的 QSTR 数据表是作为
mp_qstr_const_pool->qstrs
.
运行时 QSTR 生成¶
可以在运行时创建额外的 QSTR 池,以便向其中添加字符串。例如,代码:
foo[x] = 3
需要为 的值创建一个 QSTR,x
以便“加载属性”字节码可以使用它。
此外,在编译 Python 代码时,标识符和文字需要创建 QSTR。注意:只有少于 10 个字符的文字才能成为 QSTR。这是因为堆上的常规字符串始终占用最少 16 个字节(一个 GC 块),而 QSTR 允许将它们更有效地打包到池中。
QSTR 池(以及存储字符串数据的底层“块”)在堆上按需分配,具有最小大小。