我需要制作一个要渲染的对象列表......现在我开始怀疑,无视风格或个人偏好,只考虑性能,有什么选择更快?
多种类型/开关声明:
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。
答案 0 :(得分: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;
}
答案 1 :(得分:2)
我认为性能差异足够小,可以忽略不计。但是可维护性,可测试性和设计会受到很大影响,因此我不会使用第一个版本。
我的经验表明,每当我尝试使用如此小的优化来超越编译器时,我最终会让主机实际上更小。
答案 2 :(得分:2)
您的第一个代码是维护和设计的噩梦。在需要添加新类型时考虑一下情况!
至于表现。只有分析才能为您的平台和环境提供明确的答案。
答案 3 :(得分:0)
带开关的版本应该更快,因为多态性要求将对象移动到缓存,但这实际上是一种微优化。
我会选择多态,因为代码可以更好地设计。
答案 4 :(得分:0)
你的意思更快?
使用多态更快只是因为
从性能的角度来看,多态性的实现是通过查找表完成的,所需要的是双重间接引用以获取动态调用方法的地址。 这意味着多态性的时间是常量,即使它不是微不足道的,因为该方法可能无法加载到缓存中。
切换到调用适当的方法只是忘记了OOP。这样做是没有意义的。一些CPU周期可能因为缓存命中/未命中而更快,但几乎从不这样做。