2013-04-03 57 views
3

我正在使用以下代碼來測試初始化​​後在運行時刷新高速緩存的效果:daxpy例程(具有fill()和wall_time()例程的完整代碼位於以下位置: http://codepad.org/QuLT3cbD - 它是150行):高速緩存刷新後定時的非常高的不確定性

#define KB 1024 

int main() 
{ 
    int cache_size = 32*KB; 
    double alpha = 42.5; 

    int operand_size = cache_size/(sizeof(double)*2); 
    double* X = new double[operand_size]; 
    double* Y = new double[operand_size]; 


    //95% confidence interval 
    double max_risk = 0.05; 
    //Interval half width 
    double w; 
    int n_iterations = 100; 
    students_t dist(n_iterations-1); 
    double T = boost::math::quantile(complement(dist,max_risk/2)); 
    accumulator_set<double, stats<tag::mean,tag::variance> > unflushed_acc; 

    for(int i = 0; i < n_iterations; ++i) 
    { 
     fill(X,operand_size); 
     fill(Y,operand_size); 
     double seconds = wall_time(); 
     daxpy(alpha,X,Y,operand_size); 
     seconds = wall_time() - seconds; 
     unflushed_acc(seconds); 
    } 

    w = T*sqrt(variance(unflushed_acc))/sqrt(count(unflushed_acc)); 
    printf("Without flush: time=%g +/- %g ns\n",mean(unflushed_acc)*1e9,w*1e9); 

    //Using clflush instruction 
    //We need to put the operands back in cache 
    accumulator_set<double, stats<tag::mean,tag::variance> > clflush_acc; 
    for(int i = 0; i < n_iterations; ++i) 
    { 
     fill(X,operand_size); 
     fill(Y,operand_size); 

     flush_array(X,operand_size); 
     flush_array(Y,operand_size); 
     double seconds = wall_time(); 
     daxpy(alpha,X,Y,operand_size); 
     seconds = wall_time() - seconds; 
     clflush_acc(seconds); 
    } 

    w = T*sqrt(variance(clflush_acc))/sqrt(count(clflush_acc)); 
    printf("With clflush: time=%g +/- %g ns\n",mean(clflush_acc)*1e9,w*1e9); 

    return 0; 
} 

當運行該代碼,它報告這些數字的速率和不確定性在其中(在95%置信水平):

沒有沖水:時間= 3103.75 +/- 0.524506 ns 使用clflush:時間= 4651.72 +/- 201.25 ns

爲什麼使用clflush刷新操作數X和Y從緩存中增加超過100倍的測量噪聲?

回答

4

在3GHz時,52納秒是1.5個CPU週期,201.25納秒是604個CPU週期......考慮到當它在緩存層次結構中未命中時,需要幾百個CPU週期或更多時間才能從DRAM中讀取緩存行測量由1或2個緩存行未命中引起的變化......這並不多。在未刷新的情況下,您對非常嚴格的平均時間讀數非常嚴格......沒有發生緩存未命中事件。

我發現通過將我的Mac上的迭代次數增加到大約5000次(您可以在100次迭代中獲得),我可以獲得與未刷新的案例一樣的沖刷案例的變化。它有理由認爲,數據不在緩存中的平均時間大於緩存中的全部時間 - 但並不像您預期​​的那麼慢 - 這是因爲CPU在預測時非常有效你的情況是訪問模式,並在預期使用之前(推測性地)預取數據(前面提供了許多緩存行,實際上是爲了隱藏進入DRAM的相對較長的延遲)。

CPU可能會在預期使用之前發出多個數據高速緩存(和指令高速緩存)預取...通過同時有很多內存讀取它可以有效減少內存延遲(假設它正確猜測當然)。這引入了非確定性。在未刷新的情況下,實際上所有數據都位於1級數據緩存中 - 堆棧內存引用除了32KB數據之外,因此會導致L1數據緩存溢出,但這並不是很多,並且很快會從二級緩存 - 重點是沒有必要去內存控制器/ DRAM。在刷新的情況下,你的數據只在內存中,所以你根據處理器預取如何爭奪內存控制器(數據和指令)以及共享相同內存控制器的其他內核中發生的任何事情來獲得可變性。通過長時間運行,您可以讓系統進入該訪問模式的「正常」模式,並降低方差。

+0

因此,高速緩存行未命中數量的變化是由於變化有效的預取?有時它能夠比其他預取更多的數據,並且通過啓動迭代平滑了這一點? –

+0

是的,我已經爲我的答案添加了額外的解釋(太長以至於無法評論)。 – amdn

+0

好的。這很好,黑盒子修正只會增加迭代的次數,直到不確定性收斂到總速率的1%。謝謝! –