2013-11-14 2 views
1

Хорошо, я хочу создать класс Point3f, который представляет собой класс точек, состоящий из трехмерных плавающих координат.Какие атрибуты более эффективны для класса Point?

Итак, у меня есть два тривиальных способов определения атрибутов класса:

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

или

class Point3f 
{ 
    float coord[3]; 
} 

Я хочу знать, что больше (в целом) эффективным, особенно для графики программа. И.Е. что лучше для рисования точки: glVertex3f() или glVertex3v()?
Я не знаю, прав ли я. Я думаю, что сначала нужно больше памяти во время выполнения, а другое нужно больше использования процессора.

Редактировать: А как насчет того, если мы говорим о более сложных типах, таких как треугольник, состоит из трех точек или тетраэдра состоит из трех треугольников -> Атрибуты: массив или один за другим?

Пожалуйста, скажите, что является более эффективным и почему!

+1

Обычный подход для векторного класса заключается в использовании объединения между массивом реальных (плавающих) скаляров и анонимной структурой. Что-то вроде: 'class Point3f {union {struct {float x, y, z; }; float v [3]; }}; ' –

ответ

2

Вы можете сравнить сборку, сгенерированный для основных функций (я использовал 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}; 
} 

Compiling в 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 через геттеры. В этом смысле, я считаю, что я бы реализовать такой класс в манере, аналогичной:

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

Я думаю, что для начала требуется больше времени во время работы, а другая нужна больше Использование процессора.

Оба класса имеют точный одинаковый размер для хранения. Если ваш компилятор говорит, что float s - 4 байта, то у вас всего 12 байт для всего вашего класса. Второй пример объединяет их в единый блок памяти, где первый пример рассматривает их как отдельные 4 байтовых фрагмента.

Что касается эффективности, это все постоянный доступ времени. Это не имеет значения.

Я думаю, вы должны использовать первый. point.x является более наглядным, чем point.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 
Смежные вопросы