d的打包位.
D打包位
作者:@deadalnix.
内存很慢,约300个周期.
L1:3~4周期,L2:8-14周期.L3数十个周期.
缓存线:64字节.
序号 | 打包位好处 |
|---|---|
1 | 减少内存浪费 |
2 | 提高缓存利用率 |
3 | 最低的CPU成本 |
4 | 不能替代更好算法 |
减少实例化对象可节省大量内存!
对齐:确保加载/存储不跨缓存行/跨页边界.
未对齐,则cpu性能差,丢失原子性,硬件访问2次.SIGBUS等硬件错误,根据abi定义.
对齐规则:<size_t为T.sizeof.>size_t的为size_t.sizeof,由编译器分解访问内存.构,则是每个字段的最大对齐.添加填充来符合对齐.
构 S{
极 a;
正 b;
极 c;
}
4+4+4.字节.
构 S{
正 b;
极 a;
极 c;
}
则4+4字节.
填充提示.从高对齐字段开始,知道在哪填充,使用静态断言强制假设alignof/sizeof,
类像构,但有隐式字段:虚表/监控,至少要指针大小对齐.
信息密度
多少实际信息 极:1位信息,8位存储.对象:45位信息,64位存储.转储内存并压缩它.
用消耗内存,来换CPU.通常很划算,用整来存储,在1个整中存储多个元素,按位操作元素std.bitmanip可帮助.
struct A
{
mixin(bitfields!(
正, "x", 30,
极, "y", 1,
极, "z", 1));
}
现在x最大10亿,总为1个整.字段不再是原子的了.
enum ReadMask = (1 << S) - 1;
enum WriteMask = ReadMask << N;
@property uint entry() {
return (data >> N) & ReadMask;
}
@property void entry(uint val) in {
assert(val & ReadMask == val);
} body {
data = (data & ~WriteMask) | ((val << N) & WriteMask);
}
上面是位字段整,下面是位字段极.
enum Mask = 1 << N;
@property bool entry() {
return (data & Mask) != 0;
}
@property entry(bool val) {
if (val) {
data = data | Mask;
} else {
data = data & ~Mask;
}
}
注意,数据^掩码会翻转位,有时比置位更快.
布局位字段
2个特别点:最右边:仅掩码,最左边:仅移位.
大元素需要大掩码,放在最左边.
极值总是用掩码,最左边用符号<0检查,除非很热,不要放在特殊地方.
想要:1标志,2位枚,29位整怎么搞,如何是最佳布局
enum E { E0, E1, E2, E3 }
struct S {
import std.bitmanip;
mixin(bitfield!(
E, "e", 2,
bool, "flag", 1,
uint, "integral", 29,
));
}
生成:
e = cast(E) (data & 0x03);
flag = (data & 0x04) != 0;
integral = data >> 3;
未用位有时,不需要整个位字段,用UINT,"",29创建无名字段,外部用uint,"_derived",29调用构/子类.加上名字.最好设为私有/受保护,或在私构元素中用,要手动实现其余字段
要求特征:具显式存储的位字段.
class Symbol : Node {
Name name;
Name mangle;
import std.bitmanip;
mixin(bitfields!(
Step, "step", 2,
Linkage, "linkage", 3,
Visibility, "visibility", 3,
InTemplate, "inTemplate", 1,
bool, "hasThis", 1,
bool, "hasContext", 1,
bool, "isPoisoned", 1,
bool, "isAbstract", 1,
bool, "isProperty", 1,
uint, "derived", 18,
));
}
接着:
class Field : Symbol {
// ...
this(..., uint index, ... ) {
// ...
this.derived = index;
// 总真
this.hasThis = true;
}
@property index() const {
// 仅可用262,143字段
return derived;
}
}
标记针,@trusted,已知最低有效位为0,对齐决定多少,Log2(T.alignof),对象上至少有3位(32位系统上2位).
标记针/标记类引用,编译器时检查对齐约束,未对齐指针不安全.
enum Color { Black, Red }
struct Link(T) {
import std.bitmanip;
mixin(taggedPointer!(
T*, "child",
Color, "color", 1,
));
}
struct Node(T) {
Link!T left;
Link!T right;
}
实际指针在对象,标记针对象内指向,垃集知道内部指针.
标记针,@system,在地址空间低32位分配,截断指针为32位,限于4Gb.Jemalloc可为你这样做.HHVM用于在X86上生成代码,最高16位为零.劫持他们!,混淆GC!,不要段错误.
使用上下文,对冷但常重复使用数据,如,一般不关心实际值的编译器中的标识符,上下文存储标识符,提供32位与128位的唯一标识.可用整比较,可为自己的哈希,使GC开心:更少指针,更多不扫描!
struct Name {
private:
uint id;
this(uint id) {
this.id = id;
}
public:
string toString(const Context c) const {
return c.names[id]
}
immutable(char)* toStringz(const Context c) const {
auto s = toString();
assert(s.ptr[s.length] == '\0', "期望0结尾串");
return s.ptr;
}
}
//e
class Context {
private:
string[] names;
uint[string] lookups;
public:
auto getName(const(char)[] str) {
if (auto id = str in lookups) {
return Name(*id);
}
// 复制时,确保0在尾.
import std.string;
auto s = str.toStringz()[0 .. str.length];
auto id = lookups[s] = cast(uint) names.length;
names ~= s;
return Name(id);
}
}
预填充上下文,编译时固定些标识很有用,无需查找即可使用,生成标识符,对象.d,链接/版本/域/属性
enum Prefill = [
// 链接
"C", "D", "C++", "Windows", "System",
// 生成
"init", "length", "max", "min",
"ptr", "sizeof", "alignof",
// 域
"exit", "success", "failure",
// 定义在对象
"object", "size_t", "ptrdiff_t", "string",
"Object",
"TypeInfo", "ClassInfo",
"Throwable", "Exception", "Error",
// 属性
"property", "safe", "trusted", "system", "nogc",
// ...
];
//e
auto getNames() {
import d.lexer;
auto identifiers = [""];
foreach(k, _; getOperatorsMap()) {
identifiers ~= k;
}
foreach(k, _; getKeywordsMap()) {
identifiers ~= k;
}
return identifiers ~ Reserved ~ Prefill;
}
enum Names = getNames();
接着:
auto getLookups() {
uint[string] lookups;
foreach(uint i, id; Names) {
lookups[id] = i;
}
return lookups;
}
enum Lookups = getLookups();
//
template BuiltinName(
string name,
) {
private enum id = Lookups
.get(name, uint.max);
static assert(
id < uint.max,
name ~ "非内置名.",
);
enum BuiltinName = Name(id);
}
更多上下文!编译器中跟踪位置,环境中注册文件,分配从N到N+sizeof(file)的值域,对应文件中每个字节位置!为mixin(D)/宏(C++)加标志,环境中注册扩展.
用例:发出调试信息,错误信息.性能与错误无关,调试的访问模式大多可预测,
用元素缓存,线性搜索(8个元素),二分搜索从位置查找文件/行.环境存储文件边界和行位置.
位置31位+1位标志:2Gb源码与2Gb的宏/mixin.用于标记/表达式/符号/语句的一对位置.词分按令牌长泵位置.
多态,标签引用.
封装多种引用类型,可提供转发到元素方法:用反射,避免查找虚表/级联加载.引用对象无通用布局.
元素数受对齐限制,在X64上轻松达到8个.LLVM的调用/(invoke)
template TagFields(uint i, U...) {
import std.conv;
static if (U.length == 0) {
enum TagFields = "\n\t" ~ T.stringof ~ " = "
~ to!string(i) ~ ",";
} else {
enum S = U[0].stringof;
static assert(
(S[0] & 0x80) == 0,
S ~ "不能以统一码开始",
);
static assert(
U[0].sizeof <= size_t.sizeof,
"元素应比指针小",
);
import std.ascii;
enum Name = (S == "typeof(null)")
"Undefined"
: toUpper(S[0]) ~ S[1 .. $];
enum TagFields = "\n\t" ~ Name ~ " = "
~ to!string(i) ~ "," ~ TagFields!(i + 1, U[1 .. $]);
}
}
//
mixin("enum Tag {" ~ TagFields!(0, U) ~ "\n}");
import std.traits;
alias Tags = EnumMembers!Tag;
import std.typetuple;
alias TagTuple = TypeTuple!(uint, "tag", EnumSize!Tag);
接着:
struct TaggedRef(U...) {
private:
import std.bitmanip;
mixin(taggedPointer!(
void*, "ptr", TagTuple));
public:
auto get(Tag E)() in {
assert(tag == E);
} body {
static union Helper {
void* __ptr;
U u;
}
return Helper(ptr).u[E];
}
//
template opDispatch(string s, T...) {
auto opDispatch(A...)(A args) {
final switch(tag) {
foreach(T; Tags) {
case T:
auto r = get!T();
return mixin("r." ~ s)(args);
}
}
}
}
}
值类型多态,所有子类型给定条件下都符合,用标签来区分,用很好的API包装整个过程,在漂亮外表下隐藏暴行,这是D的力量.示例:表示D类型
template SizeOfBitField(T...) {
static if (T.length < 2) {
enum SizeOfBitField = 0;
} else {
enum SizeOfBitField =
T[2] + SizeOfBitField!(T[3 .. $]);
}//字段大小
}
enum EnumSize(E) =
computeEnumSize!E();
//
size_t computeEnumSize(E)() {
size_t size = 0;
import std.traits;
foreach (m; EnumMembers!E) {
size_t ms = 0;
while ((m >> ms) != 0) {
ms++;
}
import std.algorithm;
size = max(size, ms);
}
return size;
}//计算
//
struct TypeDescriptor(K, T...) {
enum DataSize = ulong.sizeof * 8 - 3 - EnumSize!K - SizeOfBitField!T;
import std.bitmanip;
mixin(bitfields!(
K, "kind", EnumSize!K,
TypeQualifier, "qualifier", 3,
ulong, "data", DataSize,
T,
));
static assert(TypeDescriptor.sizeof == ulong.sizeof);
this(K k, TypeQualifier q, ulong d = 0) {
kind = k;
qualifier = q;
data = d;
}
}
值类型多态,类型是TypeDescriptor+间接字段,数据取决于种类,如果不合适,用间接字段.有多种类型:内置-结构-类-别名-函数...,切换常见API来做正确事情.128位.
间接用在:类型要额外空间(函数)或引用符号/聚集/别名,否则null.替换类层次,减小内存消耗,更快运行时(20%).
可嵌套,有效创建层次,类型/表达式/符号是可标识的.标签用来在上面三样中消歧.节省标签,在sdc中可减少70M模板膨胀.
import d.semantic.identifier;
Identifiable i = ...;
i.apply!(delegate Expression(identified) {
alias T = typeof(identified);
static if (is(T : Expression)) {
return identified;
} else {
return getError(
identified,
location,
t.name.toString(pass.context) ~ "不可调用",
);
}
})();
值类型,ABI最多2个字段,直到指针大小.切片!无浮/整插件.常见反模式2指针+极值.std.bigint.BigInt是切片+极值,在内存而非寄存器中传递(差).>1指针,最好用2指针.用1/2指针大小构.
无类多态,创建基构,所有子构将其用作第1字段.包含描述类型标签,标签可是位字段的一部分.在所有子构中用插件.用静态断言来检查是否正确.别名该基.
无类多态,层次结构每叶有标签值,非叶有标签值区间,根匹配所有值,必须在编译时知道层次,用大量插件模板,加样板,大量静态断言.
struct Child {
mixin Parent!Root;
}
struct Root {
mixin Childs!(Child, SubStruct);
}
//
struct SubStruct {
mixin GrandChilds!(
Root,
SubChild,
);
}
struct SubChild {
mixin Parent!SubStruct;
}
无类多态,子项共享父项部分布局,用别名本安全上转,下转为叶子:检查标签值,便宜,简单模式匹配.
下转为子结构:检查标签区间,便宜,无类型标识指针.
虚分发,无虚表,表中取函数指针:每个方法一张表,每个叶子类型一个项,按索引用标签.HHVM用于PHP数组,创建数据结构,是向量/散列图/集合/元组/任何.
普通:每类型1虚表,每方法虚表有1项,加载虚表,再加载函数地址.
无类虚分发:每方法1虚表,每类型虚表有1项.在每函数表中按索引加载并用它.
本地性更好,在不同类型对象上调用相同方法,比相同类型对象上调用不同方法更常见.按类型排序来解决.无需排序即可获得大部分收益.仍有助于预测分支,可用D中的反射生成表.
常规类层次构需要编译时知道所有方法,可动态加类型.无类层次构需要编译时知道所有类型.可态加方法,访问者可创建访问方法表.并用标签来调度,一种方法关闭扩展,用另一方式打开.
浙公网安备 33010602011771号