Как вы его написали, я ожидал бы, что 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
, чтобы получить разборку окончательного кода, чтобы узнать, что на самом деле происходит в вашем случае.
Вы должны прочитать http://stackoverflow.com/questions/4421706/operator-overloading, который показывает методы быстрее, чем любой из них. –