【发布时间】:2012-01-26 07:45:53
【问题描述】:
我正在检查梯度下降的两种实现之间的差异,我的猜测是,经过编译器优化后,算法的两个版本将是等效的。
令我惊讶的是,递归版本明显更快。我没有放弃任何版本的实际缺陷,甚至没有放弃我测量时间的方式。请各位大神给点意见好吗?
这是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <stdint.h>
double f(double x)
{
return 2*x;
}
double descgrad(double xo, double xnew, double eps, double precision)
{
// printf("step ... x:%f Xp:%f, delta:%f\n",xo,xnew,fabs(xnew - xo));
if (fabs(xnew - xo) < precision)
{
return xnew;
}
else
{
descgrad(xnew, xnew - eps*f(xnew), eps, precision);
}
}
double descgraditer(double xo, double xnew, double eps, double precision)
{
double Xo = xo;
double Xn = xnew;
while(fabs(Xn-Xo) > precision)
{
//printf("step ... x:%f Xp:%f, delta:%f\n",Xo,Xn,fabs(Xn - Xo));
Xo = Xn;
Xn = Xo - eps * f(Xo);
}
return Xn;
}
int64_t timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p)
{
return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec);
}
int main()
{
struct timespec s1, e1, s2, e2;
clock_gettime(CLOCK_MONOTONIC, &s1);
printf("Minimum : %f\n",descgraditer(100,99,0.01,0.00001));
clock_gettime(CLOCK_MONOTONIC, &e1);
clock_gettime(CLOCK_MONOTONIC, &s2);
printf("Minimum : %f\n",descgrad(100,99,0.01,0.00001));
clock_gettime(CLOCK_MONOTONIC, &e2);
uint64_t dif1 = timespecDiff(&e1,&s1) / 1000;
uint64_t dif2 = timespecDiff(&e2,&s2) / 1000;
printf("time_iter:%llu ms, time_rec:%llu ms, ratio (dif1/dif2) :%g\n", dif1,dif2, ((double) ((double)dif1/(double)dif2)));
printf("End. \n");
}
我在 Ubuntu 11.04 上使用 gcc 4.5.2 进行编译,具有以下选项: gcc grad.c -O3 -lrt -o dg
我的代码的输出是:
Minimum : 0.000487
Minimum : 0.000487
time_iter:127 ms, time_rec:19 ms, ratio (dif1/dif2) :6.68421
End.
我阅读了一个线程,该线程还询问算法的递归版本比迭代版本更快。那里的解释是,作为使用堆栈的递归版本和使用一些向量的另一个版本,堆上的访问减慢了迭代版本。但在这种情况下(据我所知)我只是在两种情况下都使用堆栈。
我错过了什么吗?有什么我没有看到的明显的东西吗?我测量时间的方法错了吗?有什么见解吗?
编辑: 谜底在评论中解决。正如@TonyK 所说, printf 的初始化正在减慢第一次执行的速度。很抱歉我错过了那个明显的东西。
顺便说一句,代码编译得恰到好处,没有警告。我不认为“return descgrad(..”是必要的,因为停止条件之前发生过。
【问题讨论】:
-
所有“为什么它更快?”问题应附有编译器输出的汇编代码列表。
-
当
if语句为假的情况下,descgrad的返回语句在哪里?此代码不应编译。 -
@user112358132134:不,它应该只是在编译时出现警告,不是吗?
-
我不相信它会更快。您需要多次迭代这两个调用以平均抖动。您需要从基准测试部分中删除 printf。
-
如果你以另一个顺序运行时序测试(首先是
descgrad,然后是descgraditer),你会得到什么?正如 totowtwo 所暗示的那样,可能是printf初始化需要花费所有时间。