对于 Point 类,哪些属性更有效
what attributes are more efficient for a Point class?
好的,我想创建类 Point3f,它是一类由 3D 浮点坐标组成的点。
因此,我有两种简单的方法来定义类的属性:
class Point3f
{
float x;
float y;
float z;
};
或
class Point3f
{
float coord[3];
}
我想知道哪个(通常)更有效,特别是对于图形程序。 即哪个更适合绘制点:glVertex3f() 或 glVertex3v()?
我不知道我是否正确。我认为第一个需要更多的运行时内存,另一个需要更多的 CPU 使用率。
编辑:如果我们谈论更复杂的类型,例如三角形由 3 个点组成或四面体由 3 个三角形组成 ->属性:数组还是一个接一个?
请告诉我哪个更有效,为什么!
您可以比较为基本功能生成的程序集(我在OS X 10.7.4上使用GCC 4.8.1)
struct Point3D {
float m_data[3];
};
struct Point3Ds {
float x;
float y;
float z;
};
double dot(const Point3D& p1, const Point3D& p2) {
asm("# Dot - Point3D");
return p1.m_data[0] * p2.m_data[0] +
p1.m_data[1] * p2.m_data[1] +
p1.m_data[2] * p2.m_data[2];
}
double dot(const Point3Ds& p1, const Point3Ds&p2) {
asm("# Dot - Point3Ds");
return p1.x * p2.x +
p1.y * p2.y +
p1.z * p2.z;
}
Point3D cross(const Point3D& p1, const Point3D& p2) {
asm("# Cross - Point3D");
return { p1.m_data[1] * p2.m_data[2] - p1.m_data[2] * p2.m_data[1],
p1.m_data[2] * p2.m_data[0] - p1.m_data[0] * p2.m_data[2],
p1.m_data[0] * p2.m_data[1] - p1.m_data[1] * p2.m_data[0]};
}
Point3D cross(const Point3Ds& p1, const Point3Ds& p2) {
asm("# Cross - Point3Ds");
return { p1.y * p2.z - p1.z * p2.y,
p1.z * p2.x - p1.x * p2.z,
p1.x * p2.y - p1.y * p2.x};
}
编译g++ -O3 -S
我得到以下汇编程序(相关部分):
# 12 "point3f.cpp" 1
# Dot - Point3D
# 0 "" 2
movss (%rdi), %xmm0
movss 4(%rdi), %xmm1
mulss (%rsi), %xmm0
mulss 4(%rsi), %xmm1
addss %xmm1, %xmm0
movss 8(%rdi), %xmm1
mulss 8(%rsi), %xmm1
addss %xmm1, %xmm0
unpcklps %xmm0, %xmm0
cvtps2pd %xmm0, %xmm0
ret
LFE0:
.align 4,0x90
.globl __Z3dotRK8Point3DsS1_
__Z3dotRK8Point3DsS1_:
LFB1:
# 19 "point3f.cpp" 1
# Dot - Point3Ds
# 0 "" 2
movss (%rdi), %xmm0
movss 4(%rdi), %xmm1
mulss (%rsi), %xmm0
mulss 4(%rsi), %xmm1
addss %xmm1, %xmm0
movss 8(%rdi), %xmm1
mulss 8(%rsi), %xmm1
addss %xmm1, %xmm0
unpcklps %xmm0, %xmm0
cvtps2pd %xmm0, %xmm0
ret
LFE1:
.align 4,0x90
.globl __Z5crossRK7Point3DS1_
__Z5crossRK7Point3DS1_:
LFB2:
# 26 "point3f.cpp" 1
# Cross - Point3D
# 0 "" 2
movss 4(%rdi), %xmm3
movss 8(%rdi), %xmm1
movss 8(%rsi), %xmm5
movaps %xmm3, %xmm2
movss 4(%rsi), %xmm4
movaps %xmm1, %xmm0
mulss %xmm5, %xmm2
mulss %xmm4, %xmm0
subss %xmm0, %xmm2
movss (%rdi), %xmm0
mulss %xmm0, %xmm5
movss %xmm2, -24(%rsp)
movss (%rsi), %xmm2
mulss %xmm4, %xmm0
mulss %xmm2, %xmm1
mulss %xmm3, %xmm2
subss %xmm5, %xmm1
subss %xmm2, %xmm0
movss %xmm1, -20(%rsp)
movss %xmm0, -16(%rsp)
movq -24(%rsp), %xmm0
movd -16(%rsp), %xmm1
ret
LFE2:
.align 4,0x90
.globl __Z5crossRK8Point3DsS1_
__Z5crossRK8Point3DsS1_:
LFB3:
# 33 "point3f.cpp" 1
# Cross - Point3Ds
# 0 "" 2
movss 4(%rdi), %xmm3
movss 8(%rdi), %xmm1
movss 8(%rsi), %xmm5
movaps %xmm3, %xmm2
movss 4(%rsi), %xmm4
movaps %xmm1, %xmm0
mulss %xmm5, %xmm2
mulss %xmm4, %xmm0
subss %xmm0, %xmm2
movss (%rdi), %xmm0
mulss %xmm0, %xmm5
movss %xmm2, -24(%rsp)
movss (%rsi), %xmm2
mulss %xmm4, %xmm0
mulss %xmm2, %xmm1
mulss %xmm3, %xmm2
subss %xmm5, %xmm1
subss %xmm2, %xmm0
movss %xmm1, -20(%rsp)
movss %xmm0, -16(%rsp)
movq -24(%rsp), %xmm0
movd -16(%rsp), %xmm1
ret
所以程序集是相同的。但我同意存储为静态数组(即float m_data[3]
)会更实用,因为我可以两全其美:一个参数在需要时传递,以及一个高级的惯用x
,y
,z
通过getters。从这个意义上说,我相信我会以类似于以下方式实现这样的类:
class MyPoint3S {
public:
MyPoint3S(float x, float y, float z)
: m_data{x, y, z} { }
// the following getters will be inlined
float x() const {
return m_data[0];
}
float y() const {
return m_data[1];
}
float z() const {
return m_data[2];
}
// in case you want to use the pointer --- some would advice against
// offering a hook to a private member.
float* data() {
return m_data;
}
private:
float m_data[3];
};
并像这样使用它:
MyPoint3S p(1.0f, 2.0f, 3.0f);
std::cout<<"p = "<<p.x()<<", "<<p.y()<<", "<<p.z()<<std::endl;
获得:
p = 1, 2, 3
或者以您喜欢的任何方式调用 OpenGL 函数:
glVertex3fv(p.data());
或
glVertex3f(p.x(), p.y(), p.z());
使用联合怎么样?使用结构,现在您可以同时访问它们。性能方面,它应该与一个体面的编译器没有任何区别。
struct Point3F {
union {
float data[3];
struct {float x, float y, float z};
}
};
Point3F a;
a.x = 21;
a.data[1] == 42;
assert(a.data[0] == 21); // Same data
assert(a.y == 42); // Same data
我自己找到了答案。摘自openGL的红皮书(可以在这里阅读):
在某些机器上,glVertex*() 的向量形式更有效, 因为只需要将单个参数传递给图形 子系统,特殊硬件可能能够发送整个系列 单个批次中的坐标。如果你的机器是这样的,它是 对您的数据进行排列,以便顶点坐标 按顺序打包在内存中。
但我仍然想知道哪个更好,例如,对于三角形类:一个点数组或每个单独的点一个接一个。同样的问题可以扩展到更复杂的类型。
我认为第一个需要更多的运行时内存,另一个需要更多 CPU 使用率。
这两个类具有完全相同的存储大小。如果你的编译器说float
是 4 个字节,那么你的整个类总共有 12 个字节。第二个示例将它们打包为单个内存块,其中第一个示例将它们视为单独的 4 字节块。
就效率而言,这些都是恒定时间访问。没关系。
我认为你应该使用第一个。 point.x
比point.coord[0]
更具描述性。
- 如何导出包含具有"std::unique_ptr"值的"std::map"属性的
- 欧拉项目#8答案是大以获得有效答案
- 调整大小后指向元素值的指针unordered_map有效?
- 为什么是0;C++中的有效语句
- 最高有效数字侧的第N位
- C++概念:如何使用'concept'检查模板化结构的属性?
- 子目录是否继承属性,例如add_definitions,include_directories和父Cmakelist.t
- GCC对可能有效的代码抛出init list生存期警告
- 通过指向指针数组的指针访问子类的属性
- MSVC是否支持C++11样式的属性而不是__declspec
- 即使基类属性在完成向下转换时是虚拟的,是否有效
- 在BGL中,如何使用顶点的属性有效地找到相邻顶点
- 如何有效地从该对象中包含的另一个对象访问对象字段/属性
- 对于 Point 类,哪些属性更有效
- 这是对 noreturn 属性的有效使用吗?
- 当使用MSBuild目标文件时,查看vsc++项目中的有效属性
- 用不同的符号表重新计算Boost Spirit解析属性的最有效方法是什么?
- 惰性属性-可变的有效使用
- 在OpenCV 2.2中,设置视频捕获属性不再有效
- 如何在不提供有效生成器的情况下调用 boost::karma::rule 不消耗其属性