Template performance
bearophile
bearophileHUGS at lycos.com
Mon Nov 22 15:23:37 PST 2010
Time ago I have found a little C++ program that computes the number of solutions to the N Queens problem at compile-time using just templates. I have translated it into CTFE code and I have shown in this newsgroup that if your purpose is to compute a result at compile-time, then D CTFE allows you to write much simpler (and faster) code compared to the C++ template mataprogramming.
This time I have translated that C++ code to D code that uses just templates. This is not idiomatic D code, because for this purpose CTFE is better, but templates are used in D too, so it may be a performance benchmark for templates in general.
I am not very good with C++ templates yet, so if you spot an error in my D translation please tell me that I will redo the timings.
Compilation time: G++ 0.96 seconds, dmd 12.4 seconds.
With N=7 G++ uses about 34 MB RAM, DMD about 130+ MB RAM.
I have used MinGW 4.5.1 and DMD 2.050.
Compilation:
g++ nqueens_cpp.cpp -o nqueens_cpp
dmd nqueens_d.d
The D code is nicer and shorter.
GCC 4.5 has optimized the template compilation, and from the benchmark it seems faster:
http://cpptruths.blogspot.com/2010/03/faster-meta-programs-using-gcc-45-and.html
Is a similar optimization (using a hash) already present in DMD and is it useful?
-----------------------------
// G++ 4.5.1 code
#include "stdio.h"
typedef unsigned long long uint64_t;
template <uint64_t Cols = 0, uint64_t Diag135 = 0, uint64_t Diag45 = 0, uint64_t Solution = 0>
struct state {
static uint64_t const cols = Cols;
static uint64_t const diag135 = Diag135;
static uint64_t const diag45 = Diag45;
static uint64_t const solution = Solution;
};
template <int K, int J, typename State>
struct test {
static bool const result = ((State::cols & (1ull << J)) +
(State::diag135 & (1ull << (J + K))) +
(State::diag45 & (1ull << (32 + J - K)))) == 0;
};
template <int K, int J, typename State>
struct mark {
typedef state <
State::cols ^ (1ull << J),
State::diag135 ^ (1ull << (J + K)),
State::diag45 ^ (1ull << (32 + J - K)),
State::solution
> state_type;
};
template <int StartValue, int Times, typename State>
struct accumulate_result {
typedef typename
accumulate_result <
StartValue + 1,
Times - 1,
state <
State::cols,
State::diag135,
State::diag45,
State::solution + test<0, StartValue, State>::result
>
>::state_type state_type;
};
template <int StartValue, typename State>
struct accumulate_result<StartValue, 0, State> {
typedef State state_type;
};
template <int Niv, int Dx, typename State>
struct solve;
template <bool Condition, typename State, int Current, int Niv, int Dx>
struct result_from_test {
typedef typename
mark <
Niv,
Current,
typename solve <
Niv - 1,
Dx,
typename mark<Niv, Current, State>::state_type
>::state_type
>::state_type state_type;
};
template <typename State, int Current, int Niv, int Dx>
struct result_from_test<false, State, Current, Niv, Dx> {
typedef State state_type;
};
template <int Begin, int Times, typename State, int Niv, int Dx>
struct process_queens {
typedef typename
process_queens <
Begin + 1,
Times - 1,
typename result_from_test <
test<Niv, Begin, State>::result,
State,
Begin,
Niv,
Dx
>::state_type,
Niv,
Dx
>::state_type state_type;
};
template <int Begin, typename State, int Niv, int Dx>
struct process_queens<Begin, 0, State, Niv, Dx> {
typedef State state_type;
};
template <int Niv, int Dx, typename State = state<> >
struct solve {
typedef typename
process_queens<0, Dx, State, Niv, Dx>::state_type state_type;
static uint64_t const result = state_type::solution;
};
template <int Dx, typename State>
struct solve<0, Dx, State> {
typedef typename
accumulate_result<0, Dx, State>::state_type state_type;
static uint64_t const result = state_type::solution;
};
template <int Dx>
struct meta_queens:
solve<Dx - 1, Dx> {};
int main() {
printf("%llu", meta_queens<7>::result);
}
--------------------------------
// D2 code
import core.stdc.stdio: printf;
template State(ulong Cols=0, ulong Diag135=0, ulong Diag45=0, ulong Solution=0) {
enum ulong cols = Cols;
enum ulong diag135 = Diag135;
enum ulong diag45 = Diag45;
enum ulong solution = Solution;
}
template Test(int k, int j, alias state) {
enum bool Test = ((state.cols & (1UL << j)) +
(state.diag135 & (1UL << (j + k))) +
(state.diag45 & (1UL << (32 + j - k)))) == 0;
}
template Mark(int k, int j, alias state) {
alias State!(state.cols ^ (1UL << j),
state.diag135 ^ (1UL << (j + k)),
state.diag45 ^ (1UL << (32 + j - k)),
state.solution
) Mark;
}
template AccumulateResult(int startValue, int times, alias state) {
static if (times == 0)
alias state AccumulateResult;
else
alias AccumulateResult!(startValue + 1,
times - 1,
State!(state.cols,
state.diag135,
state.diag45,
state.solution + Test!(0, startValue, state))
) AccumulateResult;
}
template ResultFromTest(bool condition, alias state, int current, int niv, int dx) {
static if (condition == 0)
alias state ResultFromTest;
else
alias Mark!(niv,
current,
Solve!(niv - 1,
dx,
Mark!(niv, current, state))
) ResultFromTest;
}
template ProcessQueens(int begin, int times, alias state, int niv, int dx) {
static if (times == 0)
alias state ProcessQueens;
else
alias ProcessQueens!(begin + 1,
times - 1,
ResultFromTest!(Test!(niv, begin, state), state, begin, niv, dx),
niv,
dx
) ProcessQueens;
}
template Solve(int niv, int dx, alias state=State!()) {
static if (niv == 0)
alias AccumulateResult!(0, dx, state) Solve;
else
alias ProcessQueens!(0, dx, state, niv, dx) Solve;
}
template MetaNQueens(int dx) {
enum ulong MetaNQueens = Solve!(dx - 1, dx).solution;
}
void main() {
printf("%llu", MetaNQueens!(7));
}
-----------------------------
Bye,
bearophile
More information about the Digitalmars-d
mailing list