簡體   English   中英

什么可能導致相同的SSE代碼在同一個函數中運行速度慢幾倍?

[英]What might cause the same SSE code to run a few times slower in the same function?

編輯3:圖像是指向完整版本的鏈接。 對於文本圖片感到抱歉,但圖表很難復制/粘貼到文本表中。


對於使用icc --std=c++14 -qopenmp -axS -O3 -fPIC編譯的程序,我有以下VTune配置文件icc --std=c++14 -qopenmp -axS -O3 -fPIC

VTune簡介

在該配置文件中,組裝視圖中突出顯示了兩組指令。 盡管指令相同且順序相同,但上部集群比下部集群花費的時間少得多。 兩個集群都位於同一個函數內,顯然都被稱為n次。 每次我運行探查器時都會發生這種情況,我現在正在使用Westmere Xeon和Haswell筆記本電腦(使用SSE編譯,因為這就是我現在正在瞄准和學習的東西)。

我錯過了什么?

忽略糟糕的並發性,這很可能是由於筆記本電腦節流,因為它不會發生在桌面Xeon機器上。

我認為這不是微優化的一個例子,因為這三個加在一起相當於總時間的百分之一,我真的對這種行為的可能原因感興趣。

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

VTune簡介

相同的資料,雖然此次CPI略低。

那么,分析匯編代碼請注意運行時間歸因於下一條指令 - 因此,需要仔細解釋您通過指令查找的數據。 VTune發行說明”中有相應的說明

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

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

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

如果內聯模式為ON且程序在熱點處內聯函數較小,則可能導致運行時間歸因於錯誤的函數,因為下一條指令可能屬於緊密內聯代碼中的不同函數。

HW perf計數器通常將停頓充電到必須等待其輸入的指令,而不是緩慢產生輸出的指令。

第一組的輸入來自您的聚會。 這可能是緩存 - 錯過了很多,並且成本不會被這些SUBPS / MULPS / ADDPS指令收費。 它們的輸入直接來自voxel[]向量加載,因此存儲轉發失敗將導致一些延遲。 但這只是~10個周期的IIRC,與聚集期間的緩存未命中相比較小。 (那些緩存未命中您突出顯示的第一個組之前顯示為指令的大條)

第二組的輸入直接來自可能在高速緩存中丟失的負載。 在第一組中,緩存未命中加載的直接使用者是諸如設置voxel[0]行的指令,其具有非常大的條。

但在第二組中, a_transfer[]緩存未命中的時間將歸因於您突出顯示的組。 或者,如果它不是緩存未命中,那么可能是慢速地址計算,因為負載必須等待RAX准備好。


它看起來像有很多 ,你可以在這里最優化

  • 而不是為a_pointf存儲/重新加載,只需在__m128變量中的循環迭代中保持熱。 如果您發現編譯器在哪個向量寄存器溢出(如果它用完寄存器)方面做得不好,那么在C源中存儲/重新加載才有意義。

  • 使用_mm_cvttps_epi32(vf)計算vi ,因此ROUNDPS不是聚集索引的依賴關系鏈的一部分。

  • voxel通過將窄負載拖入向量來收集自己,而不是編寫復制到數組然后從中加載的代碼。 (保證存儲轉發失敗,請參閱Agner Fog的優化指南標簽wiki中的其他鏈接)。

    部分矢量化地址數學(計算base_0 ,使用帶有常量向量的PMULDQ )可能是值得的,所以不是存儲/重載(~5個周期延遲),而是只有一個MOVQ或兩個(~1或2個周期) Haswell的延遲,我忘了。)

    使用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)獲得更差的代碼。

    然后使用PMOVSX將低四個16位元素擴展為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不相等的比較后為_mm_cmpneq_ps 。請參閱double版本有關如何運作的更多表格的說明: http//www.felixcloutier.com/x86/CMPPD.html )。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM