问题描述
我发现懒惰评估有许多用途,例如用于优化的工具(例如matrices)。
另一种用途是句法糖。但是在我付诸实践之前,我以牺牲运行时开销为代价来使代码看起来更加整洁的情况下,编译器是否知道如何优化这种东西?还是只在潜在开销比不使用惰性评估快时才使用它?
以下是我的意思的一个例子。这不是我的实际用例,只是非懒惰与懒惰评估的简单版本。
// original
template < class out,class in >
out && lazy_cast( in && i )
{
return ( out && )( i );
}
// usage
char c1 = 10;
int c2 = lazy_cast< int >( c1 );
// lazy
template < class in >
class C_lazy_cast
{
public:
in && i;
template < class out && >
operator out &&( )
{
return ( out && )( i );
}
};
template < class in >
C_lazy_cast< in > lazy_cast( in && i )
{
return { std::forward< in >( i ) };
}
// usage
char c1 = 10;
int c2 = lazy_cast( c1 );
为了完整起见,有关MSVC,GCC和clang的信息应该足够。
解决方法
编译器可以做的比您想像的要多得多。 我不确定您要在示例中使用什么,但是请考虑following piece of code:
template <typename TLazyChar>
struct lazyUpperImpl{
TLazyChar in;
lazyUpperImpl(TLazyChar in_):in(in_){}
char constexpr operator()(){
auto c = in();
if (c >= 'a' && c <= 'z'){
c = c - 'a' + 'A';
}
return c;
}
};
template <typename TLazyNumeric>
struct lazyAdd5Impl{
TLazyNumeric in;
lazyAdd5Impl(TLazyNumeric in_):in(in_){}
int constexpr operator()(){
return in() + 5;
}
};
template <typename Tout,typename TLazyIn>
struct lazyCastImpl {
TLazyIn in;
lazyCastImpl(TLazyIn in_):in(in_){}
Tout constexpr operator()(){
return static_cast<Tout>(in());
}
};
template <typename Tout,typename TLazyIn>
auto constexpr lazyCast(TLazyIn in){
return lazyCastImpl<Tout,TLazyIn>(in);
}
template <typename TLazyChar>
auto constexpr lazyUpper(TLazyChar in){
return lazyUpperImpl<TLazyChar>(in);
}
template <typename TLazyNumeric>
auto constexpr lazyAdd5(TLazyNumeric in){
return lazyAdd5Impl<TLazyNumeric>(in);
}
int foo(int in){
auto lazyInt = [in](){return in;};
auto x =
lazyAdd5(
lazyCast<int>(
lazyUpper(
lazyCast<char>(lazyInt)
)
)
) ();
return x;
}
int main(){
return foo(109);
}
通过gcc 10.2
,clang 10.0.1
和msvc 19.24
,foo
的代码成为一组简单的指令-有条件地减去26,并始终加5。
例如,msvc
生成的程序集为:
movzx eax,cl
cmp cl,97 ; 00000061H
jl SHORT $LN26@foo
cmp cl,122 ; 0000007aH
jg SHORT $LN26@foo
lea eax,DWORD PTR [rcx-32]
$LN26@foo:
movsx eax,al
add eax,5
ret 0
msvc
的输出可以说是这三个中最不详尽(因此最容易理解)的。
此外,如果您以编译时已知的输入值开头,则将其内联到单个return new_value
指令中。
请注意,在上面的示例中,编译器无法避免出现if
条件,而'lazyCast'只是一个空操作。
Here是另一个有趣的示例,其中两个if
语句和两个数学表达式被简单地抵消了。
对于真实程序,很难从汇编中预测实际效率,这是不可能的-它取决于执行程序的机器以及该机器所处的状态(例如,它是否同时运行不同的程序?)。即使那样,您仍需要运行测试以确保哪些行效果最好。
当然,最佳是主观的。您需要确定是否更喜欢优化运行时间(可能会进一步细分),可执行文件的大小或功耗要求?
这需要大量的学习才能正确,但是您可以放心,有一些非常有才华的人正在为解决这些问题而谋生,他们通常做得很好。