问题
Consider a typical finite difference application:
// assuming T_size > 2
void process_T(double *T0, double *T, const int &T_size, bool periodic) {
for (int i = 0; i < T_size; ++i) {
double sum = 0;
double base = T0[i];
if (i > 0) sum += (T0[i-1]-base);
if (i < 0) sum += (T0[i+1]-base);
if (periodic) {
if (i == 0) sum += (T0[T_size-1]-base);
if (i == T_size-1) sum += (T0[0]-base);
} else {
if (i == 1 || i == T_size-1) sum += 0.5*(T0[i-1]-base);
if (i == 0 || i == T_size-2) sum += 0.5*(T0[i+1]-base);
}
T[i] = T0[i] + sum * 0.08; // where 0.08 is some magic number
}
}
The check for periodic
is loop-invariant, but since is only known at run-time, the conditional check cost is incurred everytime. I could create a specialized function which assumes one of the cases, but it would be cumbersome to maintain the common base, especially in case of three-dimensional problem where it would grow to 8 functions (periodicity: none, x, y, z, xy, xz, yz, xyz) to consider all combinations.
Is it possible to solve this problem via metaprogramming?
P/S: can the branch predictor optimize this accordingly?
回答1:
Templates may have non-type parameters:
template <bool periodic>
void process_T(double *T0, double *T, const int &T_size)
Of course this implies a cost of writing something like this at the call site:
bool periodicFunction = {whatever};
if (periodicFunction)
process_T<true>(...);
else
process_T<false>(...);
回答2:
Yes, you could have
enum Periodicity
{
PERIODICITY_NONE,
PERIODICITY_X,
PERIODICITY_Y
// etc
};
and then
template <Periodicity P>
void process_T(double* T0, double* T, const int& T_size)
{
if (P == PERIODICITY_NONE) // ... do something
if (P == PERIODICITY_X) // ... do something else
// Common code
}
Any decent optimising compiler would be able to perform the check at compile time, and would eliminate any dead code (g++ appears to do this even at -O0
).
来源:https://stackoverflow.com/questions/19653105/including-an-invariant-assumption-in-a-template-function