gpt4 book ai didi

c - 标记 union 中的函数指针为 "tag"

转载 作者:行者123 更新时间:2023-12-04 08:10:24 24 4
gpt4 key购买 nike

我倾向于避免使用标记 union 的原因之一是我不喜欢 switch/case 的性能损失的想法。如果标签的数量大于 4 左右,则标签的语句可能会引入。
我只是有一个想法,而不是使用标签,可以设置一个指向函数的指针,该函数读取 union 中最后写入的值。 .例如:

union u{
char a;
int b;
size_t c;
};

struct fntag{
void (*readval)(union u *ptr, void *out);
union u val;
};
然后,每当您将值写入 val ,您还更新了 readval相应的指针,以便它指向一个函数,该函数读取您在 union 中编写的最后一个字段。是的,有一个难题,就是返回读取值的位置(因为相同的函数指针不能指向返回不同类型的函数)。我选择通过指向 void 的指针返回值以便这样的功能也可以用 C11 “重载” _Generic()从而为输出转换和写入不同的类型。
当然,调用指向函数的指针有性能开销,而且我猜它比检查 enum 的值要重得多。 ,但在某些时候,如果标签数量很大,我相信它会比 switch/case 更快.
我的问题是:你见过在某处使用过这种技术吗? (我没有,我不知道是不是因为现实世界中的这个应用需要 _Generic() 在 readval 函数上,这需要 C11,或者是因为我现在没有注意到的一些问题) .你猜你需要多少标签才能让指向函数的指针比 switch/case 更快 - 在当前的 Intel CPU 中 - ?

最佳答案

你可以这样做。在您的情况下,更优化友好的函数签名是 size_t size_t (*)(union u U) (所有 union 值都可以拟合到 size_t 中,并且 union 足够小,以便通过值传递更有效),但即便如此,函数调用的开销也不容忽视,而且往往明显大于通过交换机生成的跳转表跳转。
尝试类似:

#include <stddef.h>
enum en { e_a, e_b, e_c };
union u{
char a;
int b;
size_t c;
};

size_t u_get_a(union u U) { return U.a; }
size_t u_get_b(union u U) { return U.b; }
size_t u_get_c(union u U) { return U.c; }

struct fntag{ size_t (*u_get)(union u U); union u u_val; };
struct entag{ enum en u_type; union u u_val; };

struct fntag fntagged1000[1000]; struct entag entagged1000[1000];

void init(void) {
for (size_t i=0; i<1000; i++)
switch(i%3){
break;case 0:
fntagged1000[i].u_val.a = i, fntagged1000[i].u_get = &u_get_a;
entagged1000[i].u_val.a = i, entagged1000[i].u_type = e_a;
break;case 1:
fntagged1000[i].u_val.b = i, fntagged1000[i].u_get = &u_get_b;
entagged1000[i].u_val.b = i, entagged1000[i].u_type = e_b;
break;case 2:
fntagged1000[i].u_val.c = i, fntagged1000[i].u_get = &u_get_c;
entagged1000[i].u_val.c = i, entagged1000[i].u_type = e_c;
}
}


size_t get1000fromEnTagged(void)
{
size_t r = 0;
for(int i=0; i<1000; i++){
switch(entagged1000[i].u_type){
break;case e_a: r+=entagged1000[i].u_val.a;
break;case e_b: r+=entagged1000[i].u_val.b;
break;case e_c: r+=entagged1000[i].u_val.c;
/*break;default: __builtin_unreachable();*/
}
}
return r;
}

size_t get1000fromFnTagged(void)
{
size_t r = 0;
for(int i=0; i<1000; i++) r += (*fntagged1000[i].u_get)(fntagged1000[i].u_val);
return r;
}



int main(int C, char **V)
{
size_t volatile r;
init();
if(!V[1]) for (int i=0; i<1000000; i++) r=get1000fromEnTagged();
else for (int i=0; i<1000000; i++) r=get1000fromFnTagged();


}
在 -O2 处,我在基于开关的代码中获得了两倍以上的性能。

关于c - 标记 union 中的函数指针为 "tag",我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/65998964/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com