簡體   English   中英

將浮動從高xmm四字移動到低xmm四字

[英]Move float from high xmm quadword to low xmm quadword

MOVHPD將xmm寄存器的高位四字提取到內存中。

PEXTRQ提取xmm寄存器的高位四字並將其放入整數寄存器(僅整數)。

SHUFPD隨機播放。

VPSLLDQ使高位四字清零。

是否有指令將浮點值從xmm寄存器的高位四字移動到同一xmm寄存器或另一個xmm寄存器的低位四字中? 還是我總是必須經過內存(添加額外的周期)?

更新:根據以下@fuz和@Peter Cordes的評論,這是我所做的。 這將分別為xmm0的上下四位數調用舍入函數; 由於特殊的舍入參數,必須為每個qword分別調用該函數,因此它不能是SIMD指令。 目標是將xmm0中的每個qword取整並將結果放入xmm11中。

movapd xmm2,xmm0 ;preserve both qwords of xmm0
call Round
movsd [scratch_register+0],xmm0 ; write low qword to memory
movhlps xmm0,xmm2
call Round
movsd [scratch_register+8],xmm0 ; write low qword to memory
movupd xmm11,[scratch_register]

更新#2:@Peter Cordes顯示了如何在沒有內存的情況下執行此操作:

movhlps  xmm2, xmm0   ; extract high qword for later
call Round            ; round the low qword
movaps   xmm3, xmm0   ; save the result
movaps   xmm0, xmm2   ; set up the arg
call Round            ; round the high qword
movlhps  xmm3, xmm0   ; re-combine into xmm3

請參閱Agner Fog的asm優化指南 ,他在SIMD上的章節中有一張隨機播放指令表,列出了不同類型的數據移動方式,這些指令可以讓您考慮一些指令(如果您不記得它們的確切內容,請查閱Intel手冊) ),看看它們是否就是您想要的。


向兩個元素廣播寄存器的高qword的最便宜方法是movhlps xmm0,xmm0 (或者對於整數數據(如果您的代碼可以在Nehalem上運行,請使用punpckhqdq xmm0,xmm0以避免FP <-> vec-int旁路延遲)。)

沒有AVX, movhlps很不錯,因為它的隨機播放與unpckhpd略有不同。

  • movhlps xmm3, xmm4是否xmm3[0] = xmm4[1]; ,使xmm3[1]保持不變。
  • unpckhpd xmm3, xmm4從xmm3和xmm4提取高位unpckhpd xmm3, xmm4並將其按順序放入xmm3中。 因此,在目標中,高qword移到低,然后將src中的高qword復制過來。 xmm3[0] = xmm3[1]; xmm3[1] = xmm4[1]

但是unpcklpd沒用,它長了1個字節,並且與SSE1 movlhps (將src中的低位qword復制到目標的高位qword,而保留目標的低位qword movapd 。)與movapd相同,請始終使用movaps

同樣是re:code-size:使用xmm8..15需要花費REX前綴,因此請選擇寄存器分配以在盡可能少的指令中使用xmm8..15(或已經需要REX前綴的指令,例如用於指針)在r8..15中)。 代碼大小通常並不重要,但是其他所有條件通常都較小。 較小的指令通常可以更好地打包到uop緩存中。


使用AVX,您可以將vunpckhpd與源操作數的任意順序一起使用 ,並且第一個src的高qword會到達目標的低qword。 vmovhlps沒有代碼大小優勢(或其他性能優勢),它們都可以使用2字節的VEX前綴來實現最小4字節的指令大小。

例如vunpckhpd xmm0, xmm1, xmm0就像vmovhlps xmm0, xmm0,xmm1


您可以使用shufpdvpshufd解決您要解決的問題。 因為它需要立即數,所以浪費了代碼大小,但是顯然您沒有意識到可以使用shufpd xmm0, xmm0, 0b11來獲取(按此順序):

  • xmm0[1]的低位qword(第一個src操作數,立即數的低位)
  • xmm0[1]的高位qword(第二個src操作數,立即數的高位)。

隨機播放控件可以多次讀取同一輸入元素。


有趣的是,NASM編譯器將僅使用兩個操作數來編譯VUNPCKHPD

NASM允許您將vaddps xmm0, xmm0, xmm1等指令編寫為vaddps xmm0, xmm1 ,並在與第一個源相同時省略單獨的目標操作數。

我很困惑,因為這些值是雙精度的,而不是單精度的,但是它可以工作。

一切都只是要復制的位/字節 除非您使用FP計算指令(例如addpd / addps ), addps “類型”無關緊要。 (您可以通過手冊條目中是否存在“ SIMD浮點異常”部分來判斷是否將位的含義視為FP位模式。例如addpshttps://www.felixcloutier .com / x86 / addps#simd-floating-point-exceptions 。(但沒有任何意外。唯一關心的指令是出於非常明顯的原因這樣做的,例如進行FP計算或類型轉換,而不僅僅是在周圍復制數據)

沒有真正的CPU關心PS與PD的性能指令,而是關心vec-int與vec-FP的指令,因此不幸的是,使用pshufd復制和pshufd FP數據並不總是勝利。 或將shufps用作2源整數隨機播放。

不幸的是,在AVX512之前,沒有通用的2源“整數” palignr punpck ,只有palignrpunpck指令。 在AVX之前,沒有FP復制和改組說明。 (諷刺的是, vpermilps用速是冗余對vshufps dst, same,same, imm8除了一個存儲器源負載+洗牌,並且應避免代碼大小的原因。 什么是VPERMILPS指令的點(_mm_permute_ps) ?


  movapd xmm2,xmm0 ;preserve both qwords of xmm0
  call Round
     movsd [scratch_register+0],xmm0 ; write low qword to memory
  movhlps xmm0,xmm2
  call Round

這是有效的改組,但是不幸的是,它在第一個Round的輸出和第二個Round的輸入之間創建了錯誤的依賴關系 因此,這兩個調用不能並行運行。 相反,在第一次調用之前復制時應隨機播放,最好是進入已知已經“死”一段時間或屬於xmm0中的值的依賴鏈的一部分的寄存器中,因此必須在此之前做好准備。

  movhlps  xmm2, xmm0   ; extract high qword for later
  call Round                ; round the low qword
  movaps   xmm3, xmm0   ; save the result
  movaps   xmm0, xmm2   ; set up the arg
  call Round                ; round the high qword
  movlhps  xmm3, xmm0    ; re-combine into xmm3

除非您的筆跡不足,而您的手寫Round函數不會碰到它,否則您將不需要特別的內存並且效率也不高。

另外,所有這些movapsmovhlps指令都只有3個字節長,並且它們的數量與您的版本中的指令數量相同。

另一種選擇(尤其是如果你的輸入是在一個不同的寄存器開始)將Round第一高的一半,那么你可以把高半回XMM0與movlhps

而且,順便說一句,如果您具有SSE4.1, roundpd可以四舍五入為具有Nearest的最接近整數,朝+ -Inf(上限/下限)或接近0(截斷)。


movsd [scratch_register+8],xmm0 ; write low qword to memory
movupd xmm11,[scratch_register]

永遠不要這樣做,狹窄的倉庫+寬的裝貨量是保證的倉庫轉發停滯期。 (約10個周期的額外延遲)。

使用16字節對齊的存儲位置(例如,在堆棧上[rsp+8]或其他位置),然后
unpckhpd xmm0, [scratch_register]加載+隨機播放

不幸的是,英特爾對內存源unpck指令的設計很差,因此它們需要16字節的內存源,而不僅僅是它們實際加載/使用的8字節。 在幾種情況下

暫無
暫無

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

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