在C#中,将数组元素相乘具有意想不到的性能
我想找到将两个数组元素乘以乘以的最佳方法.这是一个更广泛的项目的一部分,而不是唯一的考虑. 我今天开始在C#(LINQPAD)中编写一些功能,因此尚未以任何方式进行优化.以下代码中的输出如下: Environment.ProcessorCount: 4 Vector.Count: 4 For sequential: 129ms, sum: 2.30619276241231E+25 Plinq: 344ms, sum: 2.30619276241231E+25 Parallel.For: 137ms, 2.30619276241231E+25 Simd sequential: 100ms, sum: 2.30619276241231E+25 Simd parallel: 761ms 这是由乘法的执行时间和结果作为检查的总和.这里有一些奇怪的结果(我在C#中有点生锈,所以很可能是我的代码): 常规的速度比平行更快 plinq相对于其他人非常慢 - 我
14 2024-04-23
编程技术问答社区
矢量化没有提供预期的速度
我在.NET 4.7.2上玩得开心.在第一次尝试时,我编码了一个基本功能,以识别ASCII字符串中是否有空格.我实现了该函数的三个版本: linq, 经典循环和 矢量化版本(simd). 我很惊讶地看到矢量化版本明显慢于循环的经典. BenchmarkDotNet=v0.12.1, OS=Windows 10 Intel Core i7-8650U CPU 1.90GHz (Kaby Lake R), 1 CPU, 8 logical and 4 physical cores [Host] : .NET Framework 4.8 (4.8.4180.0), X64 RyuJIT Job-AWDTWU : .NET Framework 4.8 (4.8.4180.0), X64 RyuJIT Jit=RyuJit Platform=X64 Runtime=.NET 4.7.2 | Method | M
20 2024-04-23
编程技术问答社区
所有类型的矢量范围随机数生成
我想支持C ++中的以下操作: void generate_random_simd(T* array, T upper_bound, T lower_bound) { // uses simd instructions for rng in range [lower_bound, upper_bound] } T型可以是任何UINT,INT或Float Type -32或64位.是否有直接可用的有效实施或有关此材料的一些文献? 我确实找到了一些实现,例如 解决方案 元素边界仅在您具有下限/上限时至关重要. 否则,对于整数,您只需要在SIMD向量中的128或256位随机数据​​. 例如,您可以使用SSE2/AVX2 XORSHIFT+,该+在64位SIMD元素中运行多个Xorshift+ Generator.您可以将其视为16x uint8_t或2x uint64_t,或者当您实际想实际使用时使用时的任何东西. . 这是将其用作16位元素的一个示例 - >
22 2024-04-22
编程技术问答社区
使用 SSE 的乘法 (x*x*x)+(y*y*y)
我正在尝试使用SIMD来优化此功能,但我不知道从哪里开始. long sum(int x,int y) { return x*x*x+y*y*y; } 拆卸功能看起来像这样: 4007a0: 48 89 f2 mov %rsi,%rdx 4007a3: 48 89 f8 mov %rdi,%rax 4007a6: 48 0f af d6 imul %rsi,%rdx 4007aa: 48 0f af c7 imul %rdi,%rax 4007ae: 48 0f af d6 imul %rsi,%rdx 4007b2: 48 0f af c7 imul %rdi,%rax 4007b6: 48 8d 04 02
36 2024-04-22
编程技术问答社区
将 __m128i 中的每个 DW 右移不同的量
我想以其他数量向右移动__m128i寄存器的每个元素. __m128i mul_constant = _mm_set_epi32(8, 4, 2, 1); __m128i left_vshift = _mm_mullo_epi32(R, mul_constant); ,但是,如果我们想正确地移动它的解决方案是什么? 解决方案 我终于做到了以下内容: 将每个字节转移到左侧的不同数量,然后将32位的右移动换成3,这给了我我想要的. R = _mm_mullo_epi32(R, _mm_set_epi32(1, 2, 4, 8)); R = _mm_srli_epi32(R, 3);
26 2024-04-22
编程技术问答社区
如何使用 AVX2 和 SSE 将较大的整数转换为较小的整数?
使用AVX2和SSE? 例如: int16-> int8 int32-> int16/int32-> int8 int64-> int32/int64-> int16/int64-> int8 我知道AVX-512有说明: VPMOVQB vpmovwb 对应于: 的内在物质 _MM512_CVTEPI16_EPI8(AVX512字节和Word ISA) _MM512_CVTEPI32_EPI8(AVX512基金会) _MM512_CVTEPI32_EPI16(AVX512基金会) _MM512_CVTEPI64_EPI8(AVX512基金会) _MM512_CVTEPI64_EPI16(AVX512基金会) _MM512_CVTEPI64_EPI32(AVX512基金会) 哪种操作整数类型缩小转换率,但是如何在没有这样的说明的AVX2和SSE中完成此操作? 请注意,虽然上述AVX512内在的128和256位过载,但它们仍需要在运
12 2024-04-22
编程技术问答社区
以可移植方式访问 __m128i 变量的字段
我正在尝试使用SIMD指令来加快UINT8_T数组(即减少总和)中的元素之和.为此,我在这个问题中复制了最投票的答案: sum使用Intel上的SSE2降低无溢出的无符号字节 该答案中显示的总和减少的过程是: uint16_t sum_32(const uint8_t a[32]) { __m128i zero = _mm_xor_si128(zero, zero); __m128i sum0 = _mm_sad_epu8( zero, _mm_load_si128(reinterpret_cast(a))); __m128i sum1 = _mm_sad_epu8( zero, _mm_load_si128(reinterp
20 2024-04-22
编程技术问答社区
谁能解释一下这个 SSE BigNum 比较?
如果您查看此答案,作者设法为2个整数Bignums创建了一个紧凑的比较算法,存储在2个SSE寄存器中.我不太关注它:) 我到目前为止所做的: 如果l = a b1) 在我的问题中,我的目标是用SSE/AVX寄存器实施任意长的Bignums.
22 2024-04-22
编程技术问答社区
如何将矢量中的数值相加
在我的代码中,我解决了积分 y=x^2-4x+6 我使用了SSE-它允许我一次对4个值进行操作.我制作了程序,该程序以0到5的值求解该积分,分别为五个4元素向量n1,n2,n3,n4. .data n1: .float 0.3125,0.625,0.9375,1.25 n2: .float 1.5625,1.875,2.1875,2.5 n3: .float 2.8125,3.12500,3.4375,3.75 n4: .float 4.0625,4.37500,4.6875,5 szostka: .float 6,6,6,6 czworka: .float 4,4,4,4 .text .global main main: movups (n1),%xmm0 mulps %xmm0,%xmm0 movups (szostka),%xmm2 addps %xmm2,%xmm0 movups
10 2024-04-22
编程技术问答社区
在 XMM 寄存器中取消引用指针(收集)
如果我有一些指针或指针式值包装到SSE或AVX寄存器中,是否有任何特别有效的方法将它们放在另一个这样的寄存器中? ("特别有效"的意思是"比仅将内存用于值更有效". 编辑以进行澄清:这意味着,假设有32位指针和SSE,将XMM寄存器的四个部分一次索引到四个任意内存区域,并立即将四个结果返回到另一个寄存器.或尽可能接近"一次". (/编辑) edit2:感谢Paulr的答案,我猜我要寻找的术语是"收集",因此问题是"实施系统pre-avx2的最佳方法是什么?". 我认为没有任何指示,因为...嗯,我似乎并不存在,无论如何,似乎根本不是SSE的设计. ("像指针般的价值",意思是一个像整数索引成一个阵列,假装是堆;在机械上非常不同,但在概念上是相同的东西.如果有人想使用32位甚至16位值不管本机指针大小如何,以适合寄存器中的更多值.) 我可以想到为什么有人想这样做的两个可能的原因: 认为,使用SSE寄存器进行通用...东西探索可能很有趣,也许可以使用四个相同的"线
20 2024-04-22
编程技术问答社区
可以将字符串转换为 simd_float4x4 吗?
是否可以从字符串中构造simd_float4x4 解决方案 这是extension simd_float4x4添加失败 init的simd_float4x4,它进行了调试描述并创建simd_float4x4.这是失败 init,因为字符串可能会成立. import simd extension simd_float4x4 { init?(_ string: String) { let prefix = "simd_float4x4" guard string.hasPrefix(prefix) else { return nil } let csv = string.dropFirst(prefix.count).components(separatedBy: ",") let filtered = csv.map { $0.filter { Array("-01234567890.").contain
18 2024-04-22
编程技术问答社区
使用 SIMD 查找字节数组中未对齐 int 或 long 的索引
我有一个字节序列,我想扫描以找到整数(或long)值的索引.它可以处于任何字节偏移量,不一定是大小的倍数.具体来说,我对第一次事件感兴趣,但所有索引的示例也将有所帮助. 如果不可能,我想我需要将长时间转换为8个字节长度,而不是比较两个. 平台为x86.我可以约束应用程序仅运行x64模式. 我需要最快的方法,以便代码段很棒. 我知道这是一个简单的问题,但找不到一个例子(至少在C#中). 解决方案 阅读此 static void Main(string[] args) { var input = "12345671asdasdasd1asdasdasd2asdasdasd3asdasdasd_12345678asdasdasd1asdasdasd2asdasdasd3asdasdasd_"u8; var needle = BitConverter.ToInt64("12345678"u8); var ix = IndexOf(input, needle); } pub
10 2024-04-22
编程技术问答社区
使用 SIMD 移位/旋转字节矢量的最快方法
我有一个AVX2(256位)字节的Simd向量,该向量在前面和后部的零填充,看起来像这样: [0, 2, 3, ..., 4, 5, 0, 0, 0]. 前面的零数量尚不清楚编译时间. 我将如何有效地移动/旋转零,以使其看起来像这样: [2, 3, 4, 5, ..., 0, 0, 0, 0]? 解决方案 avx2无法进行小于4个字节的粒度的车道横断.在这种情况下,您需要AVX-512 VBMI vpermb(在Ice Lake).如果有的话,也许vpcmpeqb/vpmovmskb/tzcnt在掩码上,并将其用作偏移量来从alignas(64) int8_t shuffles = {0,1,2,...,31, 0, 1, 2, ... 31};的常数数组中加载32个字节的窗口.那就是vpermb的shuffle-control矢量. 如果没有AVX-512 VBMI,尽管商店有良好的摊位,但可能会 还是有意义地存放两次并进行了一个不和谐的重新安装.如果您需要在其他许
12 2024-04-22
编程技术问答社区
如何使用 simd 将 uint32 转换为 uint8,但不使用 avx512?
说,在对齐内存uint32 *p中有很多UINT32S商店,如何使用SIMD转换为UINT8? 我看到有_mm256_cvtepi32_epi8/vpmovdb,但它属于avx512,而我的CPU不支持它😢 解决方案 如果您真的有很多,我会做这样的事情(未经测试). 主循环读取包含16个UINT32_T值的64个字节,围绕实现截断的字节围绕的字节散装,将结果合并为单个寄存器,并使用Vector Store Orcession撰写16个字节. . void convertToBytes( const uint32_t* source, uint8_t* dest, size_t count ) { // 4 bytes of the shuffle mask to fetch bytes 0, 4, 8 and 12 from a 16-bytes source vector constexpr int shuffleScalar = 0x0C
16 2024-04-22
编程技术问答社区
AVX-512 `_mm512_load_epi64`和 `_mm512_loadu_epi64`之间是否存在性能差异?
这个问题的动机 未对准的负载通常更常用.当地址已经对齐时,开发人员应使用对齐的SIMD负载.因此,我开始怀疑这两个函数在已经对齐的地址上是否有一些性能差异.直观的猜测是,对准负载比未对齐的负载快. 我确实知道这个问题可能非常依赖硬件.另一个动机是ZEN4是第一个AMD微结构提供AVX-512,因此我想在Zen4上尝试一些AVX-512并查看结果. 基准代码和汇编 代码: https://godbolt.org/z/w3qvcjgws i基准有两种情况: 第一种情况:我确保要访问的内存data的大小小于L1缓存.所以我没有缓存的错过.因此不绑定内存. 第二种情况:访问的内存比缓存更大. 汇编中函数调用之间的唯一区别:vmovdqa64和vmovdqu64. 结果 我的实验是在AMD Zen4上进行的.我对函数进行了基准测试.结果是一致的,事实证明,这两个函数调用是相同的.这违背了我的直觉.如果是真的,则没有实际对齐负载的用法,该情况的情况最小,
32 2024-04-22
编程技术问答社区
在 AArch64 SIMD 或 ARM NEON 中将矢量比较掩码转换为位掩码?
以" ABAA"为例.我可以使用result = vceqq_u8(input, vdupq_n_u8('A'))获得FF 00 FF FF(或0xffff00ff). 有时我只需要知道第一场比赛,而有时我想知道所有比赛.从结果寄存器中,有一种方法可以得到a)第一匹配的索引吗?在这种情况下,哪个是0,因为它以'a'(低字节为ff)b)获得二进制1101? (只有第二个字母不匹配,所以第二位是0) 在AVX2上,我使用MoveMask来获取位和TZCNT以获取索引.我似乎找不到霓虹灯上的MoveMask 解决方案 霓虹灯可以迅速缩小128位比较字节蒙版至64位, 使用"右移和窄(SHRN)"或"使用签名饱和(VQMOVN/SQXTN)的包装". 这允许将蒙版提取到__aarch64__上的64位通用寄存器. . 提取后,可以检查蒙版的全二方或全何(-1). 可以使用__builtin_ctzll(m)(rbit/clz)找到第一匹匹配. 可以通过清除任何恢复钻头然后
16 2024-04-22
编程技术问答社区
对于非偶数非对齐数组,循环 AVX 的最佳方法是什么?
如果数组不能除以8(对于整数),那么为其编写周期的最佳方法是什么?到目前为止,我可能想出的可能的方法是将其分为两个单独的周期:几乎所有元素的1个主周期;和1个带有遮罩/蒙版的尾循环,用于剩余的1-7个元素.但这看起来不是最好的方法. for (auto i = 0; i
26 2024-04-22
编程技术问答社区
在矩阵-向量乘法中使用 OpenMP "for simd"?
我目前正在尝试通过将#pragma omp for与#pragma omp simd组合在一起,以使我的矩阵矢量乘法函数与BLA相比,但与仅使用for for for for for for for for for for for for,但没有得到任何加速改进.如何使用OpenMP的SIMD构造正确矢量化内部循环? vector dot(const matrix& A, const vector& x) { assert(A.shape(1) == x.size()); vector y = xt::zeros({A.shape(0)}); int i, j; #pragma omp parallel shared(A, x, y) private(i, j) { #pragma omp for // schedule(static) for (i = 0; i
24 2024-04-22
编程技术问答社区
如何获取英特尔架构 SIMD __m128 的符号?
因为" _mm_sign_ps"在我所能找到的情况下不存在:给定一个带有四个浮点值的__m128值,SIMD指令或SIMD指令的列表将其转换为__-M128值,带有四个Floating-包含的点值: +1,如果四个的原始值是正的,大于零. 0,如果四个的原始值为零. -1,如果四个的原始值为负,小于零. 解决方案 SSE确实根本不太匹配.首先,比较函数不会导致±1.0F,而是如果条件为真,则设置所有位,或者如果条件为false,则没有设置它们.另外,如果值为"零",则您要进行三路比较,而结果为"零"("零"为"零",因为您实际上没有指定您是要零还是负零; ieee 754两者都有).如果您可以重新构架问题以更好地匹配SSE提供的内容,那么您将成为很多. 也就是说: __m128 foo (__m128 value) { const __m128 zero = _mm_set_ps1 (0.0f); __m128 positives = _mm_and_
26 2024-04-22
编程技术问答社区