[英]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
。
您可以使用shufpd
或vpshufd
解決您要解決的問題。 因為它需要立即數,所以浪費了代碼大小,但是顯然您沒有意識到可以使用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位模式。例如addps
: https://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
,只有palignr
和punpck
指令。 在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函數不會碰到它,否則您將不需要特別的內存並且效率也不高。
另外,所有這些movaps
和movhlps
指令都只有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.