我正在使用的算法花费了大部分时间来比较一个数组和一行矩阵。如果任何 ith 元素相同,则算法调用过程A,如果没有元素相等,则调用过程B.例如:
[1, 4, 10, 3, 5]
和[5, 3, 0, 3, 0]
调用A()
,因为对于第4个位置,两个数组中的值都是3。
[1, 4, 10, 3, 5]
和[5, 3, 0, 1, 0]
调用B()
因为对于相同的位置,值永远不会相同。
请注意:(1)数组和矩阵行的大小N始终相同,(2)当至少一个值匹配时,算法会调用A()
。
在C中执行此操作的最简单但非常天真的方法是:
for(int i=0; i<N; i++)
if( A[i] == B[i] ){
flag = 1;
break;
}
这仍然非常低效。在最坏的情况下,我将进行N次比较。这里真正的问题是该算法可以进行数万亿次比较。
N(矩阵中数组/行的大小)从100到1000不等。我想加快这个程序。我看了矢量化,发现我可以使用cmpeq_pd
。但是,矢量化仍然有限,因为我的所有参赛作品 longs
。有没有人有想法?我可以申请面具等吗?
更多信息/背景:
答案 0 :(得分:3)
我不知道这是否适用于您正在开发的应用程序,但是大型阵列上的操作通常在GPU上加速。您可以期望比CPU高10-20倍的吞吐量。如果您的应用可能会在CUDA上运行会产生巨大差异的关键部分。
答案 1 :(得分:2)
虽然您的Sandy Bridge CPU只有AVX用于256位SIMD(而不是AVX2),因此缺乏对4路SIMD 64位整数操作的支持,我认为您仍然可以使用AVX浮点指令实现4路SIMD,如如下:比较64位整数值的2 x 256位向量,v1
,v2
:
__m256d vcmp = _mm256_xor_pd(v1, v2); // use XOR rather than compare, so we are not
// affected by values which map to NaNs
vcmp = _mm256_cmp_pd(vcmp, _mm256_setzero_pd(), _CMP_EQ_OQ);
// now we can do a valid comparison as if the
// data really is double precision float
int mask = _mm256_movemask_pd(vcmp); // extract the sign bits
bool any_eq = (mask != 0); // if any elements matched then mask
// will be non-zero
以下是用于测试和说明目的的示例程序:
#include <stdio.h>
#include <stdint.h>
#include <immintrin.h>
int test(__m256d v1, __m256d v2)
{
__m256d vcmp = _mm256_xor_pd(v1, v2);
vcmp = _mm256_cmp_pd(vcmp, _mm256_setzero_pd(), _CMP_EQ_OQ);
return _mm256_movemask_pd(vcmp);
}
int main()
{
int64_t a1[4] = { 3098, 3860, 405, 3308 };
int64_t a2[4] = { 1930, 1274, 2195, 2939 };
int64_t a3[4] = { 1930, 1274, 405, 2939 };
__m256i v1 = _mm256_loadu_pd((double *)a1);
__m256i v2 = _mm256_loadu_pd((double *)a2);
__m256i v3 = _mm256_loadu_pd((double *)a3);
printf("mask = %d (should be == 0)\n", test(v1, v2));
printf("mask = %d (should be != 0)\n", test(v1, v3));
return 0;
}
测试:
$ gcc -Wall -mavx a3mlord2.c && ./a.out
mask = 0 (should be == 0)
mask = 4 (should be != 0)
答案 2 :(得分:1)
每当你寻找优化时,你面前都有不同的路径:
根据您所说的,唯一可能的优化是并行处理n个核心,每个核心(减1)执行行的一部分,另一个处理第一次比较的结果。但如前所述,如果数据中存在规则,则收益可能会高得多。
答案 3 :(得分:1)
在C中执行此操作的最简单但非常天真的方法是
正如您所暗示的那样,从可读性的角度来看,您在此声明下提供的代码示例可能很简单,但它是否会转换为编译后数据比较最简单,最有效的方法?
建议尝试块比较:
呈现数据以进行比较的方式可以有助于速度和效率的比较。将值加载到单独的变量中(分配给使用单独的寄存器),然后比较寄存器。
long a1 = A[0];
long a2 = A[1];
long a3 = A[2];
long a4 = A[3];
...
long an = A[n];
long b1 = B[0];
long b2 = B[1];
long b3 = B[2];
long b4 = B[3];
...
long bn = B[n];
if ((a1 == b1) || (a2 == b2) || (a3 == b3) || (a4 == b4) ... || (an == bn))
{
//do something
}
else
{
//do something else
}
要真正了解方法是否最快,请对其进行编码,查看它生成的程序集,或对其进行基准测试。正如您在帖子中建议的那样,循环数组元素可能不是最有效的方法。
编辑 :一个偏见:Matlab以包含一些最快的数组比较例程而着称,和它也有一个Matlab来C转换能力。如果您或同事拥有Matlab副本,您可以尝试对使用Matlab创建的算法进行一些速度测试,然后转换为C以观察它创建的内容。我之前使用过这个功能,它产生的C构造并不是很好看,但通常非常有效(就速度而言)。
答案 4 :(得分:0)
SIMD处理根本不可能有所帮助:你有一个相当小的循环接触大量数据(每次迭代16个字节)。即使在没有SIMD的情况下运行,这也可能会使内存总线饱和。
在我看来,你有两个基本选择:
您使用更多/更宽的内存总线 这可以通过使用多个内核或GPU来实现。
您尝试减少比较次数 从您的问题中可以看出是否可能,但如果您的算法多次进行相同的比较,则可以通过缓存比较结果来重构算法。根据算法,可以显着提高速度。
答案 5 :(得分:-3)
如果您正在使用gcc,并且如果您使用的是x86平台,那么您的代码可能会受益于使用memcmp()
代替&#34;自行开发的&#34; for
循环。 memcmp()
(分别是它的内置对手)做了一些非常聪明的优化。