我正在开发一个为一个函数生成C代码的程序。这个生成的C函数驻留在另一个目标程序的中心循环中;此功能对性能敏感。生成的函数用于根据bool值调用另一个函数 - 使用传递给生成函数的2个int来获取此布尔值:状态编号和模式编号。生成的函数如下所示:
void dispatch(System* system, int state, int mode) {
// Some other code here...
if (truthTable[state][mode]) {
doExpensiveCall(system, state, mode);
}
}
一些事实:
我正在探索不同的方法来计算状态+模式是否会调用doExpensiveCall()。最后,我将不得不选择一些东西,所以我现在正在探索我的选择。到目前为止,这些是我能想到的不同方式:
1)创建一个预先计算的双维数组,其中包含布尔值。这就是我在上面的例子中使用的内容。这产生了我能想到的最快的检查。问题是如果状态和模式具有较大的范围(比如10,000x1000),则生成的表开始非常大(在10,000x1000的情况下,仅为该表的10MB)。例如:
// STATE_COUNT=4, MODE_COUNT=3
static const char truthTable[STATE_COUNT][MODE_COUNT] = {
{0,1,0},
{0,0,0},
{1,1,0},
{0,0,1}
}
2)创建一个像#1这样的表,但是压缩:而不是每个数组条目都是一个布尔值,它将是一个char位域。然后,在检查期间,我将使用state + mode进行一些计算,以决定如何索引数组。这通过MODE_MODE / 8减小了预计算表的大小。缺点是减少不是那么多,现在现在需要计算位域表中布尔值的索引,而不是像#1中那样只是一个简单的数组访问。
3)由于预期产生值为true的状态+模式组合的数量很小,因此也可以使用switch语句(使用#1中的truthTable作为参考):
switch(state){
case 0: // row
switch(mode){ // col
case 1: doExpensiveCall(system, state, mode);
break;
}
break;
case 2:
switch(mode){
case 0:
case 1: doExpensiveCall(system, state, mode);
break;
}
break;
case 3:
switch(mode){
case 2: doExpensiveCall(system, state, mode);
break;
}
break;
}
问题:
根据上述事实,有哪些其他方法可以用来计算调用doExpensiveCall()所需的布尔值?
由于
编辑: 我虽然关于Jens示例代码,但发生了以下情况。为了只有一个switch语句,我可以在生成的代码中进行这个计算:
// #if STATE_COUNT > MODE_COUNT
int i = s * STATE_COUNT + m;
// #else
int i = m * MODE_COUNT + s;
// #endif
switch(i) {
case 1: // use computed values here, too.
case 8:
case 9:
case 14:
doExpensiveCall(system, s, m);
}
答案 0 :(得分:0)
我尝试使用(3)的修改版本,其中您实际上只有一个呼叫,并且所有switch/case
内容都会导致该呼叫。通过这种方式,您可以确保编译器将选择他所拥有的任何启发式来优化它。
中的某些内容
switch(state) {
default: return;
case 0: // row
switch(mode){ // col
default: return;
case 1: break;
}
break;
case 2:
switch(mode){
default: return;
case 0: break;
case 1: break;
}
break;
case 3:
switch(mode){
default: return;
case 2: break;
}
break;
}
doExpensiveCall(system, state, mode);
也就是说,switch
内只有“控制”。编译器应该能够很好地对它进行排序。
这些启发式方法在架构和编译选项之间可能会有所不同(例如-O3
与-Os
),但这就是编译器的用途,根据平台特定的知识做出选择。
为了您提及时间效率,如果您的功能电话真的很贵,那么这部分只会被噪音所掩盖,不用担心。 (或以其他方式对您的代码进行基准测试以确定。)
答案 1 :(得分:0)
如果代码生成器知道正在使用的表的百分比,它可以在构建时选择算法。
因此,如果大约50%真/假使用10 MB表。
否则使用哈希表或基数树。
哈希表会选择哈希函数和多个存储桶。您将计算哈希值,找到存储桶并在链中搜索真(或假)值。
基数树将选择一个基数(如10)并且你有10个条目指向NULL(在那里没有真值),一个指针指向另外10个条目,直到你最终达到一个值。