在C語言中,我將回圈執行的總次數減少了近3倍,但通過測驗執行時間,我發現這樣做幾乎沒有改善。所有的優化級別都進行了測驗,結果基本相同(包括O0、O1、O2和O3)。我猜這是編譯器的問題,但我想知道是什么原因造成這種情況的。該如何做才能使結果符合預期呢?
代碼如下:
#include <stdio.h>/span>
#include <time.h>
#include <stdlib.h>
#define Len 10000000
//兩個計算回圈次數的變數。
int count1 = 0;
int count2 = 0;
int main(int argc, const char * argv[]) {
srandom((unsigned)time(NULL))。
//一個陣列來增加索引,。
//其元素的范圍是1-256。
int rand_arr[128] 。
for (int i = 0; i < 128; i)
rand_arr[i] = random()%256 1。
//一個隨機文本,其元素的范圍是0-127。
char *tex = malloc((sizeof *tex) * Len) 。
for (int i = 0; i < Len; i)
tex[i] = random()%128;
//第一次測驗。
clock_t start = clock();
for (int i = 0; i < Len; i = rand_arr[tex[i]] )
count1 ;
printf("No.1: %lf s
", ((double)(clock() - start)) / clocks_per_sec)。)
//第二次測驗(x3)
start = clock()。
for (int i = 0; i < Len; i = rand_arr[tex[i]] 256)
count2 。
printf("No.2: %lf s
", ((double)(clock() - start)) / clocks_per_sec)。)
printf("count1: %d
", count1)。)
printf("count2: %d
", count2)。)
return 0。
}
列印結果(平均)如下:
No.1。0.002213 s
沒有.2。0.002209 s
count1。72661: 72661
count2: 25417
uj5u.com熱心網友回復:
問題來自于處理器本身而不是編譯器。這是一個復雜的問題,與CPU快取、CPU預取單元和隨機訪問模式
的行為有關。這兩個代碼都是基于tex陣列讀取i值,由于rand_arr中存盤的隨機增量,處理器不能輕易提前預測。由于tex相對較大,它很可能不會完全存盤在L1快取中(如果有的話,也不會存盤在中間的L2快取中),而是存盤在最后一級快取(LLC)甚至RAM中。因此,tex需要在每個回圈中從LLC高速快取或RAM中重新加載。現在LLC快取和RAM的延遲比較大。這就意味著第二個回圈比第一個回圈更難預測,對快取也更不友好,盡管迭代次數更少!
在x86 CPU上。
在x86 CPU上,快取按64位元組的塊來打包數值,稱為快取行。當從主記憶體或其他快取中獲取一個值時(通常是由于一個快取缺失),會獲取一個完整的快取行。對同一快取行的后續訪問會更快,因為CPU不需要再次獲取它(只要該快取行沒有失效)。
在第一個回圈中,i的平均增量是128(因為rand_arr的平均值是128)。這意味著從tex獲取的兩個專案之間的平均跨度是128。在最壞的情況下,stride是256。在第二個回圈中,平均跨度是256 128=384,在最壞的情況下是256 256=512。當stride小于64時,在第一種情況下很有可能已經被取走了,而在第二種回圈中卻沒有這種情況發生。此外,預取單元可以在幾個訪問是連續的或相互接近的情況下預取快取行。這使得處理器能夠在第一個回圈中提前獲取tex陣列的大部分專案。同時,在第二個回圈中,預取器很可能無法識別任何高速快取線的取值訪問。預取單元很可能不會預取任何東西(因為這樣做成本太高),結果是許多高速快取錯過,由于訪問本身是連續的,不可預測的,所以無法緩解延遲。如果預取單元決定預取所有的快取行,那么第二個回圈不應該比第一個回圈快(因為這兩個回圈都受到記憶體層次的約束)。
注意,random和srandom不是標準函式。
另外,請注意,clock在所有平臺上并不總是精確的。實際上,它在我的Windows上的精度為1ms(使用GCC和MinGW)。這也可以在一些Linux系統上看到。
uj5u.com熱心網友回復:
有幾件事情可能會導致這種情況:
如果你的電腦上有一個 "小 "字,你就會發現它是一個 "大 "字。
如果你在為你的代碼計時,你應該有:
startTime = clock();
代碼
endTime = clock()。
然后列印你的結果/對其進行分析。你在上面做了一些數學運算,并使用了PRINTF函式,這在時間上是非常低效的。另外,投遞到double是沒有必要的,而且可能導致你的大部分時間,因為雙倍的數學運算是非常慢的。堅持使用int,這可能會快1000倍
簡單的回圈代碼 - 回圈方程的標準是:
for(int i = 0; i<length;i ) 代碼
你有
for(int i = 0; i<length;code)
i ;
就語法而言,這很奇怪,而且可能會影響你的時間安排
clock() - 這可能會影響你的計時。如果clock()回傳的是double,我建議用另一種方式,用一個回傳int或unsigned int的函式,因為如上所述,double會破壞你的計時。如果你擔心這個問題,我建議通過以下方式進行測驗:
startTime = clock()
for(int i = 0; i<10000; i )
dummy = clock()
endTime = clock()
totalTime = (endTime - startTime)/10000。
for回圈--這本身就可能是你的基準時間的主要來源(盡管這不太可能,特別是你似乎沒有做任何特別復雜的數學運算。你可以使用#pragma unroll處理器指令來解決這個問題,這基本上會將你的for回圈的所有迭代復制并粘貼到你的代碼中,消除它對時間的影響
轉載請註明出處,本文鏈接:https://www.uj5u.com/qiye/326842.html
標籤:
上一篇:為什么增加一個if(!memcmp())會加快一個在巨大的位元組陣列中隨機短跨度的回圈?
下一篇:用平方根、正弦和余弦解方程
