Массивы фиксированного размера в C ++ против нескольких объектов одного типа - PullRequest
5 голосов
/ 30 января 2011

Мне было интересно, будет ли (кроме очевидных различий в синтаксисе) какая-либо разница в эффективности между наличием класса, содержащего несколько экземпляров объекта (одного и того же типа) или массивом объектов этого типа с фиксированным размером.

В коде:

struct A {
  double x;
  double y;
  double z;
};

struct B {
  double xvec[3];
};

В действительности я бы использовал массивы boost ::, которые являются лучшей альтернативой C ++ массивам в стиле C.

В основном я занимаюсь созданием / уничтожением и чтением / записью таких двойников, потому что эти классы часто создаются только для того, чтобы один раз вызвать одну из их функций-членов.

Спасибо за вашу помощь / предложения.

Ответы [ 6 ]

8 голосов
/ 30 января 2011

Как правило, представление этих двух структур будет точно таким же. Однако возможно снижение производительности, если вы выберете неправильный для своего варианта использования .

Например, если вам нужен доступ к каждому элементу в цикле, вы можете использовать массив:

for (int i = 0; i < 3; i++)
    dosomething(xvec[i]);

Однако без массива вам потребуется дублировать код:

dosomething(x);
dosomething(y);
dosomething(z);

Это означает дублирование кода, которое может быть любым. С одной стороны, там меньше кода цикла; с другой стороны, на современных процессорах очень короткие циклы могут быть довольно быстрыми, а дублирование кода может разрушить I-кеш.

Другой вариант - это переключатель:

for (int i = 0; i < 3; i++) {
    int *r;
    switch(i) {
        case 0: r = &x; break;
        case 1: r = &y; break;
        case 1: r = &z; break;
    }
    dosomething(*r); // assume this is some big inlined code
}

Это позволяет избежать, возможно, большого использования i-cache, но оказывает огромное негативное влияние на производительность. Не делай этого.

С другой стороны, в принципе, доступ к массиву может быть медленнее, если ваш компилятор не очень умен:

xvec[0] = xvec[1] + 1;
dosomething(xvec[1]);

Поскольку xvec [0] и xvec [1] различаются, в принципе, компилятор должен иметь возможность сохранять значение xvec [1] в регистре, поэтому он не должен перезагружать значение в следующая строка Однако возможно, что некоторые компиляторы не будут достаточно умны, чтобы заметить, что xvec [0] и xvec [1] не имеют псевдонимов. В этом случае использование отдельных полей может быть немного быстрее.

Короче говоря, речь идет не о том, что один или другой быстры во всех случаях. Речь идет о сопоставлении представления с тем, как вы его используете.

Лично я бы предложил использовать то, что делает код, работающий на xvec, наиболее естественным. Не стоит тратить много человеческого времени на то, чтобы беспокоиться о том, что в лучшем случае, вероятно, приведет только к такой небольшой разнице в производительности, что вы поймете это только в микропробежках.

5 голосов
/ 30 января 2011

MVC ++ 2010 сгенерировал точно такой же код для чтения / записи из двух структур POD, как в вашем примере.Поскольку смещения для чтения / записи вычисляются во время компиляции, это не удивительно.То же самое касается строительства и разрушения.

Что касается фактической производительности, применяется общее правило: профилируйте его, если оно имеет значение, если нет - зачем это нужно?

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

2 голосов
/ 30 января 2011

Если вы не можете принять решение и хотите оставить свои настройки открытыми, вы можете использовать анонимный союз:

struct Foo
{
    union
    {
        struct
        {
            double x;
            double y;
            double z;
        } xyz;
        double arr[3];
    };
};

int main()
{
    Foo a;
    a.xyz.x = 42;
    std::cout << a.arr[0] << std::endl;
}

Некоторые компиляторы также поддерживают анонимные структуры, в этом случае вы можете оставить часть xyz.

1 голос
/ 30 января 2011

Разница может заключаться в хранении переменных в памяти.В первом примере компилятор может добавить отступы для выравнивания данных.Но в вашем случае это не имеет значения.

1 голос
/ 30 января 2011

Это зависит.Например, приведенный вами пример является классическим в пользу массивов старой школы: математическая точка / вектор (или матрица)

  • имеет фиксированное количество элементов
  • сами данные обычно хранятся в частном порядке в объекте
  • , поскольку (если?) он имеет класс в качестве интерфейса, вы можете правильно инициализировать их в конструкторе (в противном случае классическая инициализация массива - это то, что я не делаюочень похоже на синтаксис)

В таких случаях (в соответствии с примерами математического вектора / матрицы) я всегда заканчивал тем, что использовал массивы в стиле C внутренне, так как вы можете зацикливать их вместо записископируйте / вставьте код для каждого компонента.

Но это особый случай - для меня, в современных C ++ массивах == вектор STL, это быстро, и мне не нужно беспокоиться о nuthin ':)

0 голосов
/ 30 января 2011

Необработанные массивы предлагают лучшую локальность кэша, чем массивы c ++, поскольку, как показано, единственным преимуществом примера массива перед несколькими объектами является возможность итерации по элементам.

Реальный ответ, конечно, создать тестовый пример и измерить.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...