首页 > 其他分享 >空基类优化(EBO)

空基类优化(EBO)

时间:2023-05-20 21:01:10浏览次数:45  
标签:struct Tuple int EBO Base template 基类 sizeof 优化

空基类优化基础

为保证同一类型的不同对象地址始终不同,要求任何对象或成员子对象的大小至少为 1 个字节,即使这个类为一个空类。

struct A {}; // 只包含类型成员、非虚函数或静态成员变量   
                              
int main()               
{     
    DBG_LOG("%d", sizeof(A)); // 输出为1
}
struct A {};    

struct Derived
{
    int i;
    A a;
};

int main()
{
    DBG_LOG("%d", sizeof(Derived)); // 输出为8
}

当空类作为类成员时,由于空类占用一个字节,并且内存对齐之后则Derived则需要占用8个字节,存在内存浪费;

运用空基类优化之后:

struct A {};

struct Derived : A
{
    int i;
};

int main()
{
    DBG_LOG("%d", sizeof(Derived)); // 输出为4
}

则空类A不会占用额外的内存空间,内存空间上得到优化;

注:空基类优化可简称为EBO (empty base optimization)或者 EBCO (empty base class optimization)

空基类优化失效

如果首个非静态数据成员的类型与一个空基类的类型相同或者由该空基类派生,则会禁用空基类优化【3】:

struct Base {}; // empty class
 
struct Derived1 : Base
{
    int i;
};
 
struct Derived2 : Base
{
    Base c; // Base, occupies 1 byte, followed by padding for i
    int i;
};
 
struct Derived3 : Base
{
    Derived1 c; // derived from Base, occupies sizeof(int) bytes
    int i;
};
 
int main()
{
    // empty base optimization does not apply,
    // base occupies 1 byte, Base member occupies 1 byte
    // followed by 2 bytes of padding to satisfy int alignment requirements
    static_assert(sizeof(Derived2) == 2*sizeof(int));
    
    // empty base optimization does not apply,
    // base takes up at least 1 byte plus the padding
    // to satisfy alignment requirement of the first member (whose
    // alignment is the same as int)
    static_assert(sizeof(Derived3) == 3*sizeof(int));
}

通过clang -Xclang -fdump-record-layouts -std=c++17 -c TestEBO.cpp可以查看上述代码的内存布局如下:

*** Dumping AST Record Layout
         0 | struct Base (empty)
           | [sizeof=1, dsize=1, align=1,
           |  nvsize=1, nvalign=1]

*** Dumping AST Record Layout
         0 | struct Derived2
         0 |   struct Base (base) (empty)
         1 |   struct Base c (empty) // Derive3的空基类优化失效,Base c的地址偏移为1
         4 |   int i                 // 地址填充对齐,所以int i的地址偏移为4
           | [sizeof=8, dsize=8, align=4,
           |  nvsize=8, nvalign=4]

*** Dumping AST Record Layout
         0 | struct Derived1
         0 |   struct Base (base) (empty)
         0 |   int i
           | [sizeof=4, dsize=4, align=4,
           |  nvsize=4, nvalign=4]

*** Dumping AST Record Layout
         0 | struct Derived3
         0 |   struct Base (base) (empty)
         4 |   struct Derived1 c    // Derive3的空基类优化失效,Derive1 c的地址偏移为4
         4 |     struct Base (base) (empty)
         4 |     int i
         8 |   int i
           | [sizeof=12, dsize=12, align=4,
           |  nvsize=12, nvalign=4]

反之,不是首个非静态数据成员的类型

struct Base {}; // empty class
 
struct Derived1 : Base
{
    int i;
};
 
struct Derived2 : Base
{
    int i;
    Base c;
};
 
struct Derived3 : Base
{
    int i;
    Derived1 c; // derived from Base, occupies sizeof(int) bytes
};

对应的内存布局为:

*** Dumping AST Record Layout
         0 | struct Base (empty)
           | [sizeof=1, dsize=1, align=1,
           |  nvsize=1, nvalign=1]

*** Dumping AST Record Layout
         0 | struct Derived2
         0 |   struct Base (base) (empty)
         0 |   int i  // Derived2空基类优化生效
         4 |   struct Base c (empty)
           | [sizeof=8, dsize=5, align=4,
           |  nvsize=5, nvalign=4]

*** Dumping AST Record Layout
         0 | struct Derived1
         0 |   struct Base (base) (empty)
         0 |   int i
           | [sizeof=4, dsize=4, align=4,
           |  nvsize=4, nvalign=4]

*** Dumping AST Record Layout
         0 | struct Derived3
         0 |   struct Base (base) (empty)
         0 |   int i // Derived2空基类优化生效
         4 |   struct Derived1 c
         4 |     struct Base (base) (empty)
         4 |     int i
           | [sizeof=8, dsize=8, align=4,
           |  nvsize=8, nvalign=4]

C++20 空类优化

C++20提供了[[no_unique_address]] 属性提供了组合模式下空类优化:

struct Base {}; // empty class
 
struct Derived1
{
    [[no_unique_address]]Base b;
    int i;
};
 
int main()
{
    static_assert(sizeof(Derived1) == sizeof(int));
}

Derived1 占用内存为4个字节,空类并未占用额外内存。

空基类应用解析(tuple)

在【4】中介绍了std::tuple的应用实践,而stuple实际也应用了空基类优化:

struct Base1 {}; // 空类
struct Base2 {}; // 空类
struct Base3 {}; // 空类

int main()
{
    DBG_LOG("%d %d", sizeof(std::tuple<Base1, Base2, Base3>),
                     sizeof(std::tuple<Base1, Base2, Base3, int>));
}
// 输出为1 4

本节介绍tuple中如何应用EBO。

tuple的模板参数可以支持接收任意类型,熟悉可变模板参数的同学可以快速实现如下代码:

template<typename ...Args>
struct Tuple;
template<>
struct Tuple<> {
};
    
template<typename Head, typename ...Tail>
struct Tuple<Head, Tail...> {
    Head h;
    Tuple<Tail...> t;
};

此时模板参数类型为空类时存在内存浪费;OK,下一步应用EBO优化得到:

template<typename ...Args>
struct Tuple;
template<>
struct Tuple<> {
};
    
template<typename Head, typename ...Tail>
struct Tuple<Head, Tail...> : private Head, Tuple<Tail...> {
};

但Head可能为int或者final类等不可继承类型,因此引入TupleEle:

template<typename T, bool = std::is_class<T>::value && !std::is_final<T>::value>
struct TupleEle;

template<typename T>
struct TupleEle <T, false> {
    T value;
    T& Get() { return value; }
};

template<typename T>
struct TupleEle <T, true> : private T {
    T& Get() { return *this; }
};

template<typename ...Args>
struct Tuple;

template<>
struct Tuple<> {
};

template<typename Head, typename ...Tail>
struct Tuple<Head, Tail...>: private TupleEle<Head>, private Tuple<Tail...> {
};
struct Empty {};
int main()
{
    Tuple<int, Empty> t{};
    DBG_LOG("%d", sizeof(t)); // 4
}

此时如果送入重复类型,则重复继承了TupleEle<xxx>,导致 派生类转换到基类存在歧义,因此进一步修改为:

template<size_t index, typename T, bool = std::is_class<T>::value && !std::is_final<T>::value>
struct TupleEle;

template<size_t index, typename T>
struct TupleEle <index, T, false> {
    T value;
    T& Get() { return value; }
};

template<size_t index, typename T>
struct TupleEle <index, T, true> : private T {
    T& Get() { return *this; }
};

template<typename ...Args>
struct Tuple;
template<>
struct Tuple<> {
};

template<typename Head, typename ...Tail>
struct Tuple<Head, Tail...>: private TupleEle<sizeof...(Tail), Head>, private Tuple<Tail...> {
};

得益于EBO继承关系,在实现Get<xxx>(tuple)利用模板参数推导,可以在常量时间内获取对应元素,补充Get之后的完整代码如下:

template<size_t index, typename T, bool = std::is_class<T>::value && !std::is_final<T>::value>
struct TupleEle;

template<size_t index, typename T>
struct TupleEle <index, T, false> {
    template<typename U>
    TupleEle(U&& u) : value(std::forward<U>(u)) {};
    T& Get() { return value; }
private:
    T value;
};

template<size_t index, typename T>
struct TupleEle <index, T, true> : private T {
    template<typename U>
    TupleEle(U&& u) : T(std::forward<U>(u)) {};
    T& Get() { return *this; }
};

template<typename ...Args>
struct Tuple;
template<>
struct Tuple<> {
};

template<typename Head, typename ...Tail>
struct Tuple<Head, Tail...>: TupleEle<sizeof...(Tail), Head>, private Tuple<Tail...> {
    template<typename H, typename ...Rest>
    Tuple(H&& h, Rest&&...rest) : TupleEle<sizeof...(Tail), Head>(std::forward<H>(h)),
                                  Tuple<Tail...>(std::forward<Rest>(rest)...){}
    template<size_t index, typename ...Ts>
    friend decltype(auto) Get(Tuple<Ts...>& t);
};

template<size_t index, typename T>
T& GetIndex(TupleEle<index, T>& te) { return te.Get(); }

template<size_t index, typename ...Ts>
decltype(auto) Get(Tuple<Ts...>& t) { return GetIndex<sizeof...(Ts) - index -1>(t); }

在GetIndex调用时通过模板参数推导,index确定,推导出对应T;

参考资料

【1】C++20高级编程

【2】C++ Templates

【3】https://en.cppreference.com/w/cpp/language/ebo

标签:struct,Tuple,int,EBO,Base,template,基类,sizeof,优化
From: https://blog.51cto.com/u_13137973/6317923

相关文章

  • Java中的并发编程:线程池的使用与优化
    在Java编程中,处理并发任务是一项常见而重要的任务。合理地管理线程可以提高程序的性能和响应性。本文将介绍Java中线程池的使用和优化技巧,帮助开发者更好地处理并发编程的挑战。1.线程池的基本概念线程池是一种用于管理线程的技术,它通过预先创建一组线程,并将任务分配给这些线程来......
  • DC NXT 综合优化策略
    综合优化策略compile_ultra designwarelibrary数据路径优化CPR可通过compile命令使用,但必须通过-map_effort high选项显式调用组合逻辑复制始终启用,并根据需要进行以改进时序。没有控制来关闭它,寄存器复制也可用。库分析基于库中可用单元的简单布尔函数......
  • Nginx一网打尽:动静分离、压缩、缓存、黑白名单、跨域、高可用、性能优化...
    干货!文章有点长,建议先收藏引言一、性能怪兽-Nginx概念深入浅出二、Nginx环境搭建三、Nginx反向代理-负载均衡四、Nginx动静分离五、Nginx资源压缩六、Nginx缓冲区七、Nginx缓存机制八、Nginx实现IP黑白名单九、Nginx跨域配置十、Nginx防盗链设计十一、Nginx大文件传输配置十二、Ngi......
  • 基于GA遗传优化的CDVRP,CVRP,DVRP,TSP以及VRPTW常见路径优化问题求解matlab仿真
    1.算法仿真效果matlab2022a仿真结果如下:        TSP最优路径TSP最优路径TSP最优路径BestRoute:0->2->10->5->3->6->9->1->4->7->8->0TotalDistance=95.275km  DVRP最优路径DVRP最优路径DVRP最优路径总路程=19......
  • 基于GA遗传优化的CDVRP,CVRP,DVRP,TSP以及VRPTW常见路径优化问题求解matlab仿真
    1.算法仿真效果matlab2022a仿真结果如下:TSP最优路径TSP最优路径TSP最优路径BestRoute:0->2->10->5->3->6->9->1->4->7->8->0TotalDistance=95.275kmDVRP最优路径DVRP最优路径DVRP最优路径总路程=198.801kmBestRoute:0->10->......
  • generator 1(矩阵优化递推式+10倍增优化)
     x1,bx2(开始值) ......
  • vite打包中性能优化方面
    1、静态文件按类型分包build中添加如下代码:build:{rollupOptions:{output:{chunkFileNames:'static/js/[name]-[hash].js',entryFileNames:'static/js/[name]-[hash].js',assetFileNames:'static/[ext]/[name]-[hash].......
  • 基于PSO优化的OFDM系统PAPR抑制PTS算法MATLAB仿真
    1.算法仿真效果matlab2022a仿真结果如下:    2.算法涉及理论知识概要       部分传输序列(PartialTransmitSequence,PTS)由于其不受载波数量限制,并且能够有效的,无失真的降低OFDM信号峰均比,而受到广泛关注。部分传输序列算法(PTS)最初是由S.H.Muller和J.B.H......
  • 记一次 Oracle 下的 SQL 优化过程
    1.介绍事情是这样的,UAT环境的测试小伙伴向我扔来一个小bug,说是一个放大镜的查询很慢,转几分钟才出数据,我立马上开发环境试了一下,很快啊我说......
  • 接口优化
    转载:https://mp.weixin.qq.com/s/0ez_mkyr0i4MZd7DEN7M8A 接口性能优化对于从事后端开发的同学来说,肯定再熟悉不过了,因为它是一个跟开发语言无关的公共问题。该问题说简单也简单,说复杂也复杂。有时候,只需加个索引就能解决问题。有时候,需要做代码重构。有时候,需要增加缓存。......