[英]Why is atan first call much slower than the next ones?
以下代碼演示了tan的計算時間可能相差很大:
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
double get_time()
{
struct timeval t;
struct timezone tzp;
gettimeofday(&t, &tzp);
return t.tv_sec + t.tv_usec*1e-6;
}
int main() {
double worst_time = 0.0;
double best_time = 1e6;
volatile double x0 = -M_PI/2.0;
volatile double foo = atan(x0); // SLOW CALL HERE
volatile double sum = 0.0; // volatile to avoid having tan() call optimized away
for (double x = x0; x < M_PI/3.0; x += 0.1) {
volatile double y = x;
const double start = get_time();
asm volatile ("":::"memory"); // avoid reordering in -O3
const double value = atan(y);
asm volatile ("":::"memory"); // avoid reordering
const double end = get_time();
sum += value;
const double delta = end - start;
if (delta > worst_time) {
worst_time = delta;
}
if (delta < best_time) {
best_time = delta;
}
printf("* %f (value: %f)\n", delta, y);
}
printf("%f / %f\n", worst_time, best_time);
printf("%f\n", foo);
}
從我的機器來看,最差的時間是15us左右,而最理想的時間是0(太小而無法測量)。
我的機器上的平均時間(此處未顯示)約為1或2 us。
我嘗試了不同的編譯標志(-O3,靜態鏈接到libm等),但是我找不到導致最差時間變慢的原因。 任何想法?
編輯:我正在使用Ubuntu 14.04-gcc 4.8.4
edit2:將atan2替換為atan。 我對atan2是逐段定義的並且不同的分支可能花費不同的時間這一事實不感興趣。 我有興趣消除即使調用atan而不是atan2也會出現的異常值。
EDIT3:
* 0.000015 (value: -1.570796)
* 0.000000 (value: -1.470796)
* 0.000001 (value: -1.370796)
* 0.000001 (value: -1.270796)
* 0.000000 (value: -1.170796)
* 0.000002 (value: -1.070796)
* 0.000000 (value: -0.970796)
* 0.000001 (value: -0.870796)
* 0.000000 (value: -0.770796)
* 0.000000 (value: -0.670796)
* 0.000001 (value: -0.570796)
* 0.000000 (value: -0.470796)
* 0.000003 (value: -0.370796)
* 0.000001 (value: -0.270796)
* 0.000000 (value: -0.170796)
* 0.000000 (value: -0.070796)
* 0.000001 (value: 0.029204)
* 0.000000 (value: 0.129204)
* 0.000002 (value: 0.229204)
* 0.000001 (value: 0.329204)
* 0.000000 (value: 0.429204)
* 0.000001 (value: 0.529204)
* 0.000001 (value: 0.629204)
* 0.000001 (value: 0.729204)
* 0.000001 (value: 0.829204)
* 0.000001 (value: 0.929204)
* 0.000000 (value: 1.029204)
0.000015 / 0.000000 / 0.000001
edit4:
看來第一個電話是元凶! 循環外部的調用已由編譯器優化,如果我們強制atan在x0
的循環外部求值,則所有調用都相當快...
* 0.000000 (value: -1.570796)
* 0.000001 (value: -1.470796)
* 0.000000 (value: -1.370796)
* 0.000002 (value: -1.270796)
* 0.000001 (value: -1.170796)
* 0.000001 (value: -1.070796)
* 0.000000 (value: -0.970796)
* 0.000000 (value: -0.870796)
* 0.000000 (value: -0.770796)
* 0.000001 (value: -0.670796)
* 0.000000 (value: -0.570796)
* 0.000000 (value: -0.470796)
* 0.000006 (value: -0.370796)
* 0.000001 (value: -0.270796)
* 0.000002 (value: -0.170796)
* 0.000001 (value: -0.070796)
* 0.000000 (value: 0.029204)
* 0.000001 (value: 0.129204)
* 0.000003 (value: 0.229204)
* 0.000000 (value: 0.329204)
* 0.000000 (value: 0.429204)
* 0.000000 (value: 0.529204)
* 0.000001 (value: 0.629204)
* 0.000000 (value: 0.729204)
* 0.000000 (value: 0.829204)
* 0.000000 (value: 0.929204)
* 0.000000 (value: 1.029204)
0.000006 / 0.000000
時間差異實際上是由頁面錯誤(!)引起的。 第一次調用該函數時,將訪問包含atan2代碼的頁面,並發生頁面錯誤。 使用mlockall()應該可以改善這種情況。
atan2是一個分段函數,即對於某些值/值范圍,它執行不同的操作,其中一些只是返回一個常數值,這是相當快的,但是其他一些則涉及實際的三角計算,這可能需要相當長的時間。 如果您想了解詳細信息, 請訪問https://en.wikipedia.org/wiki/Atan2
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.