使用C ++在嵌入式环境中使用继承的“价格”是什么?

时间:2011-08-09 19:45:01

标签: c++ programming-languages embedded

我正在使用C ++开始一个新的嵌入式项目,我想知道使用面向接口的设计是否太昂贵了。像这样:

typedef int data;

class data_provider {

public:
    virtual data get_data() = 0;
};

class specific_data_provider : public data_provider {
public:
    data get_data() {
    return 7;
    }
};

class my_device {
public:
    data_provider * dp;
    data d;

    my_device (data_provider * adp) {
    dp = adp;
    d = 0;
    }

    void update() {
    d = dp->get_data();
    }
};

int
main() {
    specific_data_provider sdp;
    my_device dev(&sdp);

    dev.update();

    printf("d = %d\n", dev.d);

    return 0;
}

4 个答案:

答案 0 :(得分:4)

继承基本上是免费的。但是,多态和动态分派(virtual)会产生一些结果:带有虚方法的类的每个实例都包含一个指向vtable的指针,用于选择正确的调用方法。这为每个虚拟方法调用添加了两个内存访问。

在大多数情况下,它不会成为问题,但它可能成为某些实时应用程序的瓶颈。

答案 1 :(得分:4)

继承本身就是免费的。例如,从性能/内存的角度来看,BC是相同的:

struct A { int x; };
struct B : A { int y; };
struct C { int x, y; };

只有拥有虚函数时,继承只会产生成本。

struct A { virtual ~A(); };
struct B : A { ... };

在这里,几乎所有实现,由于虚函数,AB都将是一个更大的指针大小。

虚拟功能还有其他缺点(与非虚拟功能相比)

  1. 虚拟函数要求您在调用时查找vtable。如果该vtable不在缓存中,那么您将获得L2未命中,这在嵌入式平台上可能非常昂贵(例如,在当前的gen游戏控制台上超过600个周期)。
  2. 即使您点击L2缓存,如果您分支到许多不同的实现,那么您可能会在大多数调用中得到分支错误预测,从而导致管道刷新,这又需要花费很多周期。
  3. 由于虚拟函数基本上不可能内联(除极少数情况外),您也错过了许多优化机会。如果您调用的函数很小,那么与内联的非虚函数相比,这可能会增加严重的性能损失。
  4. 虚拟调用可能导致代码膨胀。每个虚函数调用都会添加几个字节的指令来查找vtable,并为vtable本身添加许多字节。
  5. 如果你使用多重继承,事情会变得更糟。

    通常人们会告诉你“在你的探查者告诉你之前不要担心性能”,但如果性能对你来说非常重要,这是一个糟糕的建议。如果你不担心性能,那么会发生什么,你到处都是虚拟功能,当你运行探查器时,没有一个热点需要优化 - 整个代码库需要优化。

    如果对你很重要,我的建议是设计性能。设计以尽可能避免需要虚拟功能。围绕缓存设计数据:偏好数组到基于节点的数据结构,如std::liststd::map。即使你有几千个元素的容器,频繁插入中间,我仍然会在某些架构上寻找阵列。您丢失复制数据插入的几千个周期可能会被您在每次遍历时实现的缓存局部性所抵消(还记得单个 L2缓存未命中的成本吗?您可以期待很多这些遍历链表时<)

答案 2 :(得分:1)

真的取决于你的硬件。继承本身可能不会花费任何成本。虚拟方法为每个类中的vTable花费了一些内存。打开异常处理可能会在内存和性能方面花费更多。我已经在NetBurner平台上广泛使用了C ++的所有功能,其中包括MOD5272等芯片,它具有几个Megs的闪存和8兆的RAM。还有一些东西可能依赖于实现,在我使用的GCC工具链上,当cout被使用而不是printf时,你需要大量内存命中(它似乎链接在一堆库中)。您可能对blog post I wrote on the cost of type safe code感兴趣。您必须在您的环境中运行类似的测试才能真正回答您的问题。

答案 3 :(得分:0)

通常的建议是使代码清晰正确,然后只有在实践中证明是一个问题(太慢或太多的内存)时才考虑优化。