2009年1月30日星期五

消除虚函数的调用

在Nebula2中,平台抽象是通过子类和虚函数来实现的。例如,nGfxServer2类实现了一个平台无关的图形接口,而子类nD3D9Server通过覆写父类的虚方法实现了Direct3D9版本。客户代码和nGFxServer2接口交互并不需要关心是通过Direct3D实现渲染的或者是其它的渲染API。

根据不同的平台,虚函数的调用性能从一般糟到很糟,因为额外的内存查找可能导致缓存无效,flush the instruction pipeline, disable branch prediction,etc...
虚函数调用一直是多态最快的方式,但这对于平台抽象一般不是必须的,编译时多态就足够了。

Nebula3通过typedef-ing来实现平台抽象,这样消除了很多虚方法的调用并且在不牺牲平台独立的情况下把一些常用的方法定义为内联方法。

首先编写一个基类定义类的接口。这个基类通常没有虚函数(除了析构函数,这个类一般是继承Core::RefCounted)。一个平台相关的类继承了基类,用平台相关的代码覆写了基类的大部分或者全部的方法。最后,把一个平台相关的类用一个合适的平台无关的类名来定义。

这边有个例子:让我们看看是如何实现CoreGraphics名字空间中的RenderDevice类的.首先在基类中定义类的接口:

namespace CoreGraphics
{
class RenderDeviceBase : public Core::RefCounted
{
DeclareClass(RenderDeviceBase);
DeclareSingleton(RenderDeviceBase);
public:
/// constructor
...
};

} // namespace CoreGraphics

注意这个类名是RenderDeviceBase,不是RenderDevice。

一个叫做D3D9RenderDevcie平台相关的子类继承了RenderDeviceBase这是RenderDevice类的
Direct3D9的一个实现。


namespace CoreGraphics
{
class D3D9RenderDevice : public RenderDeviceBase
{
...
};

} // namespace CoreGraphics

最后,为RenderDevice类选择一个合适的头文件,根据条件把一个平台相关的类用一个合适的平台无关的类名来定义。

#if __USE_DIRECT3D9__
#include "coregraphics/d3d9/d3d9renderdevice.h"
namespace CoreGraphics
{
typedef D3D9RenderDevice RenderDevice;
}
#elif __USE_DIRECT3D10__
#include "coregraphics/d3d10/d3d10renderdevice.h"
namespace CoreGraphics
{
typedef D3D10RenderDevice RenderDevice;
}
#elif __USE_OPENGL__
#include "coregraphics/ogl/oglrenderdevice.h"
namespace CoreGraphics
{
typedef OGLRenderDevice RenderDevice;
}
#else
#error "RenderDevice class not implemented on this platform!"
#endif

客户代码和RenderDevice打交道,完全不知道实际是使用D3D9RenderDevice或D3D10RenderDevice类.平台独立将在编译的时候就解决掉,所有调用RenderDevice的方法都是一般调用,没有虚函数调用。这个可以让编译器做更多的优化工作。

原文: Getting rid of virtual method calls

[声明]:限于译者水平,文中难免错漏之处,欢迎各位网友批评指正;

没有评论:

发表评论