[英]Swap halves of a NEON vector with C/gcc intrinsics: no intrinsic for VSWP?
我正在嘗試使用NEON向量指令來做相對簡單的事情:給定uint64x2_t
,我想交換64位成員的位置。
再說,如果這是一個簡單的普通代碼:
typedef struct {
U64 u[2];
} u64x2;
u64x2 swap(u64x2 in)
{
u64x2 out;
out.u[0] = in.u[1];
out.u[1] = in.u[0];
return out;
}
令人驚訝的是,我找不到它的內在因素。 顯然有一個針對它的匯編指令( VSWP
),但是沒有相應的內部指令。
真奇怪 這大約是一項微不足道的操作,因此它必須是可能的。 問題是:如何?
編輯 :供參考,使用@Jake回答godbolt
結果: https ://godbolt.org/z/ueJ6nB。 沒有vswp
,但是vext
效果很好。
沒錯,NEON內部函數不支持VSWP
指令。
但是,您可以求助於VEXT
指令,該指令也可以在內部函數中使用。
out = vextq_u64(in, in, 1);
或者,您可以使用vcombine
(並祈禱編譯器不會搞亂它):
out = vcombine_U64(vget_high_u64(in), vget_low_u64(in));
但是請注意,編譯器在看到vcombine
和/或vget
時往往會生成FUBAR機器代碼。
留在前者,這是我的建議。
表達這種混洗的另一種方法是使用GNU C本機向量內建函數,該內建函數提供了與目標無關的方式來執行給定操作。 根據目標的支持,可以將編譯時常量混洗掩碼優化為立即混洗。 但是,取決於目標ISA的支持,運行時變量改組可能效率很低。
#include <arm_neon.h>
#ifndef __clang__
uint64x2_t swap_GNU_shuffle(uint64x2_t in)
{
uint64x2_t mask = {1,0};
uint64x2_t out = __builtin_shuffle (in, mask);
return out;
}
#endif
Godbolt上的AArch64 gcc8.2實際上可以編譯為Jake建議的相同改組 ,而不是SWP:
swap_GNU_shuffle:
ext v0.16b, v0.16b, v0.16b, #8
ret
Clang還優化了我們大多數對ext
指令的pure-C嘗試,包括使用memcpy
對您的普通結構進行類型雙關(punch)並返回的指令。 與GCC不同,后者沒有那么好的混洗優化器。 (在Godbolt上,將下拉菜單中的任何clang
與-O3 -target arm64
一起使用-O3 -target arm64
通常是在默認情況下構建的,支持多個目標ISA,這與GCC不同。)
因此,無論這些編譯器已經錯過了調整=通用和優化-mcpu=cortex-a53
, a57
和a75
,否則ext
實際上是一個不錯的選擇,也許不比swp
其中有寫2個輸出寄存器,而不是邏輯寫作一個全角寄存器。 但是通常這對ARM來說不是問題。 大量的說明可以做到這一點,並且通常可以提高效率。
ARM的Cortex-A8時序信息對 vext
和vswp
具有相同的編號(從Qn
到Q
輸出均為1個周期的延遲,而從Qm
到Q
輸出均為2個周期)。 我沒有檢查過較新的內核(或任何64位內核)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.