• C++ 性能剖析 (二):值语义 (value semantics)


     

    Value Semantics (值语义) 是C++的一个有趣的话题。

    什么是值语义? 简单的说,所有的原始变量(primitive variables)都具有value semantics. 也可以说,它们可以对应传统数学中的变量。有人也称它为POD (plain old data), 也就是旧时的老数据(有和 OOP 的新型抽象数据对比之意)。

    对一个具有值语义的原始变量变量赋值可以转换成内存的bit-wise-copy。

    对于用户定义的类呢?我们说,如果一个type X 具有值语义, 则:

    1)X 的size在编译时可以确定。

         这一点看似自然,其实在C++里有许多变量的size编译时无法确定。比如我在reference 三位一体里提到的polymorphic 变量,因为是“多身份”的,其(内容)的size是动态的。

    2)将X的变量x,赋值与另一个变量y,无须专门的 = operator,简单的bit-wise-copy 即可。

    3)当上述赋值发生后,x和y脱离关系:x 和 y 可以独立销毁, 其内存也可以独立释放。

    了解第三点很重要,比如下面的class A就不具备值语义:

    class A

    {

         char * p;

         public:

              A() { p = new char[10]; }

              ~A() { delete [] p; }

    };

    A 满足1和2,但不满足3。因为下面的程序会出错误: 

    Foo()

    {

        A a;

        A b = a;

    } // crash here

    改进的方法是定义一个A::operator=(constA &),并且用reference counting 的技术来保护指针,实现起来并不简单。所以我们说一旦一个class 失去了value semantics, 它也就失去了简单明了的 = 语义。

    从上面的分析可以得出结论,value semantics 有个简单的 = , 也正是数学意义上的 =

    学过Java, C#, 和JavaScript的程序员都知道,这些语言里的object都不具有值语义,因为它们都是指针,= 并不copy内容。也不满足条件3。

    那么value semantics 对C++性能有什么影响呢?我觉得有以下几方面:

    1)std 库是基于值语义 的。std container 包含的元素,都具有值语义. 不理解这一点,就不能正确使用std,也不会对std的性能,做出合理预期。

    2)简单的bit-wise-copy 赋值语句一般会提高赋值性能,因为它不需要特殊的 = operator 了。在使用std container 时,会有大量的copy 或assignment。 bit-wise-copy对于小的变量通常比函数划算得多。

    3)具有值语义的,size 不大的变量,在stack里,作为auto变量,传递,拷贝,释放全部和原始变量的用法完全一致,既好用,一般也具有优良的性能。动态语言缺乏这个(值语义)的语言构造和能力,(C# 有有限地支持:c# struct),所以速度上很难优化。

    4)注意,在设计具有值语义的类时,不要保留无用的destructor. destructor 的存在,使得你的类的语义和原始类有了本质的区别,C++ 编译会为此处心积虑地添加管理代码,使得一个简单的函数复杂化(会专门著文论证), 并且严重影响性能。这些当然是有附加值的,但是必须是设计需求的,而不是简单照搬的。

    5)不要保留无用的destructor这点,对使用 std 时更重要。我会专门论述。不要有臃余无用的 destructor 对任何类都是适用的,而对于有大量 copy, assignment 的 std container 尤为重要!

    那么,什么样的类没有值语义呢?我们不妨称这种型为 none-value-semantics type (NVST).

    1)有virtual function 的类

    2)包含NVST成员的类

    3)NVST 的衍生类(derived classed)

    4)定义了自己的 = operator 的类

    5)继承 virtual 基类的衍生类

    2014-6-21 西雅图

  • 相关阅读:
    spark 读取mongodb失败,报executor time out 和GC overhead limit exceeded 异常
    在zepplin 使用spark sql 查询mongodb的数据
    Unable to query from Mongodb from Zeppelin using spark
    spark 与zepplin 版本兼容
    kafka 新旧消费者的区别
    kafka 新生产者发送消息流程
    spark ui acl 不生效的问题分析
    python中if __name__ == '__main__': 的解析
    深入C++的new
    NSSplitView
  • 原文地址:https://www.cnblogs.com/ly8838/p/3929025.html
Copyright © 2020-2023  润新知