Поиск списков простых чисел с помощью SIMD — SSE/AVX

Мне любопытно, есть ли у кого-нибудь советы о том, как использовать SIMD для поиска списков простых чисел. Особенно меня интересует, как это сделать с помощью SSE/AVX.

Я рассматривал два алгоритма: пробное деление и решето Эратосфена. Мне удалось найти способ использовать SSE с пробным разделением. Я нашел более быстрый способ деления, который хорошо работает для вектора/скаляра "Деление на инвариантные целые числа с использованием умножения"http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.1.2556 Каждый раз, когда я нахожу простое число, я формирую результаты, чтобы быстро разделение и спасти их. Тогда в следующий раз, когда я выполняю деление, все пойдет намного быстрее. Делая это, я получаю ускорение примерно в 3 раза (из 4 раз). Возможно, с AVX2 это будет быстрее.

Однако пробное деление намного медленнее, чем решето Эратосфена, и я не могу придумать никакого способа использовать SIMD с решетом Эратосфена, кроме как с какой-то инструкцией разброса, которой еще нет даже в AVX2. Поможет ли скаттер-инструкция? Кто-нибудь знает, что это используется на графических процессорах для Решета Эратосфена?

Вот самая быстрая версия Решета Эратосфена с использованием OpenMP, о которой я знаю. Есть идеи, как ускорить это с помощью SSE/AVX? http://create.stephan-brumme.com/eratosthenes/

Вот функция, которую я использую, чтобы определить, является ли число простым. Я работаю сразу с восемью простыми числами (на самом деле это будет 4 за один раз, дважды без AVX2). Я использую векторный класс Агнера Фога. Идея состоит в том, что для больших значений маловероятно, что в последовательности будет восемь простых чисел. Если я найду простое число среди восьми, мне придется перебирать результаты последовательно.

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;
}

Вот где я настраиваюсь на восемь основных кандидатов. При этом я пропускаю числа, кратные 2, 3 и 5.

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;
}

person Community    schedule 21.06.2013    source источник
comment
Кстати, Гранлунд улучшил метод деления (особенно для современных процессоров) в этой статье 2011 года. . Если бы вы предварительно вычислили обратные величины (после нормализации) для пробного деления, вы могли бы выполнить пробное «деление» очень быстро. Только первые 54 простых числа ‹ 256 исключат ~ 80% всех нечетных кандидатов и дадут правильный результат для любого кандидата ‹ 65536.   -  person Brett Hale    schedule 21.06.2013
comment
Я экспериментировал с этим год назад. Я обнаружил, что заблокированный подход к решету Эратосфена был самым быстрым, но он также несовместим с SIMD.   -  person Mysticial    schedule 21.06.2013
comment
@Mysticial, не поможет ли какая-нибудь инструкция по разбросу с Решетом Эатосфена? Xeon Phi и многие графические процессоры имеют опцию разброса. Ссылка, которую я упомянул, использует блочный подход для решета — это дает большое улучшение.   -  person    schedule 21.06.2013
comment
@BrettHale, спасибо за ссылку! Я просматриваю ваш профиль. Ваш лучший парень, может быть, вы могли бы написать ответ. Я уже пропускаю кратные 2,3,5 (из колесной факторизации). Удаляет более 70% композитов. Вы хотите составить подсписок простых кандидатов, используя первые 54 простых числа, используя SIMD и быстрое деление, а затем выполняя скалярное деление на гораздо меньшем подсписке?   -  person    schedule 21.06.2013
comment
@raxman - на самом деле я думал о тестировании 4 кандидатов одновременно. Проблема в том, что они выручат в разное время. Я думал о факторизации колеса, но Mystical прав во всем, что касается SIMD. Я не вижу, как заставить конвейер выполнять полезную работу по всем 4 или 8 элементам. Это также зависит от того, чего вы пытаетесь достичь: сгенерировать все простые числа ‹ N? Сколько памяти вы будете использовать? Насколько допустим предварительный расчет? и т.п.   -  person Brett Hale    schedule 21.06.2013
comment
Я уже тестирую 4 кандидатов одновременно (ну, в каком-то смысле, 8). Может быть, я должен предоставить свой код, чтобы показать, что я делаю сейчас.   -  person    schedule 21.06.2013
comment
@BrettHale, я добавил код, который использую сейчас.   -  person    schedule 21.06.2013
comment
Я просто хотел бы найти способ победить сито, используя пробное деление и SIMD (ну, еще лучше было бы использовать SIMD с ситом, но я понятия не имею, как это сделать), в настоящее время я пытаюсь сгенерировать простые числа в первый миллиард чисел (около 50 миллионов простых чисел), но в конечном итоге я хочу перейти на 64-битную версию, чтобы выйти за пределы 4 миллиардов.   -  person    schedule 21.06.2013
comment
Как вы думаете, поможет ли здесь взаимное деление? Это, безусловно, выигрыш для регистров общего назначения (целочисленных), но я не вижу здесь потенциала. Эффективность подхода Гранлунда основана на эффективном аппаратном умножении битов NxN=2N.   -  person Brett Hale    schedule 21.06.2013
comment
У меня появилась идея быстрого деления из векторного класса. Я сделал тест, разделив массив целых чисел на скаляр, используя скалярное деление и быстрое деление, и SIMD-версия примерно в 4 раза быстрее. Вот и вся причина, по которой он находится в векторном классе. Я не получаю ускорение в 4 раза с моим основным поиском, но оно составляет от 2 до 3 раз. Я делаю это, сохраняя четыре значения каждый раз, когда нахожу простое число (четыре целых числа: простое число, множитель и два значения сдвига), что ускоряет деление. Это описано в статье и векторном классе   -  person    schedule 21.06.2013
comment
Что ж, вот вам действительно психотический вызов... как насчет 4- или 8-факторного теста Миллера-Рабина? Всего один тест, оптимизированный для базы (2) (тест 2-SPRP); первым композитом, прошедшим этот тест, является (2047). Есть только (4842) композита до 1 миллиарда, которые прошли этот тест и нуждаются в дополнительном внимании. Из них еще один тест 7-SPRP и 61-SPRP обнаружит все составные части — фактически, это найдет все составные части для немного более 2^32.   -  person Brett Hale    schedule 22.06.2013
comment
Вот почему я попросил вас написать ответ. Я не первоклассный человек, это всего лишь хобби, чтобы помочь мне узнать о параллельном программировании. Я просто ищу предложения. Я думаю, было бы легче объяснить, что вы имеете в виду, если бы вы написали ответ.   -  person    schedule 22.06.2013
comment
Что ж, дайте мне подумать об этом. Я действительно использовал встроенные функции SSE только для операций с плавающей запятой (матрица/вектор); У меня не было много общего с целочисленными или логическими инструкциями, кроме or/xor. Тем временем я ответил на вопрос на днях и предоставил некоторые код для теста SPRP. Вы можете посмотреть, есть ли какие-либо перспективы для параллельного тестирования. У меня есть руководства Тумана, но я посмотрю его векторную библиотеку.   -  person Brett Hale    schedule 22.06.2013
comment
Спасибо за эти ссылки и код! Я прочитаю их. Я нечасто использовал SSE для работы с целыми числами (на самом деле, я впервые начал использовать SSE/AVX около 4 месяцев назад), поэтому я начал этот основной проект — помочь мне изучить операции с целыми числами с помощью SSE/AVX.   -  person    schedule 22.06.2013
comment
@BrettHale, я немного больше о SIMD и простых числах. Поскольку сито намного быстрее, я не думаю, что SIMD полезен для поиска списка простых чисел. Однако для проверки того, является ли число простым, это может быть полезно. Что я мог сделать, так это сгенерировать все простые числа для N ‹ 2 ^ 32 (например, с помощью сита), затем я мог проверить любое простое число до 2 ^ 64. Если бы я мог заставить этот алгоритм деления работать со скаляром/вектором, я мог бы одновременно тестировать кратные простые числа. Однако мне понадобится 64-битная версия, поэтому лучшее, что я могу сделать, это два номера одновременно с SSE / AVX или четыре номера одновременно с AVX2. Я посмотрю на это.   -  person    schedule 23.06.2013