在嵌入式项目中工作时,我遇到了一个函数,该函数在应用程序的生命周期中被称为数千次,通常每秒循环数十次。我想知道是否可以降低其成本,但发现它的大多数参数在编译过程中都是已知的。
让我用一个例子来说明。
原始的hpp / cpp文件可以近似如下:
original.hpp:
void example(bool arg1, bool arg2, const char* data);
original.cpp:
#include "ex1.hpp"
#include <iostream>
void example(bool arg1, bool arg2, const char* data)
{
if (arg1 && arg2)
{
std::cout << "Both true " << data << std::endl;
}
else if (!arg1 && arg2)
{
std::cout << "False and true " << data << std::endl;
}
else if (arg1 && !arg2)
{
std::cout << "True and false " << data << std::endl;
}
else
{
std::cout << "Both false " << data << std::endl;
}
}
让我们假设,每次调用函数时,arg1
和arg2
在编译期间都是已知的。参数data
不是,并且由于各种原因,它的处理不能放在头文件中。
但是,所有这些if
语句都可以由编译器通过一点点模板魔术来处理:
magic.hpp:
template<bool arg1, bool arg2>
void example(const char* data);
magic.cpp:
#include "ex1.hpp"
#include <iostream>
template<bool arg1, bool arg2>
struct Processor;
template<>
struct Processor<true, true>
{
static void process(const char* data)
{
std::cout << "Both true " << data << std::endl;
}
};
template<>
struct Processor<false, true>
{
static void process(const char* data)
{
std::cout << "False and true " << data << std::endl;
}
};
template<>
struct Processor<true, false>
{
static void process(const char* data)
{
std::cout << "True and false " << data << std::endl;
}
};
template<>
struct Processor<false, false>
{
static void process(const char* data)
{
std::cout << "Both false " << data << std::endl;
}
};
template<bool arg1, bool arg2>
void example(const char* data)
{
Processor<arg1, arg2>::process(data);
}
template void example<true, true>(const char*);
template void example<false, true>(const char*);
template void example<true, false>(const char*);
template void example<false, false>(const char*);
如您所见,即使在这个很小的示例中,cpp文件也比原始文件大得多。但是我确实删除了一些汇编程序指令!
现在,在我的现实生活中,情况有些复杂,因为我有两个枚举和结构来代替两个bool
自变量。长话短说,所有组合给我约一千种组合,所以我有template void example<something>(const char*);
我当然不是手动生成它们,而是使用宏,但是与原始文件和目标文件相比,cpp文件仍然显得笨拙。
所有这些都是以删除多个if
和一个switch
语句的名义进行的。
我的问题是:尺寸是模板魔术方法唯一的问题吗?我想知道使用相同功能的这么多版本是否会产生一些隐性成本。我真的节省了一些资源,还是相反?
答案 0 :(得分:4)
二进制大小增加的问题几乎从来不是文件本身的存储-问题在于,更多的代码意味着在任何时候缓存中都可以使用较低百分比的程序指令,从而导致缓存未命中。如果要在一个紧密的循环中调用相同的实例化,那么减少它的工作量就很棒。但是,如果您不断在不同的模板实例化之间跳来跳去,那么进入主存储器加载指令的成本可能会比从函数内部删除一些指令所节省的成本高得多。
但是,这种事情很难预测。在这种(和任何一种)类型的优化中找到最佳结合点的方法是测量。它也可能会在各个平台上发生变化,尤其是在嵌入式世界中。