d的打包位.

2021/11/15 23:12:55

本文主要是介绍d的打包位.,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

D打包位

作者:@deadalnix.
内存很慢,约300个周期.
L1:3~4周期,L2:8-14周期.L3数十个周期.
缓存线:64字节.

序号打包位好处
1减少内存浪费
2提高缓存利用率
3最低的CPU成本
4不能替代更好算法

减少实例化对象可节省大量内存!
对齐:确保加载/存储跨缓存行/跨页边界.
未对齐,则cpu性能差,丢失原子性,硬件访问2次.SIGBUS等硬件错误,根据abi定义.
对齐规则:<size_tT.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中的反射生成表.
常规类层次构需要编译时知道所有方法,可动态加类型.无类层次构需要编译时知道所有类型.可态加方法,访问者可创建访问方法表.并用标签来调度,一种方法关闭扩展,用另一方式打开.



这篇关于d的打包位.的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程