Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/135.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 如何在两个AVX2向量之间交换128位部分_C#_C++_.net_Avx2 - Fatal编程技术网

C# 如何在两个AVX2向量之间交换128位部分

C# 如何在两个AVX2向量之间交换128位部分,c#,c++,.net,avx2,C#,C++,.net,Avx2,问题:我有4 x 256位AVX2向量(A、B、C、D),我需要对它们各自的128位部分以及两个不同向量之间执行交换操作。这是我需要做的转变 Original Transformed || Low Lane || High Lane|| || Low Lane || High Lane|| A = || L1 || H1 || = > || L1 || L2

问题:我有4 x 256位AVX2向量(A、B、C、D),我需要对它们各自的128位部分以及两个不同向量之间执行交换操作。这是我需要做的转变

             Original                      Transformed
    || Low Lane || High Lane||     || Low Lane || High Lane||
A = ||    L1    ||    H1    || = > ||    L1    ||    L2    ||
B = ||    L2    ||    H2    || = > ||    H1    ||    H2    ||
C = ||    L3    ||    H3    || = > ||    L3    ||    L4    ||
D = ||    L4    ||    H4    || = > ||    H3    ||    H4    ||

基本上,我需要按以下顺序将输出存储到数组中:L1、L2、L3、L4、H1、H2、H3、H4。

我当前的解决方案正在使用:
4x\u mm256\u blend\u epi32(最坏情况:延迟1,吞吐量0.35)
4x毫米256字节2x128毫米si256(最坏情况:延迟3,吞吐量1)


如果我理解正确的话,我想你可以不用2x4转置的混合指令,创建新的变量来选择你想要的车道。比如:

__m256i a;    // L1 H1
__m256i b;    // L2 H2
__m256i c;    // L3 H3
__m256i d;    // L4 H4

__m256i A = _mm256_permute2x128_si256(a, b, 0x20);  // L1 L2
__m256i B = _mm256_permute2x128_si256(a, b, 0x31);  // H1 H2
__m256i C = _mm256_permute2x128_si256(c, d, 0x20);  // L3 L4
__m256i D = _mm256_permute2x128_si256(c, d, 0x31);  // H3 H4
仍然有
vperm2i128
指令的3个周期延迟,但当数据跨越128位通道时,总是会有3个周期延迟。这4个洗牌是独立的,因此它们可以管道化(ILP);英特尔和Zen 2具有
vperm2i128
(,)的1/时钟吞吐量

如果幸运的话,编译器会将L1、L2和L3、L4混洗优化为
vinserti128
,AMD Zen 1运行效率更高(1个uop而不是8个;车道交叉混洗被拆分为多个128位uop)



这4个随机播放端口(Intel上的端口5)需要4个UOP;Intel和Zen2对于这些洗牌只有1/时钟洗牌吞吐量。如果这将是你循环中的一个瓶颈,考虑@ ChTZ的答案,通过做2次洗牌来提高4前端的吞吐量,以准备廉价的共混物(<代码> VPBLDEND )。相关:

您可以使用两个permutes和4个混合液进行操作,提供2个循环的绝对吞吐量:

void foo(
    __m256i a,    // L1 H1
    __m256i b,    // L2 H2
    __m256i c,    // L3 H3
    __m256i d,    // L4 H4
    __m256i* outputPtr
)
{
    // permute. Port usage: 1*p5, Latency 3 on both inputs
    __m256i BA = _mm256_permute2x128_si256(a, b, 0x21);  // H1 L2 
    __m256i DC = _mm256_permute2x128_si256(c, d, 0x21);  // H3 L4

    // blend. Port usage: 1*p015, Latency 1 on both inputs
    __m256i A = _mm256_blend_epi32(a, BA, 0xf0);  // L1 L2
    __m256i B = _mm256_blend_epi32(BA, b, 0xf0);  // H1 H2
    __m256i C = _mm256_blend_epi32(c, DC, 0xf0);  // L3 L4
    __m256i D = _mm256_blend_epi32(DC, d, 0xf0);  // H3 H4

    _mm256_store_si256(outputPtr+0, A);
    _mm256_store_si256(outputPtr+1, B);
    _mm256_store_si256(outputPtr+2, C);
    _mm256_store_si256(outputPtr+3, D);
}

但是,根据上下文(特别是如果
a
,…,
d
最初是从内存中读取的,
m128
内存操作数也最好使用and指令序列。您将有两倍的负载,但在端口5上没有层间延迟和瓶颈——关于延迟和端口使用,基于内存的
vinsert128
表现得像一个混合。

这是一个消除4x混合操作的伟大解决方案!仍在学习AVX2集..我可以使用_mm256_permute2x128_si256而不是_mm256_permute2f128_ps吗?还是有具体的原因?好的,它也适用于第一个变量…不,您可以使用整数版本。我误读了您最初的示例,没有抓住您有整数数据的事实。我会编辑。很有趣,因为我对代码矢量化非常陌生,我不熟悉“端口”这个术语,你能详细说明一下吗?我正在用broutcast从内存中读取数据<代码>uint*状态=新的uint[32]{0、1、2、3、20、21、22、23、4、5、6、7、24、25、26、27、8、9、10、11、28、29、30、31、12、13、14、15、32、33、34、35};a=Avx2.BroadcastVector128ToVector256(状态);b=Avx2.BroadcastVector128ToVector256(状态+Vector128.Count);c=Avx2.BroadcastVector128ToVector256(状态+Vector128.Count*2);d=Avx2.BroadcastVector128ToVector256(状态+Vector128.Count*3)我有理由使用“广播”,因为我同时计算2块chacha20密码,只是为了澄清。另外,如何计算绝对吞吐量
提供2个周期的绝对吞吐量
@xtremertx:我在Jason的答案中添加了3个周期延迟数的来源链接。“思想输出”仅适用于整个街区,包括周边代码;如果您仍然不会在随机端口吞吐量上出现瓶颈,而是在前端uop吞吐量上出现瓶颈(或者更糟的是,在延迟上),那么请使用Jason答案中的4指令方式,而不是这个答案中的6指令方式。e、 g.如果在循环中的这些洗牌步骤之间有大量和/或/或移位工作,可能会优化为更少的指令。@xtremertx是直接在“转置”之前发生的广播负载吗?或者两者之间是否有指示?另外,我正确理解了C#-AVX语法:在广播
a={0,1,2,3,0,1,2,3}
b={20,21,22,23,20,21,22,23}
等之后,@chtz很可能有多种解决方案如何使用AVX2对chacha20进行矢量化,但是这种讨论需要一个新的主题。我使用下面的方法更详细地解释了一些事情,在发布时,它们比chromium项目有更好的性能。我基本上是使用AVX2同时计算两个密钥流块(他们在论文中称之为双四分之一)。
__m256i a;    // L1 H1
__m256i b;    // L2 H2
__m256i c;    // L3 H3
__m256i d;    // L4 H4

__m256i A = _mm256_permute2x128_si256(a, b, 0x20);  // L1 L2
__m256i B = _mm256_permute2x128_si256(a, b, 0x31);  // H1 H2
__m256i C = _mm256_permute2x128_si256(c, d, 0x20);  // L3 L4
__m256i D = _mm256_permute2x128_si256(c, d, 0x31);  // H3 H4
void foo(
    __m256i a,    // L1 H1
    __m256i b,    // L2 H2
    __m256i c,    // L3 H3
    __m256i d,    // L4 H4
    __m256i* outputPtr
)
{
    // permute. Port usage: 1*p5, Latency 3 on both inputs
    __m256i BA = _mm256_permute2x128_si256(a, b, 0x21);  // H1 L2 
    __m256i DC = _mm256_permute2x128_si256(c, d, 0x21);  // H3 L4

    // blend. Port usage: 1*p015, Latency 1 on both inputs
    __m256i A = _mm256_blend_epi32(a, BA, 0xf0);  // L1 L2
    __m256i B = _mm256_blend_epi32(BA, b, 0xf0);  // H1 H2
    __m256i C = _mm256_blend_epi32(c, DC, 0xf0);  // L3 L4
    __m256i D = _mm256_blend_epi32(DC, d, 0xf0);  // H3 H4

    _mm256_store_si256(outputPtr+0, A);
    _mm256_store_si256(outputPtr+1, B);
    _mm256_store_si256(outputPtr+2, C);
    _mm256_store_si256(outputPtr+3, D);
}