两个C语言tuple乘法函数的性能差异及原因咨询
问题
若需调用以下两个C语言tuple乘法函数数百万次,二者的性能差异是什么?我最初认为tuple_mul1无需额外栈分配,性能更优,但测试显示tuple_mul2更快:2亿次调用中,tuple_mul1耗时约1.55秒,tuple_mul2耗时约1秒(使用cc无优化选项编译)。请解释该现象原因。
相关代码
typedef struct s_tuple{ double x; double y; double z; double w; double m; double n; double o; double p; } t_tuple; // (1) t_tuple tuple_mul1(const double q, t_tuple a) { a.x *= q; a.y *= q; a.z *= q; a.w *= q; a.m *= q; a.n *= q; a.o *= q; a.p *= q; return a; } // (2) t_tuple tuple_mul2(const double q, const t_tuple a) { t_tuple b; b.x = a.x * q; b.y = a.y * q; b.z = a.z * q; b.w = a.w * q; b.m = a.m * q; b.n = a.n * q; b.o = a.o * q; b.p = a.p * q; return b; }
测试代码
#include <time.h> #include <stdio.h> #include <stdlib.h> typedef struct s_tuple{ double x; double y; double z; double w; double m; double n; double o; double p; } t_tuple; // (1) t_tuple tuple_mul1(const double q, t_tuple a) { a.x *= q; a.y *= q; a.z *= q; a.w *= q; a.m *= q; a.n *= q; a.o *= q; a.p *= q; return a; } // (2) t_tuple tuple_mul2(const double q, const t_tuple a) { t_tuple b; b.x = a.x * q; b.y = a.y * q; b.z = a.z * q; b.w = a.w * q; b.m = a.m * q; b.n = a.n * q; b.o = a.o * q; b.p = a.p * q; return b; } int main(int ac, char **av) { int i; long int n; double q; t_tuple a; clock_t start, end; q = 0.7; a.x = 1.5; a.y = 2; a.z = 35897.78; a.w = 4.6698; a.m = 5.5; a.n = 1065; a.o = 11.6887; a.p = 109090808.789; if (ac > 1) { n = atol(av[1]); double execution_time; start = clock(); for (i = 0; i < n; i++) tuple_mul1(q, a); // tuple_mul2(q, a); end = clock(); execution_time = ((double)(end - start))/CLOCKS_PER_SEC; printf("exec_time = %f\nn = %.f * 1e6\n", execution_time, n / 1e6); } }
原因解释
在无优化编译的场景下,两个函数的性能差异核心源于内存访问模式的不同,具体如下:
成员操作的内存周期差异
tuple_mul1中每个成员执行*=操作:先从栈上读取成员值,乘以q后写回原栈位置,属于读-修改-写三步操作。无优化时,编译器不会合并这些步骤,CPU需要等待写操作完成才能进行下一次读,产生额外内存延迟。tuple_mul2中每个成员执行直接赋值:从栈上读取源结构体成员值,乘以q后写入新结构体位置,属于读-写两步操作。源结构体成员处于只读状态,更容易被CPU缓存命中,且无同一内存地址的重复读写冲突,流水线执行更顺畅。
栈分配的实际开销可忽略
你之前认为tuple_mul1无需额外栈分配,但tuple_mul2中创建的t_tuple b只是栈指针的简单移动,几乎没有额外开销;而tuple_mul1的读-修改-写带来的内存周期开销,远大于栈分配的成本。返回值拷贝的开销基本一致
两个函数返回结构体时,都需要将结果拷贝到调用者指定位置,这部分开销在无优化下基本相同,不会造成明显性能差异。
综上,tuple_mul2更高效的内存访问模式,使其在无优化编译下比tuple_mul1更快。
内容的提问来源于Stack Exchange,提问作者BobDeTunis
相关产品推荐
相关产品推荐

