which is better?

W

wogston

(A)

template <typename T>
metainline foo<T> lerp(const foo<T>& a, const foo<T>& b, const T& time)
{
foo<T> v;
repeat< 4,exec_mul<T> >::exec(v,a,b,time);
return v;
}

(B)

template <typename T>
inline foo<T> lerp(const foo<T>& a, const foo<T>& b, const T& time)
{
return foo<T>(
a[0] + (b[0] - a[0]) * time,
a[1] + (b[1] - a[1]) * time,
a[2] + (b[2] - a[2]) * time,
a[3] + (b[3] - a[3]) * time);
}

Which of the above: (A) or (B) is "better" from the viewpoint of the
language police? I am mainly interested in efficiency (assuming correctness
is met in both cases). I'm unrolling repeatitive tasks in (A), but I would
like to know if this is the famous NRVO I keep hearing about ; the return
value is named.. but is (B) theoretically more efficient, excluding possible
overhead from repeat<> template?

Here's the repeat and exec_mul in case they are relevant:

template <typename SCALAR>
struct exec_lerp
{
template <typename A, typename B, typename C>
static metainline void exec(int index, A& a, const B& b, const C& c,
const SCALAR& d)
{
a[index] = b[index] + (c[index] - b[index]) * d;
}
};

template <int SIZE, typename E>
struct repeat
{
enum { INDEX = SIZE - 1 };

template <typename A, typename B, typename C>
static metainline void exec(A& a, const B& b, const C& c)
{
repeat<INDEX,E>::exec(a,b,c);
E::exec(INDEX,a,b,c);
}

template <typename A, typename B>
static metainline void exec(A& a, const B& b)
{
repeat<INDEX,E>::exec(a,b);
E::exec(INDEX,a,b);
}

template <typename A>
static metainline void exec(A& a)
{
repeat<INDEX,E>::exec(a);
E::exec(INDEX,a);
}
};

In otherwords, is this case of Named Return Value Optimization? While I am
aware that C++ doesn't recognize such concept, but contemporary compilers do
and this isn't std.c++ but rather generic C++ group I thought to ask here to
learn more: I'm interested in performance (and correctness). The "foo" type
has const and non-const [] operator, through which it accesses the
components, the internal presentation is static array, in most cases like
this:

template <typename SCALAR, int SIZE>
class foo
{
protected:
SCALAR m_v[SIZE];
// ...
};

Ie. is it generally (with current compilers!) prefered to return-by-value
constructing the return value object in return statement, or give it a name
and return?

I posted on same topic few years back on my opinions on my current vector
library, where I named the members:

SCALAR x,y,z,w; // example

These are possible to initialize in constructor using initializers, array
isn't.. but array was recommended back then so I decided to give it a shot
in the current design, so from this point of view it doesn't make any
difference if I try to initialize the object in constructor or just unroll
and initialize components with a template.

Any thoughts? Criticism? Helpful suggestions?
 
A

Alf P. Steinbach

(A)

template <typename T>
metainline foo<T> lerp(const foo<T>& a, const foo<T>& b, const T& time)

C++ has no keyword 'metainline', and you don't define a 'metainline'
macro in the code presented here.

Right here you're already in off-topic non-C++ land.


{
foo<T> v;
repeat< 4,exec_mul<T> >::exec(v,a,b,time);
return v;
}

(B)

template <typename T>
inline foo<T> lerp(const foo<T>& a, const foo<T>& b, const T& time)
{
return foo<T>(
a[0] + (b[0] - a[0]) * time,
a[1] + (b[1] - a[1]) * time,
a[2] + (b[2] - a[2]) * time,
a[3] + (b[3] - a[3]) * time);
}

Which of the above: (A) or (B) is "better"

Asking for "better" is trolling.


from the viewpoint of the language police?

Using deragatory terms like "language police" is trolling.


I am mainly interested in efficiency (assuming correctness
is met in both cases). I'm unrolling repeatitive tasks in (A), but I would
like to know if this is the famous NRVO I keep hearing about ;

It is not.

the return value is named..

It is not.


but is (B) theoretically more efficient, excluding possible
overhead from repeat<> template?

That is a Quality Of Implementation issue (also known as trolling).

Assuming the compiler does not optimize, the version with a single
'return'-statement should generally be more efficient than the one
with additional statements.

There is no answer without such assumptions.


Here's the repeat and exec_mul in case they are relevant:

template <typename SCALAR>
struct exec_lerp
{
template <typename A, typename B, typename C>
static metainline void exec(int index, A& a, const B& b, const C& c,

As mentioned, C++ has no keyword 'metainline'.


const SCALAR& d)
{
a[index] = b[index] + (c[index] - b[index]) * d;
}
};

template <int SIZE, typename E>
struct repeat
{
enum { INDEX = SIZE - 1 };

template <typename A, typename B, typename C>
static metainline void exec(A& a, const B& b, const C& c)
{
repeat<INDEX,E>::exec(a,b,c);
E::exec(INDEX,a,b,c);
}

template <typename A, typename B>
static metainline void exec(A& a, const B& b)
{
repeat<INDEX,E>::exec(a,b);
E::exec(INDEX,a,b);
}

template <typename A>
static metainline void exec(A& a)
{
repeat<INDEX,E>::exec(a);
E::exec(INDEX,a);
}
};

In otherwords, is this case of Named Return Value Optimization?

No (NRVO is a language extension that you don't use here).

While I am
aware that C++ doesn't recognize such concept, but contemporary compilers do
and this isn't std.c++ but rather generic C++ group

That is incorrect.

Read Shiva's welcome-text.

Read the FAQ.


I thought to ask here to
learn more: I'm interested in performance (and correctness). The "foo" type
has const and non-const [] operator, through which it accesses the
components, the internal presentation is static array, in most cases like
this:

template <typename SCALAR, int SIZE>
class foo
{
protected:
SCALAR m_v[SIZE];
// ...
};

Why don't you use a std::vector<>?


Ie. is it generally (with current compilers!) prefered to return-by-value
constructing the return value object in return statement, or give it a name
and return?

Whatever gives clear, readable code.


I posted on same topic few years back on my opinions on my current vector
library, where I named the members:

SCALAR x,y,z,w; // example

These are possible to initialize in constructor using initializers, array
isn't.. but array was recommended back then so I decided to give it a shot
in the current design, so from this point of view it doesn't make any
difference if I try to initialize the object in constructor or just unroll
and initialize components with a template.

Any thoughts? Criticism? Helpful suggestions?

Try to describe what you're trying to achieve, not your technical solution.
 
W

wogston

C++ has no keyword 'metainline', and you don't define a 'metainline'
macro in the code presented here.

Right here you're already in off-topic non-C++ land.

My bad, it was copy-paste from "real code", not typed specificly to here.
It's a macro for __forceinline or inline, depending on what compiler you
happen to be on. OK, so __forceinline is off-topic here, assume it reads
"inline" from this point forward.
 
W

wogston

C++ has no keyword 'metainline', and you don't define a 'metainline'
Here are the full definitions. hope this helps (somehow I doubt it, I tried
to trim it down to minimum previous time). If there are further headers you
would like to see before staying on-topic let me know. ;-)



// begin "configure.hpp"

#ifndef PRMATH_CONFIGURE_HPP
#define PRMATH_CONFIGURE_HPP
namespace prmath
{
// ------------------------------------------------------------
// Microsoft Visual C++
// ------------------------------------------------------------
#if defined(__VISUALC__)
#pragma inline_depth(255)
#pragma inline_recursion(on)
#pragma auto_inline(on)
#ifndef metainline
#define metainline __forceinline
#endif
#ifndef PRMATH_EXPRESSION_ENABLE
#define PRMATH_EXPRESSION_ENABLE
#endif
#endif
// ------------------------------------------------------------
// Intel C++
// ------------------------------------------------------------
#if defined(__INTEL_COMPILER)
#ifndef metainline
#define metainline __forceinline
#endif
#endif
// ------------------------------------------------------------
// generic c++ compiler
// ------------------------------------------------------------
#ifndef metainline
#define metainline inline
#endif
#ifdef PRMATH_EXPRESSION_DISABLE
#ifdef PRMATH_EXPRESSION_ENABLE
#undef PRMATH_EXPRESSION_ENABLE
#endif
#endif
#ifdef PRMATH_TYPENAME_DISABLE
#ifdef PRMATH_TYPENAME_ENABLE
#undef PRMATH_TYPENAME_ENABLE
#endif
#else
#ifndef PRMATH_TYPENAME_ENABLE
#define PRMATH_TYPENAME_ENABLE
#endif
#endif
} // namespace prmath
#endif


// begin "evaluate.hpp"

#ifndef PRMATH_EVALUATE_HPP
#define PRMATH_EVALUATE_HPP
#include <cassert>
#include "configure.hpp"
namespace prmath
{
// ------------------------------------------------------------
// permute masks
// ------------------------------------------------------------
enum
{
xxx = 0, yxx = 1, zxx = 2,
xyx = 4, yyx = 5, zyx = 6,
xzx = 8, yzx = 9, zzx = 10,
xxy = 16, yxy = 17, zxy = 18,
xyy = 20, yyy = 21, zyy = 22,
xzy = 24, yzy = 25, zzy = 26,
xxz = 32, yxz = 33, zxz = 34,
xyz = 36, yyz = 37, zyz = 38,
xzz = 40, yzz = 41, zzz = 42
};
enum
{
xxxx,yxxx,zxxx,wxxx,xyxx,yyxx,zyxx,wyxx,xzxx,yzxx,zzxx,wzxx,xwxx,ywxx,zwxx,w
wxx,
xxyx,yxyx,zxyx,wxyx,xyyx,yyyx,zyyx,wyyx,xzyx,yzyx,zzyx,wzyx,xwyx,ywyx,zwyx,w
wyx,
xxzx,yxzx,zxzx,wxzx,xyzx,yyzx,zyzx,wyzx,xzzx,yzzx,zzzx,wzzx,xwzx,ywzx,zwzx,w
wzx,
xxwx,yxwx,zxwx,wxwx,xywx,yywx,zywx,wywx,xzwx,yzwx,zzwx,wzwx,xwwx,ywwx,zwwx,w
wwx,
xxxy,yxxy,zxxy,wxxy,xyxy,yyxy,zyxy,wyxy,xzxy,yzxy,zzxy,wzxy,xwxy,ywxy,zwxy,w
wxy,
xxyy,yxyy,zxyy,wxyy,xyyy,yyyy,zyyy,wyyy,xzyy,yzyy,zzyy,wzyy,xwyy,ywyy,zwyy,w
wyy,
xxzy,yxzy,zxzy,wxzy,xyzy,yyzy,zyzy,wyzy,xzzy,yzzy,zzzy,wzzy,xwzy,ywzy,zwzy,w
wzy,
xxwy,yxwy,zxwy,wxwy,xywy,yywy,zywy,wywy,xzwy,yzwy,zzwy,wzwy,xwwy,ywwy,zwwy,w
wwy,
xxxz,yxxz,zxxz,wxxz,xyxz,yyxz,zyxz,wyxz,xzxz,yzxz,zzxz,wzxz,xwxz,ywxz,zwxz,w
wxz,
xxyz,yxyz,zxyz,wxyz,xyyz,yyyz,zyyz,wyyz,xzyz,yzyz,zzyz,wzyz,xwyz,ywyz,zwyz,w
wyz,
xxzz,yxzz,zxzz,wxzz,xyzz,yyzz,zyzz,wyzz,xzzz,yzzz,zzzz,wzzz,xwzz,ywzz,zwzz,w
wzz,
xxwz,yxwz,zxwz,wxwz,xywz,yywz,zywz,wywz,xzwz,yzwz,zzwz,wzwz,xwwz,ywwz,zwwz,w
wwz,
xxxw,yxxw,zxxw,wxxw,xyxw,yyxw,zyxw,wyxw,xzxw,yzxw,zzxw,wzxw,xwxw,ywxw,zwxw,w
wxw,
xxyw,yxyw,zxyw,wxyw,xyyw,yyyw,zyyw,wyyw,xzyw,yzyw,zzyw,wzyw,xwyw,ywyw,zwyw,w
wyw,
xxzw,yxzw,zxzw,wxzw,xyzw,yyzw,zyzw,wyzw,xzzw,yzzw,zzzw,wzzw,xwzw,ywzw,zwzw,w
wzw,
xxww,yxww,zxww,wxww,xyww,yyww,zyww,wyww,xzww,yzww,zzww,wzww,xwww,ywww,zwww,w
www
};
// ------------------------------------------------------------
// execs
// ------------------------------------------------------------
template <typename SCALAR>
struct exec_copy
{
template <typename A, typename B>
static metainline void exec(int index, A& a, const B& b)
{
a[index] = b[index];
}
template <typename A>
static metainline void exec(int index, A& a, const SCALAR& b)
{
a[index] = b;
}
};
template <typename SCALAR>
struct exec_min
{
template <typename A, typename B, typename C>
static metainline void exec(int index, A& a, const B& b, const C& c)
{
a[index] = b[index] < c[index] ? b[index] : c[index];
}
template <typename A, typename B>
static metainline void exec(int index, A& a, const B& b)
{
a[index] = a[index] < b[index] ? a[index] : b[index];
}
};
template <typename SCALAR>
struct exec_max
{
template <typename A, typename B, typename C>
static metainline void exec(int index, A& a, const B& b, const C& c)
{
a[index] = b[index] > c[index] ? b[index] : c[index];
}
template <typename A, typename B>
static metainline void exec(int index, A& a, const B& b)
{
a[index] = a[index] > b[index] ? a[index] : b[index];
}
};
template <typename SCALAR>
struct exec_neg
{
template <typename A, typename B>
static metainline void exec(int index, A& a, const B& b)
{
a[index] = -b[index];
}
template <typename A>
static metainline void exec(int index, A& a)
{
a[index] = -a[index];
}
};
template <typename SCALAR>
struct exec_add
{
template <typename A, typename B, typename C>
static metainline void exec(int index, A& a, const B& b, const C& c)
{
a[index] = b[index] + c[index];
}
template <typename A, typename B>
static metainline void exec(int index, A& a, const B& b)
{
a[index] += b[index];
}
};
template <typename SCALAR>
struct exec_sub
{
template <typename A, typename B, typename C>
static metainline void exec(int index, A& a, const B& b, const C& c)
{
a[index] = b[index] - c[index];
}
template <typename A, typename B>
static metainline void exec(int index, A& a, const B& b)
{
a[index] -= b[index];
}
};
template <typename SCALAR>
struct exec_mul
{
template <typename A, typename B, typename C>
static metainline void exec(int index, A& a, const B& b, const C& c)
{
a[index] = b[index] * c[index];
}
template <typename A, typename B>
static metainline void exec(int index, A& a, const B& b)
{
a[index] *= b[index];
}
template <typename A, typename B>
static metainline void exec(int index, A& a, const B& b, const SCALAR& c)
{
a[index] = b[index] * c;
}
template <typename A>
static metainline void exec(int index, A& a, const SCALAR& b)
{
a[index] *= b;
}
};
template <typename SCALAR, int SIZE>
struct exec_cross
{
template <typename A, typename B, typename C>
static metainline void exec(int, A&, const B&, const C&)
{
}
};
template <typename SCALAR>
struct exec_cross<SCALAR,3>
{
template <typename A, typename B, typename C>
static metainline void exec(int index, A& a, const B& b, const C& c)
{
switch ( index )
{
case 0: a[0] = b[1] * c[2] - b[2] * c[1]; break;
case 1: a[1] = b[2] * c[0] - b[0] * c[2]; break;
case 2: a[2] = b[0] * c[1] - b[1] * c[0]; break;
}
}
};
template <typename SCALAR>
struct exec_lerp
{
template <typename A, typename B, typename C>
static metainline void exec(int index, A& a, const B& b, const C& c, const
SCALAR& d)
{
a[index] = b[index] + (c[index] - b[index]) * d;
}
};
template <typename SCALAR>
struct exec_permute
{
template <typename A, typename B>
static metainline void exec(int index, A& a, const B& b, const int& mask)
{
const int idx = (mask >> (index * 2)) & 0x03;
a[index] = b[idx];
}
};
// ------------------------------------------------------------
// repeat
// ------------------------------------------------------------
template <int SIZE, typename E>
struct repeat
{
enum { INDEX = SIZE - 1 };
template <typename A, typename B, typename C>
static metainline void exec(A& a, const B& b, const C& c)
{
repeat<INDEX,E>::exec(a,b,c);
E::exec(INDEX,a,b,c);
}
template <typename A, typename B>
static metainline void exec(A& a, const B& b)
{
repeat<INDEX,E>::exec(a,b);
E::exec(INDEX,a,b);
}
template <typename A>
static metainline void exec(A& a)
{
repeat<INDEX,E>::exec(a);
E::exec(INDEX,a);
}
};
template <typename E>
struct repeat<3,E>
{
template <typename A, typename B, typename C>
static metainline void exec(A& a, const B& b, const C& c)
{
E::exec(0,a,b,c);
E::exec(1,a,b,c);
E::exec(2,a,b,c);
}
template <typename A, typename B>
static metainline void exec(A& a, const B& b)
{
E::exec(0,a,b);
E::exec(1,a,b);
E::exec(2,a,b);
}
template <typename A>
static metainline void exec(A& a)
{
E::exec(0,a);
E::exec(1,a);
E::exec(2,a);
}
};
template <typename E>
struct repeat<2,E>
{
template <typename A, typename B, typename C>
static metainline void exec(A& a, const B& b, const C& c)
{
E::exec(0,a,b,c);
E::exec(1,a,b,c);
}
template <typename A, typename B>
static metainline void exec(A& a, const B& b)
{
E::exec(0,a,b);
E::exec(1,a,b);
}
template <typename A>
static metainline void exec(A& a)
{
E::exec(0,a);
E::exec(1,a);
}
};
template <typename E>
struct repeat<1,E>
{
template <typename A, typename B, typename C>
static metainline void exec(A& a, const B& b, const C& c)
{
E::exec(0,a,b,c);
}
template <typename A, typename B>
static metainline void exec(A& a, const B& b)
{
E::exec(0,a,b);
}
template <typename A>
static metainline void exec(A& a)
{
E::exec(0,a);
}
};
// ------------------------------------------------------------
// product
// ------------------------------------------------------------
template <typename SCALAR, int SIZE>
struct product
{
enum { INDEX = SIZE - 1 };
template <typename A, typename B>
static metainline SCALAR exec(const A& a, const B& b)
{
return product<SCALAR,INDEX>::exec(a,b) + a[INDEX] * b[INDEX];
}
};
template <typename SCALAR>
struct product<SCALAR,4>
{
template <typename A, typename B>
static metainline SCALAR exec(const A& a, const B& b)
{
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2] + a[3] * b[3];
}
};
template <typename SCALAR>
struct product<SCALAR,3>
{
template <typename A, typename B>
static metainline SCALAR exec(const A& a, const B& b)
{
return a[0] * b[0] + a[1] * b[1] + a[2] * b[2];
}
};
template <typename SCALAR>
struct product<SCALAR,2>
{
template <typename A, typename B>
static metainline SCALAR exec(const A& a, const B& b)
{
return a[0] * b[0] + a[1] * b[1];
}
};
template <typename SCALAR>
struct product<SCALAR,1>
{
template <typename A, typename B>
static metainline SCALAR exec(const A& a, const B& b)
{
return a[0] * b[0];
}
};
// ------------------------------------------------------------
// evaluators
// ------------------------------------------------------------
template <typename SCALAR>
struct eval_ref
{
template <typename A>
static metainline SCALAR evaluate(int index, const A& a, const int&)
{
return a[index];
}
};
template <typename SCALAR>
struct eval_neg
{
template <typename A>
static metainline SCALAR evaluate(int index, const A& a, const int&)
{
return -a[index];
}
};
template <typename SCALAR>
struct eval_add
{
template <typename A, typename B>
static metainline SCALAR evaluate(int index, const A& a, const B& b)
{
return a[index] + b[index];
}
};
template <typename SCALAR>
struct eval_sub
{
template <typename A, typename B>
static metainline SCALAR evaluate(int index, const A& a, const B& b)
{
return a[index] - b[index];
}
};
template <typename SCALAR>
struct eval_mul
{
template <typename A>
static metainline SCALAR evaluate(int index, const A& a, const SCALAR b)
{
return a[index] * b;
}
template <typename B>
static metainline SCALAR evaluate(int index, const SCALAR a, const B& b)
{
return a * b[index];
}
};
template <typename SCALAR>
struct eval_div
{
template <typename A>
static metainline SCALAR evaluate(int index, const A& a, const SCALAR b)
{
return a[index] / b;
}
};
template <typename SCALAR, int SIZE>
struct eval_cross
{
template <typename A, typename B>
static metainline SCALAR evaluate(int, const A&, const B&)
{
return 0;
}
};
template <typename SCALAR>
struct eval_cross<SCALAR,3>
{
template <typename A, typename B>
static metainline SCALAR evaluate(int index, const A& a, const B& b)
{
switch ( index )
{
case 0: return a[1] * b[2] - a[2] * b[1];
case 1: return a[2] * b[0] - a[0] * b[2];
case 2: return a[0] * b[1] - a[1] * b[0];
default: return 0;
}
}
};
template <typename SCALAR, int SIZE>
struct eval_permute
{
template <typename A>
static metainline SCALAR evaluate(int index, const A& a, const int& mask)
{
index = (mask >> (index * 2)) & 0x03;
assert( index < SIZE );
return a[index];
}
};
// ------------------------------------------------------------
// expression
// ------------------------------------------------------------
template <typename TYPE, typename SCALAR, int SIZE, typename EVALUATOR,
typename A, typename B>
class expr
{
private:
const A m_a;
const B m_b;
public:
metainline expr(const A& a, const B& b)
: m_a(a), m_b(b)
{
}
metainline SCALAR operator [] (int index) const
{
assert( index >= 0 && index < SIZE );
return EVALUATOR::evaluate(index,m_a,m_b);
}
metainline expr operator + () const
{
return *this;
}
metainline expr< TYPE,SCALAR,SIZE,eval_neg<SCALAR>,expr,int >
operator - () const
{
return expr< TYPE,SCALAR,SIZE,eval_neg<SCALAR>,expr,int >(*this,0);
}
};
} // namespace prmath
#endif
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,744
Messages
2,569,484
Members
44,904
Latest member
HealthyVisionsCBDPrice

Latest Threads

Top