2013-11-14 36 views
1

好的,我想創建類Point3f,這是一類包含三維浮點座標的點。對於Point類,哪些屬性更有效?

所以,我有兩個平凡的方法來定義類的屬性:

class Point3f 
{ 
    float x; 
    float y; 
    float z; 
}; 

class Point3f 
{ 
    float coord[3]; 
} 

我想知道這是更高(一般)有效,特別是對顯卡程序。 I.E.這對繪製點更好:glVertex3f()或glVertex3v()?
我不知道我是否正確。我認爲第一個需要更多的運行時內存,另一個需要更多的CPU使用。

編輯:如果我們談論像三角形這樣更復雜的類型包含3點或四面體包含3個三角形 - >屬性:數組還是一個接一個?

請告訴我哪個更有效率,爲什麼!

+1

矢量類的常用方法是使用實​​數(浮點)標量數組和一個匿名結構之間的聯合。就像:class Point3f {union {struct {float x,y,z; }; float v [3]; }};' –

回答

2

可以比較爲基本功能生成的彙編(I使用GCC 4.8.1在OS X 10.7.4)

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])會更實用一些,因爲我可以擁有兩全其美的優點:當需要時可以傳遞一個參數,並且可以使用高級別,慣用的xy,z通過獲得者。在這個意義上,我相信我會以類似的方式實現這種類:

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()); 
+0

非常好的答案。 謝謝你研究這個案例翻譯成彙編,我覺得它很有趣。 我認爲你的主張是合理的,也知道glVertexv *通常更有效,因爲我已經發布了下面的內容,而且比使用工會更容易。 – Akronix

0

我覺得首先需要更多的運行時內存,另一個需要更多的 cpu使用。

這兩個類都有確切的相同的存儲大小。如果你的編譯器說float s是4個字節,那麼你的全班共有12個字節。第二個示例將它們作爲單個內存塊打包,第一個示例將它們視爲單獨的4個字節的塊。

就效率而言,這些都是恆定時間訪問。不要緊。

我認爲你應該使用第一個。 point.xpoint.coord[0]更具描述性。

1

我自己找到了答案。從openGL的紅色書(It can be read here)萃取:

在一些機器上,glVertex *的(矢量形式)是更有效的, 因爲只有一個參數需要傳遞到圖形 子系統並且特殊硬件可能能夠在單個批次中發送整個系列的座標。如果你的機器是這樣的話,那麼 有利於排列數據,以便頂點座標 按順序排列在內存中。

但我仍然想知道哪個更好,例如,對於三角形類:一個點的數組或一個個點。同樣的問題可以擴展到更復雜的類型。

2

如何使用union?使用一個結構,現在你可以通過兩種方式訪問​​它們。性能方面,它不應該與一個體面的編譯器有任何區別。

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 
相關問題