很多时候,处于减少函数栈帧的申请开销,一些奇怪的建议(尤其是出于搞ACM的经验 会 诱导 我们把max/min这种小函数定义为宏。
通常情况下,这种优化策略是没得问题的,但是有时宏的简单替换不但没法减少开销,反而会大大增加开销的。
这个例子是我从《编程珠玑》看来的。
求一个数组中的最大值。
方法一
把max定义为函数
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int max(int a, int b)
{
return a > b ? a : b;
}
int argmax(int* a, int n)
{
if (n == 1)
{
return a[0];
}
return max(a[n - 1], argmax(a, n - 1));
}
#define N 28
int a[N] = { 0 };
int main()
{
srand(time(0));
for (int i = 0; i < N; i++)
{
a[i] = N-i;
}
long long int start = time(0);
int m = argmax(a, N);
long long int end = time(0);
printf("[%lld,%lld]:%d\n", start, end, m);
system("pause");
return 0;
}
运行结果:基本在毫秒级就得到了结果。
方法二:
把max定义为宏
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#define max(a,b) (a)>(b)?(a):(b)
//int max(int a, int b)
//{
// return a > b ? a : b;
//}
int argmax(int* a, int n)
{
if (n == 1)
{
return a[0];
}
return max(a[n - 1], argmax(a, n - 1));
}
#define N 28
int a[N] = { 0 };
int main()
{
srand(time(0));
for (int i = 0; i < N; i++)
{
a[i] = N-i;
}
long long int start = time(0);
int m = argmax(a, N);
long long int end = time(0);
printf("[%lld,%lld]:%d\n", start, end, m);
system("pause");
return 0;
}
运行结果:运行了8s才出结果。。。。
为何???
差别如此巨大这是为何呢???
本质是因为宏的 无脑替换。
如果是函数实现,则max(a[n-1],argmax(a,n-1))
中,只计算一次argmax(a,n-1]
然后把结果作为参数送给max函数。
如果是宏实现:
max(a[n-1],argmax(a,n-1))
其实展开后是a[n-1] > argmax(a,n-1)? a[n-1]:argmax(a,n-1)
,第n层需要计算两次 argmax(a,n-1)
,第n-1层需要计算4次argmax(a,n-2)
这样下去,其实是
求一个数组的最大值。
这个例子说明,有的时候宏还真不一定函数更快。宏的快更多是鼠目寸光式的快,它只引入短期的加速,但是如果宏的参数是函数调用,那么就要特别注意了!!!因为宏的无脑替换,有可能明明只需计算一次的参数,在宏的展开却要计算多次。