-1

我需要列出要渲染的对象......现在我开始怀疑,不管风格或个人喜好,只考虑性能,有什么替代方案更快?

多种类型/switch语句:

void Object::Render()
{
    switch(type)
    {
        case BUTTON:
            RenderButton(this);
            break;
        case NOT_BUTTON:
            RenderNotButton(this);
            break;
        case WIDGET_ABC:
            RenderWidgetAbc(this);
            break;
    }
}

或多态性:

virtual void Object::Render();

class Button : public Object
{
     void Render();
}

编辑 1:如果可能有帮助,我提到这将在 ARM v6 设备中运行

编辑 2:目前该项目上的对象为零(项目目标的一部分是以更具功能性的编程方式使用 C++ 进行实验),这就是考虑这一点的原因。

此外,这将是代码中最常被调用的部分,如果它被实现为 switch 语句,则不会调用其他方法作为这个,因此它确实对性能至关重要。

编辑3:这部分代码实际上是处理要转储到GPU的顶点和UV列表,GPU比CPU快,我想尽可能快地向它抛出顶点和UV。渲染不受 CPU 限制。它还将具有多边形对象,而不仅仅是简单的类似操作系统的 GUI。

4

5 回答 5

3

我运行以下代码来比较两者。正如我预期的那样,虚函数会稍微快一些(我将在下面解释原因)。

#include <stdio.h>

#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>

class Object
{
    public:
        enum Type
        {
            BUTTON,
            NOT_BUTTON,
            WIDGET_ABC
        };

        Object(Type type);

        virtual void renderVirtual() = 0;
        void renderSwitch();

        int counter;

    private:
        void renderButton();
        void renderNotButton();
        void renderWidgetAbc();

        Type type;
};

class Button : public Object
{
    public:
        Button();

        virtual void renderVirtual();
};

class NotButton : public Object
{
    public:
        NotButton();

        virtual void renderVirtual();
};

class WidgetAbc : public Object
{
    public:
        WidgetAbc();

        virtual void renderVirtual();
};

Object::Object(Type type)
    :type(type),
     counter(0)
{

}

void Object::renderSwitch()
{
    switch(type)
    {
        case BUTTON:
            renderButton();
            break;
        case NOT_BUTTON:
            renderNotButton();
            break;
        case WIDGET_ABC:
            renderWidgetAbc();
            break;
    }
}

void Object::renderButton()
{
    counter += 1;
}

void Object::renderNotButton()
{
    counter += 2;
}

void Object::renderWidgetAbc()
{
    counter += 3;
}

Button::Button()
    :Object(BUTTON)
{

}

void Button::renderVirtual()
{
    counter += 1;
}

NotButton::NotButton()
    :Object(NOT_BUTTON)
{

}

void NotButton::renderVirtual()
{
    counter += 2;
}

WidgetAbc::WidgetAbc()
    :Object(WIDGET_ABC)
{

}

void WidgetAbc::renderVirtual()
{
    counter += 3;
}

static struct timeval start, end;
static long mtime, seconds, useconds;

static void startTime()
{
    gettimeofday(&start, NULL);
}

static void printTimeDiff()
{
    gettimeofday(&end, NULL);
    seconds  = end.tv_sec  - start.tv_sec;
    useconds = end.tv_usec - start.tv_usec;
    mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;
    printf("Elapsed time: %ld milliseconds\n", mtime);
}

int main()
{
    const int size = 10000000;
    Object *button = new Button();
    Object *notButton = new NotButton();
    Object *widgetAbc = new WidgetAbc();

    startTime();

    for(int i = 0; i < size; i++)
    {
        button->renderVirtual();
        notButton->renderVirtual();
        widgetAbc->renderVirtual();
    }

    printf("Virtual Function:\n");
    printTimeDiff();
    printf("button counter = %d\n", button->counter);
    printf("notButton counter = %d\n", notButton->counter);
    printf("widgetAbc counter = %d\n", widgetAbc->counter);

    startTime();

    for(int i = 0; i < size; i++)
    {
        button->renderSwitch();
        notButton->renderSwitch();
        widgetAbc->renderSwitch();
    }

    printf("Switch Function:\n");
    printTimeDiff();
    printf("button counter = %d\n", button->counter);
    printf("notButton counter = %d\n", notButton->counter);
    printf("widgetAbc counter = %d\n", widgetAbc->counter);

    return 0;
}

当我使用“g++ main.cpp”构建时,我得到了以下结果

Virtual Function
Elapsed time 132 milliseconds
button counter = 10000000
notButton counter = 20000000
widgetAbc counter = 30000000
Switch Function
Elapsed time 206 milliseconds
button counter = 20000000
notButton counter = 40000000
widgetAbc counter = 60000000

然后我添加了-02(用于优化)并得到了以下结果

Virtual Function
Elapsed time 58 milliseconds
button counter = 10000000
notButton counter = 20000000
widgetAbc counter = 30000000
Switch Function
Elapsed time 76 milliseconds
button counter = 20000000
notButton counter = 40000000
widgetAbc counter = 60000000

在这两种情况下,虚函数都更快。

尽管虚函数比非虚函数慢,但开销很小。虚函数很可能是函数指针(尽管编译器可能会以不同的方式做)。因此,当您调用虚函数时,唯一的额外开销是指针取消引用。这是编译器可以为虚拟调用执行的操作的示例。编译器可以做得更优雅一点,但你可以明白。

#include <stdio.h>

class Object
{
    public:
        // function pointer acting as virtual function call
        void (*funcPtr) (void *this_ptr);
};

class Button : public Object
{
    public:
        Button();
        static void virtualFunc(void *this_ptr);

        int counter;
};

Button::Button()
    :counter(0)
{
    // set object function pointer to our "virtual function"
    funcPtr = &Button::virtualFunc;
}

void Button::virtualFunc(void *this_ptr)
{
    Button *button_ptr = reinterpret_cast<Button*>(this_ptr);
    button_ptr->counter++;
}

int main()
{
    Object *button = new Button();

    // virtual call using a function pointer
    button->funcPtr(button);

    printf("button counter = %d\n", static_cast<Button*>(button)->counter);

    return 0;
}
于 2013-03-21T15:39:03.477 回答
2

我认为性能差异将足够小,可以忽略不计。但是可维护性、可测试性和设计会受到很大影响,所以我不会使用第一个版本。

我的经验表明,每次我尝试使用如此小的优化来超越编译器时,我最终都会让主机变得更小。

于 2013-03-21T14:38:54.210 回答
2

您的第一个代码是维护和设计的噩梦。考虑一下需要添加新类型的情况!
至于表现。只有分析才能为您各自的平台和环境提供明确的答案。

于 2013-03-21T14:39:19.977 回答
0

带开关的版本应该更快,因为多态性需要将对象移动到缓存中,但这实际上是一个微优化。

我会选择多态,因为代码可以设计得更好。

于 2013-03-21T14:40:12.950 回答
0

你说的更快是什么意思?

使用多态性更快只是因为

  • 它更好地封装了行为
  • 可维护性肯定更好(所以它对你来说更快,这很重要)
  • 它允许以简单的方式进行更复杂的设计

仅从性能的角度来看,多态性的实现是通过查找表完成的,所需的是双重间接引用以获得动态调用方法的地址。这意味着多态性的时间是恒定的,即使它不是微不足道的,因为该方法无法加载到缓存中。

切换调用适当的方法只是忘记了 OOP。这样做是没有意义的。仅仅因为缓存命中/未命中,它可能会更快一些 CPU 周期,但几乎从不这样做是不值得的。

于 2013-03-21T14:40:18.697 回答