gpt4 book ai didi

c++ - 非虚拟接口(interface)? (需要非常高效的低级抽象)

转载 作者:可可西里 更新时间:2023-11-01 18:37:09 27 4
gpt4 key购买 nike

我正在尝试在应用程序架构中非常低的水平点微优化我的代码。所以这是我的具体场景:

  • 我有一个解析器类,它解析图形文件(节点、边、邻接项等)
  • 文件格式是版本化的,因此每个版本都存在解析器,这些解析器作为单独的类(ParserV1、ParserV2、...)实现。
  • 解析器为应用程序中的某些上层提供相同的功能。因此,它们实现了相同的“接口(interface)”。
  • 在 C++ 中,我会将这样的接口(interface)实现为抽象类,所有函数都是纯虚拟
  • 由于虚拟函数需要另一个内存查找并且不能在编译时静态绑定(bind),而且——更重要的是——将不允许内联解析器类中的小方法,使用经典子类习语不会导致我能达到的最佳性能。

[在描述我可能的解决方案之前,我想解释一下为什么我要在这里进行微优化(你可以跳过这一段):解析器类有很多小方法,其中“小”意味着它们不不要做太多。它们中的大多数只从缓存的比特流中读取一个或两个字节,甚至只读取一个比特。所以应该有可能以一种非常非常有效的方式实现它们,其中一个函数调用,当内联时,只需要少量的机器命令。 这些方法在应用程序中被调用得非常频繁,因为它们在一个非常大的图形(世界范围的道路网络)中查找节点属性,每个用户请求可能发生大约一百万次,并且这样的请求应该尽可能快可能。]

去这里的路是什么?我可以看到以下解决问题的方法:

  1. 使用纯虚方法编写一个接口(interface)并将其子类化。性能会受到影响。
  2. 不要写这样的接口(interface)。每个解析器都自己定义相同的方法。在上层(使用解析器)有指向每个版本子类的指针(作为成员)。首先,实例化应该使用的特定解析器。每当访问函数时,使用 switch block 并将解析器实例强制转换为显式子类。性能会不会更好? (if/switch block 与虚拟表查找)。
  3. 混合使用两种解决方案 1. + 2.:为很少使用的方法编写一个带有纯虚拟方法的接口(interface),其中性能不是非常关键。如果很关键,不要提供虚拟方法,而是使用第二种方法。
  4. 改进2.:在抽象类中提供非虚方法;在抽象类中保留一个版本号作为成员变量(一种自己的运行时类型信息)并在这些方法中实现 if/switch block 和强制转换;然后调用子类中的方法。这提供了内联和静态绑定(bind)。

有没有更好的方法来解决这个问题?有什么成语吗?

澄清一下,我有很多函数是独立于版本的(至少到现在为止),因此非常适合某些父类(super class)。我将对大多数功能使用标准的子类设计,而这个问题只涵盖要优化的版本相关功能的解决方案。 (其中一些不会被频繁调用,在这些情况下我当然可以使用虚拟方法。)除此之外,我不喜欢让解析器类决定哪些方法需要执行,哪些不需要的想法. (尽管这样做是可能的。)

最佳答案

一个可能运作良好的选项如下:让每个解析器类定义具有相同签名的方法,但完全独立于其他类。然后,引入一个二级类层次结构来虚拟实现所有这些相同的功能,然后将每个方法调用转发给一个具体的解析器对象。这样,解析器的实现就获得了内联的所有好处,因为从类的角度来看,所有调用都可以静态解析,而客户端则获得了多态性的好处,因为任何方法调用都将动态解析为正确的类型。

这样做的问题是你使用了额外的内存(包装对象占用空间),而且当你调用解析器函数时你也可能至少涉及一个额外的间接寻址,因为调用会发生

client → wrapper → implementation

根据您从客户端调用方法的频率,此实现可能工作得很好。

使用模板,可以非常简洁地实现包装层。这个想法如下。假设您有方法 fA、fB 和 fC。从像这样定义一个基类开始:

class WrapperBase {
public:
virtual ~WrapperBase() = 0;

virtual void fA() = 0;
virtual void fB() = 0;
virtual void fC() = 0;
};

现在,将以下模板类型定义为子类:

template <typename Implementation>
class WrapperDerived: public WrapperBase {
private:
Implementation impl;

public:
virtual void fA() {
impl.fA();
}
virtual void fB() {
impl.fB();
}
virtual void fC() {
impl.fC();
}
};

现在,你可以这样做:

WrapperBase* wrapper = new WrapperDerived<MyFirstImplementation>();
wrapper->fA();
delete wrapper;

wrapper = new WrapperDerived<MySecondImplementation>();
wrapper->fB();
delete wrapper;

换句话说,编译器只需实例化 WrapperDerived 模板即可为您生成所有包装器代码。

希望这对您有所帮助!

关于c++ - 非虚拟接口(interface)? (需要非常高效的低级抽象),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/11302068/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com