2016-10-07 28 views
9

編輯3:該圖像爲全尺寸版本的鏈接。對不起,圖片的文字,但圖表很難複製/粘貼到文本表。什麼可能導致相同的SSE代碼在相同的函數中運行速度慢幾倍?


我有以下VTune™可視化簡檔用於與icc --std=c++14 -qopenmp -axS -O3 -fPIC編譯的程序:

VTune profile

在該簡檔中,指令兩個簇在裝配視圖突出顯示。儘管指令是相同的並且順序相同,但上部羣集的時間比下部時間少得多。兩個集羣都位於相同的功能內,顯然都稱爲n次。每當我運行探查器時,都會發生這種情況,在我現在使用的Westmere Xeon和Haswell筆記本電腦上(使用SSE進行編譯,因爲這正是我正在尋找和正在學習的內容)。

我錯過了什麼?

忽略可憐的併發,這很可能是由於筆記本電腦限制,因爲它不會在桌面至強機上發生。

我相信這不是一個微優化的例子,因爲這三個加起來相當於總時間的體面百分比,我對這種行爲的可能原因真的很感興趣。

編輯:OMP_NUM_THREADS=1 taskset -c 1 /opt/intel/vtune...

VTune profile

相同的配置,儘管稍低的CPI這個時候。

+0

數據對齊 –

+0

@SegFault然後,它會從RAM或高速緩存到寄存器,這是'movapsx'指令的加載時間在這裏?它只是FPU操作,據我瞭解。 – iksemyonov

+0

多線程?你的圖像很難閱讀,但也許FPU忙於其他的東西在其中一種情況下... –

回答

3

HW perf計數器通常會對不得不等待其輸入的指令進行計時,而不是緩慢產生輸出的指令。

第一組的輸入來自您的收集。這可能會導致緩存失誤,並且這些成本不會計入這些SUBPS/MULPS/ADDPS指令。它們的輸入直接來自矢量負載voxel[],因此存儲轉發失敗會導致一些延遲。但這只是〜10個週期的IIRC,與收集期間的緩存未命中相比較小。 (那些緩存未命中顯示爲之前的大條柱您已突出顯示的第一個組)

第二組的輸入直接來自緩存中可能未命中的負載。在第一組中,緩存未命中負載的直接使用者是像voxel[0]那樣的線條的指令,其具有非常大的條。

但在第二組中,a_transfer[]中的緩存未命中的時間已歸因於您突出顯示的組。或者,如果不是緩存未命中,那麼可能是地址計算速度慢,因爲負載必須等待RAX準備就緒。


它看起來像有一個很多您可以優化這裏

  • ,而不是存儲/重載爲a_pointf,只是不停地跨越循環迭代熱在__m128變量。只有當您發現編譯器對哪個向量寄存器溢出(如果寄存器用完)做出糟糕選擇時,在C源中存儲/重新載入纔有意義。

  • 計算vi_mm_cvttps_epi32(vf),所以ROUNDPS不是聚集索引的依賴關係鏈的一部分。

  • 難道voxel通過將窄載荷轉換爲向量來聚集自己,而不是寫代碼複製到數組,然後從中加載。 (保證存儲轉發失敗,請參閱Agner Fog's optimization guides以及標記wiki中的其他鏈接)。

    它可能是值得的部分矢量化地址數學(計算base_0,使用PMULDQ with a constant vector),所以,而不是存儲/重新加載(~5週期延遲),你只需要一個MOVQ或兩個(約1或2個週期我忘了。)

    使用MOVD加載兩個相鄰的short值,並將另一對合併到PINSRD的第二個元素中。你可能會從_mm_setr_epi32(*(const int*)base_0, *(const int*)(base_0 + dim_x), 0, 0)得到很好的代碼,除了指針別名是未定義的行爲。您可能會從_mm_setr_epi16(*base_0, *(base_0 + 1), *(base_0 + dim_x), *(base_0 + dim_x + 1), 0,0,0,0)得到更糟糕的代碼。

    然後將低4位16位元素擴展爲帶有PMOVSX的32位元素整數,並將其全部轉換爲與_mm_cvtepi32_ps (CVTDQ2PS)並行的float

  • 你的標量LERP沒有被自動矢量化,但是你正在做兩個並行的(也許可以保存一條指令,因爲你希望結果在矢量中)。

  • 調用floorf()是愚蠢的,函數調用強制編譯器將所有xmm寄存器溢出到內存中。用-ffast-math或其他什麼來編譯,讓它內聯到ROUNDSS,或者手動完成。特別是因爲你繼續並將你計算的浮點值加載到矢量中!

  • 使用向量比較而不是標量prev_x/prev_y/prev_z。使用MOVMASKPS將結果轉換爲可以測試的整數。 (您只關心較低的3個元素,因此請使用compare_mask & 0b0111進行測試(如果設置了4位掩碼的低3位中的任何一個,則在與_mm_cmpneq_ps進行比較之後進行測試,請參見double版本的指令關於它是如何工作更多的表:http://www.felixcloutier.com/x86/CMPPD.html

+0

uni的教授(我們這裏有幾個asm民間)告訴我最少需要200s,並提高採樣率。我做了哪些並且獲得了更多關卡結果,但無論如何,這需要進一步的分析。我列出的優化已經讓我獲得了25-30%的收益,儘管在算法的新版本中,在某些情況下,我已經設法將代碼路徑的重量降到最低。還是,學到了很多! – iksemyonov

+0

@iksemyonov:很酷,感謝真正的數字,你得到了多少改善。 200s的聲音聽起來比您通常需要描述的東西更長。 1秒通常可以爲單個相當快速的功能提供良好的結果(假設您考慮CPU頻率上升)。 (即至少〜100k到10M重複調用被測實際功能)。如果您正在測試的代碼需要很長時間才能運行,並觸及大量內存,那麼需要更長的測試間隔來平均多次運行,並讓緩存安置到訪問模式。 –

3

那麼,分析彙編代碼時請注意,運行時間歸因於下一條指令 - 因此,您按指令查看的數據需要仔細解讀。有一個相應的說明中VTune Release Notes

運行時間歸因於下一條指令(200108041)

收集有關 目標,英特爾®VTune耗時運行的區域數據™放大器中斷執行目標 線程並將時間歸於上下文IP地址。

由於收集機制,捕獲的IP地址指向實際上大部分時間消耗的指令之後的指令爲 。 這會導致運行時間歸因於 裝配視圖中的下一條 指令(或者很少到後續指令之一)。在極少數情況下,這也可能導致來源的運行時間錯誤歸屬 - 時間可能會錯誤地歸因於實際熱線後的源線 。

萬一串聯模式爲ON,程序具有在熱點內聯小函數 ,這會導致運行時間是 ,因爲下一個指令歸因於錯誤的功能可以屬於 到一個不同的功能在緊緊內聯代碼。

+0

感謝您的鏈接!我知道這個偏移量,但現在對它的方向感到困惑。在我的例子中,「mul/add/sub」指令是否真的會產生更長的條形? – iksemyonov

+0

指令A的時間將顯示爲指令A + 1的時間 - 因此,實際上由於子/多/加指令會導致較長的條。 – Vital

+0

好的,這是我一直假設的,謝謝澄清!現在,是的,剩下的一點點:找出爲什麼他們在第二個集羣中多花兩倍到三倍的時間,而被稱爲相同的時間量:) – iksemyonov

相關問題