C言語の数学関数のうちpow, sin, asin, abs, sqrt, exp, logについてfloat,double,long doubleの実行速度を比べてみた。出したのはdouble, long doubleの計算時間とfloatの計算時間の比。
生成する乱数の範囲が適当かどうかわからない。ご指摘大歓迎です。
#include<stdio.h>
#include<stdlib.h>
#include<math.h>
#include<string.h>
#include<time.h>
#include<sys/time.h>
double elapsed(void)
{
struct timeval tp;
struct timezone tz;
if (gettimeofday(&tp, &tz) != 0)
printf("error at elapsed\n");
return (double) tp.tv_sec + (double) tp.tv_usec * 1.0e-6;
}
float randf()
{
return 1e5f - 2e5f * (rand() / (float)RAND_MAX);
}
double randd()
{
return 1e5 - 2e5 * (rand() / (double)RAND_MAX);
}
long double randl()
{
return 1e5L - 2e5L * (rand() / (long double)RAND_MAX);
}
int n = 50000;
double timeit(void (*f)(void))
{
int i;
double t = elapsed();
for (i = 0; i < n; i++)
f();
t = elapsed() - t;
return t;
}
void Powf () { powf (randf(), randf()); }
void Sinf () { sinf (randf()); }
void Asinf() { asinf (randf()); }
void Absf () { fabsf (randf()); }
void Sqrtf() { sqrtf (randf()); }
void Expf () { expf (randf()); }
void Logf () { logf (randf()); }
void Pow () { pow (randd(), randd()); }
void Sin () { sin (randd()); }
void Asin () { asin (randd()); }
void Abs () { fabs (randd()); }
void Sqrt () { sqrt (randd()); }
void Exp () { exp (randd()); }
void Log () { log (randd()); }
void Powl () { powl (randl(), randl()); }
void Sinl () { sinl (randl()); }
void Asinl() { asinl (randl()); }
void Absl () { fabsl (randl()); }
void Sqrtl() { sqrtl (randl()); }
void Expl () { expl (randl()); }
void Logl () { logl (randl()); }
void Randf(){ randf(); }
void Randd(){ randd(); }
void Randl(){ randl(); }
int main(int argc, char** argv)
{
#define FLT "%.5lf "
#define FMT "%s " FLT FLT FLT FLT FLT "\n"
int i;
char fmt[256]=FMT;
srand(time(NULL));
if (argc >= 2)
n = atoi(argv[1]);
printf("n = %d\n", n);
void (*fun[8][3])(void) = {
{Powf, Pow, Powl}, {Sinf, Sin, Sinl}, {Asinf, Asin, Asinl},
{Absf, Abs, Absl}, {Sqrtf, Sqrt, Sqrtl}, {Expf, Exp, Expl},
{Logf, Log, Logl}, {Randf, Randd, Randl}};
char fname[8][128] = {"pow", "sin", "asin", "abs", "sqrt", "exp", "log", "rand"};
for (i = 0; i < 8; i++){
double f = timeit(fun[i][0]);
double d = timeit(fun[i][1]);
double l = timeit(fun[i][2]);
printf(FMT, fname[i], f, d, l, d/f, l/f);
}
}
実行結果(表)
Core2 QuadのマシンとCore2 DuoのマシンとAtom N270のマシン(dell inspiron mini9)で実行してみた。コンパイルオプションはいずれも"-lm -O3"にした。繰り返し回数は1000万回に設定した。
関数 |
float |
double |
ldouble |
d/f比 |
l/f比 |
pow |
1.01140 |
1.32000 |
2.08669 |
1.30512 |
2.06316 |
sin |
0.09872 |
0.10074 |
0.09872 |
1.02043 |
0.99994 |
asin |
0.26519 |
0.39756 |
1.50401 |
1.49916 |
5.67148 |
abs |
0.10074 |
0.09871 |
0.09872 |
0.97986 |
0.97987 |
sqrt |
0.29654 |
0.35533 |
1.54225 |
1.19826 |
5.20082 |
exp |
0.72587 |
0.40774 |
1.38476 |
0.56173 |
1.90772 |
log |
0.43713 |
0.59930 |
0.73032 |
1.37101 |
1.67072 |
rand |
0.09879 |
0.09875 |
0.10072 |
0.99952 |
1.01950 |
OS |
Ubuntu 8.04 32bit |
Mem |
2G |
gcc |
4.2.4; 4.2.4-1ubuntu3 |
関数 |
float |
double |
ldouble |
d/f比 |
l/f比 |
pow |
6.46111 |
6.82862 |
7.63098 |
1.05688 |
1.18106 |
sin |
0.26396 |
0.26451 |
0.26510 |
1.00208 |
1.00431 |
asin |
2.87325 |
3.06951 |
2.47927 |
1.06831 |
0.86288 |
abs |
0.26445 |
0.26461 |
0.26444 |
1.00059 |
0.99993 |
sqrt |
3.00352 |
3.18386 |
3.26626 |
1.06004 |
1.08748 |
exp |
2.29557 |
2.41368 |
2.33544 |
1.05145 |
1.01737 |
log |
1.11410 |
1.28898 |
1.40549 |
1.15697 |
1.26154 |
rand |
0.26454 |
0.26420 |
0.26420 |
0.99872 |
0.99872 |
OS |
Windows XP SP3 |
Mem |
1G |
gcc |
mingw4.2.1 (Ubuntu8.04のmingw32パッケージ) |
関数 |
float |
double |
ldouble |
d/f比 |
l/f比 |
pow |
11.39063 |
11.21875 |
12.50000 |
0.98491 |
1.09739 |
sin |
0.48438 |
0.50000 |
0.50000 |
1.03226 |
1.03226 |
asin |
6.14063 |
6.14063 |
6.18750 |
1.00000 |
1.00763 |
abs |
0.48438 |
0.50000 |
0.48438 |
1.03226 |
1.00000 |
sqrt |
3.07813 |
5.46875 |
3.20313 |
1.77665 |
1.04061 |
exp |
11.32813 |
11.40625 |
1.95313 |
1.00690 |
0.17241 |
log |
2.90625 |
6.15625 |
2.59375 |
2.11828 |
0.89247 |
rand |
0.51563 |
0.48438 |
0.51563 |
0.93939 |
1.00000 |
どういう実装になってるんでしょ
absの実行速度が型で変わらないのは、どの型でも符号ビットを反転させるだけだろうから納得。
sin,sqrt,logなどに関しては要求される精度が高くなるほど時間がかかるのかなと思っていたらそうではなかった。特にatomN270でのlong doubleのexpは他の型のexpよりもずっと早い。
glibcでは数学関数ライブラリをどう実装してるんだろーか。気が向いたら調べる。