Optimization 使用SIMD-SSE/AVX查找素数列表

Optimization 使用SIMD-SSE/AVX查找素数列表,optimization,primes,sse,simd,avx,Optimization,Primes,Sse,Simd,Avx,我很好奇是否有人对如何使用SIMD查找素数列表提出了建议。我特别感兴趣的是如何使用SSE/AVX实现这一点 我一直在研究的两种算法是试除法和埃拉托什尼筛法。 我已经设法找到了一种方法,将SSE与审判司一起使用。我发现了一种更快的除法,它适用于向量/标量“使用乘法除以不变整数” 每次我找到一个素数,我就形成结果,进行快速除法并保存它们。下一次我做除法的时候,速度要快得多。这样做,我得到了大约3倍的加速比(4倍中)。使用AVX2可能也会更快 然而,试验划分比Eratosthenes筛慢得多,我想不出

我很好奇是否有人对如何使用SIMD查找素数列表提出了建议。我特别感兴趣的是如何使用SSE/AVX实现这一点

我一直在研究的两种算法是试除法和埃拉托什尼筛法。 我已经设法找到了一种方法,将SSE与审判司一起使用。我发现了一种更快的除法,它适用于向量/标量“使用乘法除以不变整数” 每次我找到一个素数,我就形成结果,进行快速除法并保存它们。下一次我做除法的时候,速度要快得多。这样做,我得到了大约3倍的加速比(4倍中)。使用AVX2可能也会更快

然而,试验划分比Eratosthenes筛慢得多,我想不出任何方法将SIMD与Eratosthenes筛一起使用,除非使用某种分散指令,甚至AVX2都还没有。分散式教学有帮助吗?有人知道GPU上正在使用这种方法筛选埃拉托斯烯吗

这是我所知道的使用OpenMP的Eratosthenes筛的最快版本。有没有办法通过SSE/AVX来加速这一进程?

这是我用来确定一个数是否为素数的函数。我一次操作八个素数(在没有AVX2的情况下,实际上一次操作四个)。我用的是Agner Fog的矢量类。其思想是,对于较大的值,不太可能有八个素数按顺序排列。如果我在八个素数中找到一个素数,我必须按顺序循环计算结果

inline int is_prime_vec8(Vec8ui num, Divisor_ui_s *buffer, int size) {
    Divisor_ui div = Divisor_ui(buffer[0].m, buffer[0].s1, buffer[0].s2);
    int val = buffer[0].d; 
    Vec8ui cmp = -1;

    for(int i=0; (val*val)<=num[7]; i++) {
        Divisor_ui div = Divisor_ui(buffer[i].m, buffer[i].s1, buffer[i].s2);
        val = buffer[i].d;
        Vec8ui q = num/div; 
        cmp &= (q*val != num);
        int cnt = _mm_movemask_epi8(cmp.get_low()) || _mm_movemask_epi8(cmp.get_high());
        if(cnt == 0) {
            return size;  //0 primes were found
        }
    }
    num &= cmp;  //at least 1 out of 8 values were found to be prime
    int tmp[8];
    num.store(tmp);

    for(int i=0; i<8; i++) {
        if(tmp[i]) {
            set_ui(tmp[i], &buffer[size++]);
        }
    }       
    return size;
}
inline int是_prime_vec8(Vec8ui num,除数_ui_s*缓冲区,int size){
除数_uidiv=除数_ui(缓冲区[0].m,缓冲区[0].s1,缓冲区[0].s2);
int val=buffer[0].d;
Vec8ui-cmp=-1;

对于(int i=0;(val*val)顺便说一句,Granlund在2011年的论文中改进了除法(特别是对于现代处理器)对于试除法,你可以很快地进行试除法。仅前54个小于256的素数就可以消除约80%的奇数候选者,并为任何小于65536的候选者提供正确的结果。我在一年前进行了这项试验。我发现,接近埃拉托什尼筛的阻塞法速度最快,但它也与其他方法不兼容h SIMD。@Mysticial,某种分散指令对Eatosthenes的筛选没有帮助吗?Xeon Phi和许多GPU都有分散选项。我提到的链接对筛选使用了一种阻塞方法,这带来了很大的改进。@BrettHale,感谢链接!我查看了你的个人资料。你是一个优秀的人,也许你可以写一个答案。我已经跳过了2,3,5的倍数(来自轮子分解)。删除了超过70%的合成。您的意思是使用SIMD和fast除法,使用前54个素数,然后在更小的子列表上进行标量除法,生成一个素数候选子列表吗?@raxman-实际上,我在考虑一次测试4个候选。问题是,它们会在不同的时间退出。我考虑了轮子分解,但Miscial对SIMD的所有事情都是正确的。我不知道如何让管道在所有4或8个元素上都做有用的工作。这还取决于您试图实现的目标:生成小于N的所有素数?您将使用多少内存?可以接受多少预计算?等等。
int find_primes_vec8(Divisor_ui_s *buffer, const int nmax) {
    int start[] = {2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47};
    int size = sizeof(start)/4;

    for(int i=0; i<size; i++) {
        set_ui(start[i], &buffer[i]);
    }

    Vec8ui iv(49, 53, 59, 61, 67, 71, 73, 77);
    size-=3;
    for(int i=49; i<nmax; i+=30) {
        if((i-1)%100000==0) printf("i %d, %f %%\n", i, 100.f*i/(nmax/16));
        size = is_prime_vec8(iv, &buffer[3], size);
        iv += 30;
    }   
    size+=3;

    return size;
}