我实现了static_for
类以利用编译器效率,但效率方面的结果与我的预期完全不同。
课程如下:
namespace std{
template <int First, int Last>
class static_for
{
public:
template <typename Lambda>
static inline constexpr void apply(Lambda const& f)
{
if (First < Last)
{
f(std::integral_constant<int, First>{});
static_for<First + 1, Last>::apply(f);
}
}
};
template <int N>
class static_for<N, N>
{
public:
template <typename Lambda>
static inline constexpr void apply(Lambda const& f) { f(std::integral_constant<int, N>{}); }
};
}
为了测试这实际上是否更有效,我计算了随机生成的双精度矢量的avg和sd(实际上是方差,但这不是帖子的重点):
std::vector<double> t;
std::vector<double> t1;
std::vector<double> t2;
std::srand((unsigned)time(NULL));
for (int i = 0; i < 10000000; ++i) {
int b = std::rand() % 20 + 1;
t.push_back(b);
}
//Static-for
for (int i = 0; i < 9000000; ++i)
{
double N = 0;
double avg = 0;
double sd = 0;
std::static_for<0, 1000>::apply([&](auto j)
{
avg += t[i + j.value];
sd += t[i + j.value] * t[i + j.value];
++N;
});
avg /= N;
sd /= N;
sd -= avg * avg;
t1.push_back(sd);
}
//Dynamic-for
for (int i = 0; i < 9000000; ++i)
{
double N = 0;
double avg = 0;
double sd = 0;
for (int j = 0; j <= 1000; ++j)
{
avg += t[i + j];
sd += t[i + j] * t[i + j];
++N;
}
avg /= N;
sd /= N;
sd -= avg * avg;
t2.push_back(sd);
}
注意:如果创建模板深度问题,可以将1000更改为较低的数字,因为这不是重点。
我希望第一件比第二件更快地执行,但事实并非如此。我认为编译器强制动态调用每个单独的static_for<X,Y>::apply
,而不是将代码内联。
我正在使用Visual C ++ 2017.所以
添加ASM代码:
所以看一下ASM(~147k行),我发现了以下内容:
第19行 jmp ?? $ apply @ V @@@?$ static_for @ $ 00 $ 0DOI @@ std @@ SAXAEBV @@@ Z;的std :: static_for&LT; 1,1000&GT; ::应用&LT; &GT; ?? $ apply @ V @@@?$ static_for @ $ 0A @ $ 0DOI @@ std @@ SAXAEBV @@@ Z ENDP;的std :: static_for&LT; 0,1000&GT; ::应用&LT; &GT; _TEXT ENDS
下次我看到它时看起来像这样:
第19行 mov rcx,r11 jmp ?? $ apply @ V @@@?$ static_for @ $ 0L @ $ 0DOI @@ std @@ SAXAEBV @@@ Z;的std :: static_for&LT; 11,1000&GT; ::应用&LT; &GT; ?? $ apply @ V @@@?$ static_for @ $ 00 $ 0DOI @@ std @@ SAXAEBV @@@ Z ENDP;的std :: static_for&LT; 1,1000&GT; ::应用&LT; &GT; _TEXT ENDS
请注意,它会移至static_for<11,1000>
,然后展开1到10。
然后21,从11到20开始。依此类推,直到结束。
此外,在开始时有一个寂寞的static_for<0,1000>
。
不确定这是否足以看到发生了什么。让我知道你还需要什么。
答案 0 :(得分:2)
我会试试这个:
template <typename Lambda>
static inline constexpr void apply(Lambda&& f)
我发现通过引用传递这种元编程有时会导致编译器做错事。
为了更好的衡量,您还可以FORCE_INLINE
:
#if defined(_MSC_VER)
#define FORCE_INLINE __forceinline
#else
#define FORCE_INLINE __attribute__((always_inline))
#endif
答案 1 :(得分:1)
首先:您可以使用Google Benchmark。我下载并编译成lib(MSVC),现在我可以将它添加到我的benchamrking项目中。它可以在线获得(quick-bench.com),但它不允许运行很长的基准测试。
要比较汇编代码,编译器资源管理器是要使用的工具。 (godbolt.org)