(这个editor今天有毛病,把我的format全搞乱了,抱歉!)

Inheritance 是OOP 的一个重要特征。虽然业界有许多同行不喜欢inheritance,但是正确地使用inheritance是一个应用层面和架构层面的重要设计决定。 大量使用inheritance,尤其在类似std container 中使用,会对程序性能产生何等影响呢?

从我个人的经验来看,constructor对创建具有深层inheritance链的class,有很大的影响。 如果应用容许,最好使用没有constructor的基类。下面举个例子:

struct __declspec(novtable) ITest1

{ virtual void AddRef() = 0;

    virtual void Release() = 0;

    virtual void DoIt(int x) = 0; };

class CTest: public ITest1

{

int ref;

public: inline CTest() { ref = 0; }

inline void AddRef() { ++ref; }

inline void Release() {--ref; }

inline void DoIt(int x) {ref *= x; }

inline void AddRef2() { ++ref; }

inline void Release2() {--ref; }

inline void DoIt2(int x) {ref *= x; }

static void TestPerf(int loop); };

这是个dummy程序,然而在COM中确是再常见不过。如果我们要大量创建并使用CTest,有经验的程序员应该看出,ITest1 完全不需要constructor。 根据C++ 说明书,ITest1因为有虚拟函数,属于“非简单构造类”,编译必须产生一个constructor,其唯一的目的是设置ITest1的vtbl (虚拟函数表)。

然而interface的唯一作用是被继承,所以其vtbl一定是被其继承类设置。编译在这种情况下没必要生成constructor。 微软在设计ATL时认识到这一点,推出自己的方案来躲避C++官方SPEC的缺陷:VC++提供了novtableclass modifier,告诉编译:我不需要你的constructor. 然而我在VS 2010中的测试结果却令人失望:

ITest1的constructor 仍然被生成了,只是它没有将vtbl赋值而已,这对增进基类构造的性能实为杯水车薪之举。 下面我们看看这个“毫无用处的constructor”对性能的影响。 我们权且拿出另一个不需要虚拟函数的ITestPOD (POD的意思是“数据而已”)来做比较:

struct ITest1POD

{ inline void AddRef() { }

inline void Release() { }

inline void DoIt(int x) { } };

ITestPOD当然不能完全作interface用(interface必须用虚拟函数),仅仅为了测试。然后,我们设计一个继承类,和上面的CTest功能完全一样:

class CTestPOD: public ITest1POD

{

int ref;

public: inline CTestPOD() { ref = 0; }

inline void AddRef() { ++ref; }

inline void Release() {--ref; }

inline void DoIt(int x) {ref *= x; }

};

我们的目的是用这个CTestPOD来和CTest作一番苹果与苹果的比较:

void CTest::TestPerf(int loop)

{

clock_t begin = clock();

for(int i = 0; i < loop; ++i) //loop1

{

CTestPOD testPOD; // line1

testPOD.AddRef();

testPOD.DoIt(0);

testPOD.Release();

}

clock_t end = clock();

printf("POD time: %f \n",double(end - begin) / CLOCKS_PER_SEC);

begin = clock();

for(int i = 0; i < loop; ++i) //loop2

{

CTest test; // line2

test.AddRef2();

test.DoIt2(0);

test.Release2();

}

end = clock();

printf("Interface time: %f \n",double(end - begin) / CLOCKS_PER_SEC);

}

上面的loop1loop2的唯一区别在line1和line2,为了避免用虚拟函数,我特意给CTest准备了AddRef2,DoIt2,Release2,三个同样的但却是非虚拟的函数,为的是遵循性能测试的一大原理:compare apple to apple。

我将loop设为10万,测试结果显示,loop2比loop1的速度低了20% 左右。从生成的代码来看,唯一的区别是CTest的constructor调用了编译自动生成的ITest1 的constructor。这个constructor没有任何作用,却白占了许多CPU周期。一个好的编译,应该是可以把这个constructor裁剪掉的,这个靠我们自己去搜索了。

总结

在应用inheritance时,除去基类里无用的constructor,对大量构造的object的性能来说,会有明显的影响。不幸的是,微软的__declspec(novtable) class modifier对解决这个问题没有提供任何帮助。在设计海量存储的object的应用中,我们应该尽量用POD来做其基类,避免上面CTest类那样明显的性能漏洞。

2014-9-3 西雅图

C++ 性能剖析 (四):Inheritance 对性能的影响的更多相关文章

  1. C++ 性能剖析 (一)

    C++ 性能剖析 (一) 性能问题也不是仅仅用“技术”可以解决的,它往往是架构,测试,假设等综合难题.不过,对于一个工程师来说,必须从小做起,把一些“明显”的小问题解决.否则的话积小成多,千里堤坝,溃 ...

  2. 快速学习C语言二: 编译自动化, 静态分析, 单元测试,coredump调试,性能剖析

    上次的Hello world算是入门了,现在学习一些相关工具的使用 编译自动化 写好程序,首先要编译,就用gcc就好了,基本用法如下 gcc helloworld.c -o helloworld.o ...

  3. CUDA学习笔记(四)——CUDA性能

    转自:http://blog.sina.com.cn/s/blog_48b9e1f90100fm5h.html 四.CUDA性能 CUDA中的block被划分成一个个的warp,在GeForce880 ...

  4. PDF.NET开发框架性能剖析

    PDF.NET开发框架性能剖析 前俩天发布了 关于PDF.NET开发框架对Mysql Sqlite PostgreSQL数据库分页支持的个人看法 ,说明了本人对框架的一些介绍和看法.今天我们一起思考一 ...

  5. ANTS Performance Profiler 8:支持对Web请求、异步代码和WinRT的性能剖析

    下载与激活:http://download.csdn.net/detail/lone112/6734291 离线激活   位于英国的Red Gate Software有限公司最近发布了ANTS Per ...

  6. 使用PerfView监测.NET程序性能(四):折叠,过滤和时间范围选择

    在上一篇文章中,我们使用了Perfview的分组功能.分组功能旨在对某些函数按照某个格式进行分组,以减少视图中的各种无关函数的数量.但仅有分组还不够,有时我们想将一些函数调用信息按某些条件过滤掉,例如 ...

  7. MySQL性能剖析工具(pt-query-digest)【转】

    这个工具同样来自percona-toolkit 该工具集合的其他工具 MySQL Slave异常关机的处理 (pt-slave-restart)  验证MySQL主从一致性(pt-table-chec ...

  8. Python脚本性能剖析

    ################### #Python脚本性能剖析 ################### cProfile/profile/hotshot用于统计Python脚本各部分运行频率和耗费 ...

  9. Linux的系统级性能剖析工具-perf

    一直在找个靠谱且易用的性能分析工具,perf 貌似是很符合要求的,先给出阿里整理的几篇文档: Linux的系统级性能剖析工具-perf-1.pdf Linux的系统级性能剖析工具-perf-2.pdf ...

随机推荐

  1. 权重随机算法的java实现

    一.概述 平时,经常会遇到权重随机算法,从不同权重的N个元素中随机选择一个,并使得总体选择结果是按照权重分布的.如广告投放.负载均衡等. 如有4个元素A.B.C.D,权重分别为1.2.3.4,随机结果 ...

  2. iOS cell自动换行

    // //  DynamicHeightsViewController.h //  DynamicHeights // //  Created by Matt Long on 9/22/09. //  ...

  3. 【Linux】鸟哥的Linux私房菜基础学习篇整理(一)

    最近,一直在写PPC的模拟器和汇编器,也在做设计.所以重新看了看<鸟哥的Linux私房菜>,还是有好多命令不太熟悉.就打算写几篇blog记下来. 1. nl [-bnw] filename ...

  4. CF 85D Sum of Medians (五颗线段树)

    http://codeforces.com/problemset/problem/85/D 题意: 给你N(0<N<1e5)次操作,每次操作有3种方式, 1.向集合里加一个数a(0< ...

  5. (转载)PHP中刷新输出缓冲

    (转载)http://www.cnblogs.com/mutuan/archive/2012/03/18/2404957.html PHP中刷新输出缓冲buffer是一个内存地址空间,Linux系统默 ...

  6. Contains Duplicate III —— LeetCode

    Given an array of integers, find out whether there are two distinct indices i and j in the array suc ...

  7. 数学概念——H 最美素数

    H - 数论,晒素数 Time Limit:1000MS     Memory Limit:32768KB     64bit IO Format:%I64d & %I64u Submit S ...

  8. [转载]软件测试之Web测试经典总结

    转载自:软件测试之Web测试经典总结 基于Web的系统测试在基于Web的系统开发中,如果缺乏严格的过程,我们在开发.发布.实施和维护Web的过程中,可能就会碰到一些严重的问题,失败的可能性很大.而且, ...

  9. 详解udev

    如果你使用Linux比较长时间了,那你就知道,在对待设备文件这块,Linux改变了几次策略.在Linux早期,设备文件仅仅是是一些带有适当的属性集的普通文件,它由mknod命令创建,文件存放在/dev ...

  10. vue-cli 中 使用vue-resource 输出后台数据

    阅读此文前,请了解vue-cli 组件如何使用  http://www.cnblogs.com/pearl07/p/6252116.html 1,mock(模拟)后台数据(新建data.Json文件) ...