2015-07-02 47 views
4

我在優化我的代碼爲我的N體模擬的過程,剖析我的代碼的時候,已經看到了這一點:相同的操作採取不同的時間

enter image description here

這兩條線,


float diffX = (pNode->CenterOfMassx - pBody->posX); 
float diffY = (pNode->CenterOfMassy - pBody->posY); 

pNode是一個指針,它指向我已經定義Node類型的對象,並且包含(與其他事物)2個浮筒,CenterOfMassxCenterOfMassy

凡​​是指向Body型我已經定義的對象,並且包含(與其他事物)2個浮筒,posXposY


應該採取的時間是相同的,但不要。實際上,第一行佔功能樣本的0.46%,而第二行佔5.20%。

現在我可以看到第二行有3條指令,第一條只有一條。

我的問題是爲什麼這些看似做同樣的事情,但在實踐中不同的事情?

回答

5

如前所述,profiler只列出一條彙編指令,第一行顯示三條,第二行顯示三條彙編指令。但是,因爲優化器可以將代碼移動很多,所以這不是很有意義。它看起來像代碼被優化,首先加載所有的值到寄存器,然後執行減法。所以它從第一行開始執行一個動作,然後從第二行開始執行一個動作(負載),接着是第一行的動作和第二行的動作(減法)。由於這很難表示,因此它只是在顯示與代碼內聯的反彙編代碼時對哪個彙編代碼進行了最佳近似。

請注意,執行第一個加載時,可能仍會在下一個加載指令執行時位於CPU管道中。第二個負載不依賴於第一個負載中使用的寄存器。然而,第一次減法的確如此。該指令要求先前的加載指令在流水線中足夠遠,以便結果可以用作減法的一個操作數。這可能會導致CPU停滯,而管道則讓負載結束。

這一切真的加強了內存優化是對現代的CPU比CPU的優化更爲重要的概念。例如,如果您之前將所需的值加載到寄存器15的指令中,則減法可能發生得更快。

通常,您可以爲優化做的最好的事情是保持緩存與您將要使用的內存一致,並確保它儘快得到更新,而不是在需要內存之前。除此之外,優化複雜

當然,這一切還通過現代CPU複雜,可能向前看40-60指令out of order execution.

優化這個進一步,你可以考慮使用,做一個優化的向量和矩陣運算庫方式。使用這些庫中的一個,可以使用兩個向量指令而不是4個標量指令。

3

根據擴展的程序集,將指令重新排序,在與​​的數據成員進行相減之前,加載pNodes的數據成員。目的可能是利用內存緩存。

因此,執行順序不再與C代碼相同。比較第一個C語句和第一個C語句佔1個movss + 2子句的1個movss是不公平的。

+0

那麼,爲什麼代碼行會變成不同的彙編指令,並且有什麼方法可以加快第二行的速度? –

+0

每行變成相同的兩條asm指令,但是它們被重新排序。一個源代碼行獲取一個insn,另一個獲取3,因爲否則,asm指令將不得不按順序顯示。這不是要利用緩存,順便說一句。這是爲了更好地隱藏負載的延遲。 –

+0

如果'pNode'與'pBody'很遠,是不是通過在'* pBody'之前加載'* pNode'來比較交錯來利用緩存? – timrau

1

性能計數器不是週期精確的。有時候錯誤的指導會被指責。但是在這種情況下,它可能會指向產生結果的指令,其他所有事情都在等待。

因此,在等待內存訪問和FP子結果的時候,可能會耗盡它可能做的事情。如果發生緩存遺漏,請尋找方法來構建代碼以獲得更好的內存局部性,或者至少讓內存訪問按順序發生。硬件預取程序可以檢測到達步長限制的順序訪問模式。

另外,你的編譯器可以將這個向量化。它從連續地址加載兩個標量,然後從連續地址中減去兩個標量。這將是更快地

movq xmm0, [esi+30h] # or movlps, but that wouldn't break the dep 
movq xmm1, [edi]  # on the old value of xmm0/xmm1 
subps xmm0, xmm1 

這使得在元件0 diffXdiffYxmm0 1,而不是2個不同的暫存器,因此好處取決於周圍的代碼。

相關問題