2012-05-20 3 views
3

C++ Если у меня есть два структур:операторов-членов Инлайн против встроенных операторов

struct A 
{ 
    float x, y; 
    inline A operator*(A b) 
    { 
     A out; 
     out.x = x * b.x; 
     out.y = y * b.y; 
     return out; 
    } 
} 

И эквивалентная-структура

struct B 
{ 
    float x, y; 
} 

inline B operator*(B a, B b) 
{ 
    B out; 
    out.x = a.x * b.x; 
    out.y = a.y * b.y; 
    return out; 
} 

вы знаете какую-либо причина для оператора Б * компилировать любого по-другому, или запускать все медленнее или быстрее, чем оператор A * (фактические действия, выполняемые внутри функций, должны быть неактуальными)?

Что я имею в виду ... объявлял бы оператор inline как член, а не как член, каким-либо общим эффектом на скорость фактической функции?

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

+0

Вы должны прочитать http://stackoverflow.com/questions/4421706/operator-overloading, который показывает методы быстрее, чем любой из них. –

ответ

10

Как вы его написали, я ожидал бы, что B::operator* будет работать немного медленнее. Это происходит потому, что реализация «под капотом» из A::operator*, как:

inline A A::operator*(A* this, A b) 
{ 
    A out; 
    out.x = this->x * b.x; 
    out.y = this->y * b.y; 
    return out; 
} 

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

Ваш код будет гораздо лучше, и, вероятно, будет выполнять то же самое для A и B, если вы написали его, используя ссылки и сделал это const правильно:

struct A 
{ 
    float x, y; 
    inline A operator*(const A& b) const 
    { 
     A out; 
     out.x = x * b.x; 
     out.y = y * b.y; 
     return out; 
    } 
} 

struct B 
{ 
    float x, y; 
} 

inline B operator*(const B& a, const B& b) 
{ 
    B out; 
    out.x = a.x * b.x; 
    out.y = a.y * b.y; 
    return out; 
} 

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


Добавление

Однако с сопзЬ пройти по ссылке для обоих аргументов, в B, он будет делать это эффективно быстрее, чем из-за разыменования?

Во-первых, оба связаны с тем же разыменованием, когда вы изложите весь код. (Помните, что доступ к элементам this подразумевает разыменование указателя.)

Но даже тогда это зависит от того, насколько умен ваш компилятор. В этом случае, скажем, он смотрит на вашу структуру и решает, что он не может заполнить его в регистре, потому что это два поплавка, поэтому он будет использовать указатели для доступа к ним. Таким образом, разыменованный случай указателя (который является тем, что ссылки реализуются как) является лучшим, что вы получите. Сборка будет выглядеть примерно так (это псевдо-сборочно-код):

// Setup for the function. Usually already done by the inlining. 
r1 <- this 
r2 <- &result 
r3 <- &b 

// Actual function. 
r4 <- r1[0] 
r4 <- r4 * r3[0] 
r2[0] <- r4 
r4 <- r1[4] 
r4 <- r4 * r3[4] 
r2[4] <- r4 

Это предполагает, что на RISC-архитектуру, как (скажем, ARM). x86, вероятно, использует меньше шагов, но в любом случае он расширяется до этого уровня детализации с помощью декодера команд. Дело в том, что это все фиксированные смещения разметки указателей в регистрах, что примерно так же быстро, как и получится.Оптимизатор может попытаться быть более умным и реализовать объекты в нескольких регистрах, но такого оптимизатора намного сложнее написать. (Хотя у меня есть подозрение, что LLVM типа компилятор/оптимизатор может сделать эту оптимизацию легко, если result были всего лишь временный объект, который не сохранился.)

Итак, так как вы используете this, у вас есть неявное разыменование указателя. Но что, если объект был в стеке? Не помогает; стековые переменные превращаются в фиксированные смещения в виде разметки указателя стека (или указателя фрейма, если они используются). Итак, вы разыскиваете указатель где-то в конце, если только ваш компилятор достаточно яркий, чтобы взять ваш объект и распространить его на несколько регистров.

Не стесняйтесь передать -S вариант gcc, чтобы получить разборку окончательного кода, чтобы узнать, что на самом деле происходит в вашем случае.

+0

Спасибо, Майк ... Я всегда забываю об использовании const или pass-by-reference. Моя ошибка ... Однако, если аргумент const по ссылке для обоих аргументов, в B, будет ли он работать быстрее, чем A, из-за разыменования? – Serge

+1

@Stefan: Нет, потому что оба связаны с разыменованием (один разучивает указатель 'this', другой - явный ссылочный аргумент, но стоимость такая же). –

+0

Если компилятор может доказать, что аргумент pass-by-reference/argument является аргументом только для загрузки, он может оптимизировать указатель/ссылку на значение pass-by-value. – dirkgently

3

Вы действительно должны оставить inline -удаление компилятору.

Таким образом, функции, определенные в определении класса (как в случае с A), по умолчанию равны inline. Спецификатор inline для A::operator * бесполезен.

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

Прочитать C++ FAQ 9.

+0

Спасибо, дирк! Всегда опираясь на новый материал ... – Serge

+0

Также помните, что компилятор (безумно) может игнорировать 'inline', когда ему это нравится. Таким образом, вы также можете объявить функции 'inline' nonmember как' static', если он делает это с вами, поэтому вы не получаете ошибки с множественным определением символов во время ссылки. (Я говорю «безумно», потому что это создает ситуацию, в которой я попал, где '# define' может принудительно вставлять, в то время как функция' inline' не может, и оказалось, что код с использованием 'inline' был фактически больше и требовал большего количества стека. из встроенных ребята.) –

+0

Обратите внимание, что я сказал: «Объявлять' inline' ** nonmember ** функции как 'static'. Ключевое слово 'static', к сожалению, имеет совершенно разные значения в двух контекстах. Моя точка зрения заключалась исключительно в том, чтобы избежать ошибок с множественным определением символов, потому что компилятор решил не включать функцию ** nonmember **. Кроме того, я столкнулся с этой проблемой на практике, поэтому не все компиляторы соответствуют стандарту в том, как вы описываете. –

2

Вот как я бы написать-структуру:

struct A 
{ 
    float x, y; 
    A(float ax, float ay) : x(ax), y(ay) { } 
    A operator*(const A& b) const { return b(x * b.x, y * b.y); } 
} 

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

Некоторые примечания:

  1. Никогда не беспокоиться об использовании встроенного ключевого слова. Оптимизация компиляторов принимает собственные решения о том, что и что не встраивать.

  2. Использование инициализации конструкторов. Сделайте это, потому что они улучшают читаемость кода . Сон лучше, зная, что они могут принести небольшие преимущества .

  3. Проводить структурные ссылки по константам как можно чаще.

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

+1

Не могли бы вы объяснить, какие обстоятельства могут вызвать потенциальное (крошечное) увеличение скорости? Если нет, все в порядке. – Serge

+0

Некоторые компиляторы в некоторых условиях предпочитают передавать «этот» указатель вокруг по регистру, но это означало бы, что какой-то другой код сборки рядом с вызовом функции был немного медленнее. Итак: не беспокойтесь об этом. – cdiggins

+0

О, спасибо вам большое! – Serge

Смежные вопросы