什么是零成本抽象?这东西不是国人提出的,那就得用人家提出的人的解释:“What you don’t use, you don’t pay for. And further: What you do use, you couldn’t hand code any better.”。这句话什么意思呢?不会为不用的东西付出代价,也就是说,你用到的东西,无法(在不付出代价的情况)做得更好。这句话有点别扭啊。
可以这样理解,在开发过程中,仅仅通过设计就可以达到最佳的性能。更形象的一点比喻就是,不通过技术创新(这玩意得付出成本),仅通过工程方法进行组合调度,实现更优的性能。这个是不是有点越解释越糊涂的意思,大家自己慢慢领悟吧。
零成本抽象,zero overhead abstraction,主要有三点意义:
1、不增加整体开销
这也是在定义中提到的,不能为实现或者达到某种效果,需要这个库那个库,需要增加依赖或者其它成本。如果这样做,就不叫零成本。
2、其代码达到一定程度的最佳
如果抽象无法达到最佳,抽象的意义何在。所以说,一定是在某种程度上达到了一种基本无法用常用的方法进行优化的结果。
3、提升开发设计的整体体验
这个比较难缠,就象人的饮食,萝卜白菜各有所爱。设计开发体验也是如此,但从主流上看,应该是大多数感到了更好的抽象的体验。
在c++中,零成本抽象其实都隐藏在一些细节中,其实有很多的技术,用不好就达不到零成本抽象,用得好,就是零成本抽象。有的技术在编译器不断发展的过程中,自动会对其进行优化,即在背后默默的为开发者实现了零成本抽象。但c++的优势不在于编译器优化,而是在于可以使用代码手动优化,这才是重点。
举一个最简单的例子,初始化,看下面的例子:
#includeclass A
{
public:
A() { std::cout<< "call A"<< std::endl; }
};
class B
{
public:
B() { std::cout<< "call B"<< std::endl; }
};
class Data
{
public:
Data(A a,B b):a_(a),b_(b)//a_(std::move(a)),b_(std::move(b))
{
}
private:
A a_;
B b_;
};
int main()
{
A a;
B b;
Data(a,b);
}
看这个代码,其实就有两个方面需要分析,第一,如果A和B是两个平凡的类(结构体),那么,这个编译器一般优化不会有太大的问题;但是,第二,就是如果这两个类是非平凡的,或者传入的对象是临时对象,这事就比较麻烦了,会拷贝至少两次。所以最好还是用注释的方式,移动构造,这就方便多了。
其实在c++中这种现象非常多,析构函数也有类似的情况:
class Data
{
public:
Data(A a,B b):a_(a),b_(b)
{
}
~Data(){}
//~Data=default;
public:
int a_;
int b_;
};
这两者一个使用自定义的一个析构函数,一个是使用默认的析构函数,后者就比前者要效率高。
再举一个常见的例子,比如开发者都知道,对于一个反复使用对象的工作,如果反复不断的分配对象,分配和析构都需要耗费大量的时间(前提是用得非常频繁),这时,可以通过设计引入池化技术,就可以解决类似问题。其它比如for循环vector里的string优化,可以使用const &的方式(这种方式在发生隐式转换时无效,仍然会造成复制,比如unordered_map 用pair遍历,解决的方式是使用auto&或结构化绑定)。这种很常见,大家可能都不怎么注意,其实你写的多了,就会发现存在非常多的这种代码,不过有些比较老的,就不一定了。
C++基本保证了零成本抽象(虚表就无法保证, C++设计中,有2个语言特性是不遵守零开销原则的,运行时类型识别(RTTI)和异常),它的意思就是说没有使用到的特性不会产生开销。怎么理解这句话呢?你不会模板照样进行c++编程。你不会模板元编程,照样进行模板编程。一些所谓的必须用到的技术,c++的编译器会在编译时对其进行处理,而无须手动干预。像模板技术,很多甚至在编译期就搞定了。在这方面其实Rust做的比c++要好,比如借用和所有权,这个就是单纯设计上搞的,非常好。
做技术就是这样,其实想达到真正的zero overhead abstraction,其实是一个不可能三角。但是,不可能也要朝着优化不断的前进,不可能因为有问题就不去改进,也许在不断的改进过程中,就会有新的突破。
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧