提问人:widesense 提问时间:9/28/2023 最后编辑:timrauwidesense 更新时间:10/2/2023 访问量:83
Union vs void 指针性能 [已关闭]
Union vs void pointer performance [closed]
问:
TL;DR 在性能方面比 void 指针更好
当我搜索 Union vs void 指针性能时,我仔细研究了这个问题:Union vs void pointer。 许多人建议使用联合,但都不是因为性能。我的问题是 void 指针是否比 union 花费更多时间,因为我们需要一次又一次地键入 cast。
我写了下面的代码来测试性能,发现联合要好得多。
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
typedef struct union_test union_test;
typedef struct void_test void_test;
typedef void (*PrintUnionFunction)(union_test *);
typedef void (*PrintVoidFunction)(void_test *);
typedef enum ELEM_TYPE
{
INT_TYPE,
STRING_TYPE,
FLOAT_TYPE
} ELEM_TYPE;
struct union_test
{
ELEM_TYPE elemType;
union {
int **intElems;
float **floatElems;
char **stringElems;
};
size_t elemCount;
PrintUnionFunction printFunction;
};
struct void_test
{
ELEM_TYPE elemType;
void **elems;
size_t elemCount;
PrintVoidFunction printFunction;
};
void printUnionInt(union_test *fpTest)
{
for (size_t i = 0; i < fpTest->elemCount; i++)
{
int *temp = fpTest->intElems[i];
(*temp)++;
// printf("%d ", *fpTest->intElems[i]);
}
}
void printVoidPointerInt(void_test *fpTest)
{
for (size_t i = 0; i < fpTest->elemCount; i++)
{
int *temp = ((int *)fpTest->elems[i]);
(*temp)++;
// printf("%d ", *fpTest->intElems[i]);
}
}
int main()
{
clock_t start_time_union, end_time_union;
clock_t start_time_void, end_time_void;
size_t elemCount = 1024 * 1024 * 1024;
union_test *fp = (union_test *)malloc(sizeof(union_test));
fp->elemCount = elemCount;
fp->elemType = INT_TYPE;
fp->printFunction = printUnionInt;
fp->intElems = (int **)malloc(sizeof(int *) * fp->elemCount);
for (size_t i = 0; i < fp->elemCount; i++)
{
fp->intElems[i] = (int *)malloc(sizeof(int));
memcpy(fp->intElems[i], &i, sizeof(int));
}
void_test *void_fp = (void_test *)malloc(sizeof(union_test));
void_fp->elemCount = elemCount;
void_fp->elemType = INT_TYPE;
void_fp->printFunction = printVoidPointerInt;
void_fp->elems = (void **)malloc(sizeof(void *) * void_fp->elemCount);
for (size_t i = 0; i < void_fp->elemCount; i++)
{
void_fp->elems[i] = (int *)malloc(sizeof(int));
memcpy(void_fp->elems[i], &i, sizeof(int));
}
start_time_union = clock();
fp->printFunction(fp);
end_time_union = clock();
start_time_void = clock();
void_fp->printFunction(void_fp);
end_time_void = clock();
printf("\n\nunion execution time: %f seconds\n", (double)(end_time_union - start_time_union) / CLOCKS_PER_SEC);
printf("void pointer execution time: %f seconds\n", (double)(end_time_void - start_time_void) / CLOCKS_PER_SEC);
return 0;
}
我得到了以下结果。
union execution time: 8.237730 seconds
void pointer execution time: 8.647505 seconds
我在某些地方看到使用 -O3 会使编译器永远不会打扰指针类型,并将所有内容视为仅内存字节,但即使使用 -O3 标志,我也找不到任何改进的性能。
注意:我只关心性能,而不关心可读性。我知道,当我们要处理的类型数量有限时,使用联合是提高可读性的好方法。
答:
1赞
KamilCuk
9/28/2023
#1
您的代码在带有 gcc13.2 和 -O3 的 godbolt 上生成以下程序集:
printUnionInt:
mov rdx, QWORD PTR [rdi+16]
test rdx, rdx
je .L1
mov rax, QWORD PTR [rdi+8]
lea rcx, [rax+rdx*8]
.L3:
mov rdx, QWORD PTR [rax]
add rax, 8
add DWORD PTR [rdx], 1
cmp rcx, rax
jne .L3
.L1:
ret
printVoidPointerInt:
mov rdx, QWORD PTR [rdi+16]
test rdx, rdx
je .L9
mov rax, QWORD PTR [rdi+8]
lea rcx, [rax+rdx*8]
.L11:
mov rdx, QWORD PTR [rax]
add rax, 8
add DWORD PTR [rdx], 1
cmp rcx, rax
jne .L11
.L9:
ret
我拿了一把尺子,测量了每个函数的长度,它完全一样。没有区别。
您可能对 https://quick-bench.com/ 感兴趣。
我的问题是 void 指针是否比 union 花费更多时间,因为我们需要一次又一次地键入 cast。
在程序集中没有类型,寄存器就是寄存器。在 C 世界中,转换指针是某种东西,在汇编中它是空操作,什么都不会发生。
评论
0赞
widesense
9/28/2023
那么为什么无效指针方法需要更多时间呢?
3赞
infinitezero
9/28/2023
@widesense可能是因为您的测试方法有问题。
0赞
David C. Rankin
9/28/2023
干巴巴的机智几乎和讽刺一样好......一把尺子?:)
0赞
widesense
9/28/2023
问题是由于发生了大量页面交换,我的机器中的内存分配过多。
评论
for (int counter = 0; counter < 10; counter++) { … }
main()
main()