Последний раз редактировалось Dmitriy40 03.10.2025, 19:38, всего редактировалось 7 раз(а).
Ещё вариант, промежуточный, 2.5, без делений, но с двумя остатками, надо дополнительно перед циклом обнулить R12 (в самом первом исходнике): inc R12 ;i%13
cmp R12,13
jc @f
mov R12,0
@@: inc R13 ;i%17
cmp R13,17
jc @f
mov R13,0
@@: cmp R12,9
je .next
cmp R13,16
je .next
inc R14 Время всего 3.901с, быстрее третьего варианта! Потому что в том тормозят и bt и cmov. -- 03.10.2025, 19:00 --Ну и вариант на AVX2 проверки сразу всех модулей 5-53:
;Эту часть кода надо добавить перед меткой .cycle:
vzeroupper
vpxor ymm0,ymm0,ymm0 ;Начальные значения остатков нулевые
vpxor ymm1,ymm1,ymm1 ;Начальные значения остатков нулевые
.cycle:
lea RAX,[R14+1]
vpcmpeqb ymm7,ymm0,[.noteq] ;если i%p==no тогда FF, иначе 00
vptest ymm7,ymm7 ;все ли байты нулевые?
cmovz R14,RAX ;если да, т.е. все остатки допустимы, то обновим счётчик
vpaddb ymm0,ymm0,[.c1] ;i++
vpsubb ymm7,ymm0,[.mods] ;i-p
vpminub ymm0,ymm0,ymm7 ;(i++)%p
;Эту часть кода надо добавить сразу после метки .mm:
align 32
.c1: db 32 dup(1)
.mods: db 1,1,1,13,17,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ;На непроверяемых местах больше 1 не будет
.noteq: db 7,7,7, 9,16,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 ;И уж точно не будет там 7
В таком варианте, с проверкой только по модулям 13 и 17, отрабатывает за 2.513с. Заменив константы инициализации на .mods: db 5,7,11,13,17,19,23,29,31,37,41,43,47,53, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1 ;На непроверяемых местах больше 1 не будет
.noteq: db 3,0, 9, 9,16,13,20,22, 5,36,24, 8, 6,28, 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7 ;И уж точно не будет там 7 для обработки модулей 5-53, отрабатывает за те же 2.606с, зато оставляет вдвое меньше кандидатов, лишь 902257012 вместо 1920000000 во всех предыдущих вариантах. Как видно осталось ещё 18 вакантных мест для проверки остатков по модулю, можно туда разместить например модули 59-79, каждого по три для контроля трёх запрещённых остатков, достаточно лишь заменить константы на: .mods: db 5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,59,59,61,61,61,67,67,67,71,71,71,73,73,73,79,79,79
.noteq: db 3,0, 9, 9,16,13,20,22, 5,36,24, 8, 6,28,44,46,48,20,31,42,43,46,49,16,45,66,19,28,37, 3,18,50 получим то же время в 2.590с, однако осталось лишь 687198159 кандидатов из 2.21млрд. Удвоив команды в цикле можно пофильтровать ещё по 9 модулям 83-127 (больше 128 для данного кода недопустимы) по трём запрещённым остаткам для каждого, что ещё немного улучшит фильтрацию:
lea RAX,[R14+1]
vpcmpeqb ymm7,ymm0,[.noteq] ;если i%p==no тогда FF, иначе 00
vpcmpeqb ymm6,ymm1,[.noteq+32] ;если i%p==no тогда FF, иначе 00
vpor ymm7,ymm7,ymm6
vptest ymm7,ymm7 ;все ли байты нулевые?
cmovz R14,RAX ;если да, т.е. все остатки допустимы, то обновим счётчик
vpaddb ymm0,ymm0,[.c1] ;i++
vpsubb ymm7,ymm0,[.mods] ;i-p
vpminub ymm0,ymm0,ymm7 ;(i++)%p
vpaddb ymm1,ymm1,[.c1] ;i++
vpsubb ymm7,ymm1,[.mods+32] ;i-p
vpminub ymm1,ymm1,ymm7 ;(i++)%p
;Это после метки .c1:
.mods: db 5,7,11,13,17,19,23,29,31,37,41,43,47,53, 59,59,59, 61,61,61, 67,67,67, 71,71,71, 73,73,73, 79,79,79
db 83,83,83, 89,89,89, 97,97,97, 101,101,101, 103,103,103, 107,107,107, 109,109,109, 113,113,113, 127,127,127, 1,1,1,1,1
.noteq: db 3,0, 9, 9,16,13,20,22, 5,36,24, 8, 6,28, 44,46,48, 20,31,42, 43,46,49, 16,45,66, 19,28,37, 3,18,50
db 16,57,78, 3,11,19, 55,59,63, 11, 39, 67, 13, 26, 71, 20, 75,101, 43, 90,105, 3, 44, 75, 2, 67, 98, 7,7,7,7,7
За время 3.355с оставит 524912534 кандидатов, менее четверти исходных, т.е. дальнейшим проверкам будет легче (они будут тупо реже запускаться). Можно и ещё регистр остатков добавить, только команды с суффксом w (по 16 битов) вместо b (байтовые), в регистр влезает 5 модулей по три запрещённых остатка по каждому. Добавит ещё около секунды времени и ещё чуть улучшит фильтрацию (в  раза). Напоминаю, все времена для 2.21млрд проверок, для 221млн времена надо делить на 10, а для 100млн делить на 22. -- 03.10.2025, 19:00 --Но как сделано, не понял. Я рассчитывал что те уроки ассемблера не совсем забылись. Смотрите: inc R13 увеличивает текущее значение остатка по модулю 221, при этом он может стать и 221 тоже cmp R13,221 как раз и проверяет не стал ли остаток равным 221 (это вычитание без сохранения результата, обновляет только флаги) jc @f это обход следующей команды (условный переход на первую метку @@ ниже) если выставлен флаг переноса, т.е. число в R13 меньше 221 - т.е. нормальное по модулю 221 mov R13,0 обнуляет остаток по модулю 221 если он стал равным или больше 221 lea RAX,[R14+1] это замена двум командам mov RAX,R14 и inc RAX, просто так компактнее и так же быстро, это подготовка увеличенного значения счётчика R14 если таковое понадобится bt [.mm],R13 это ровно bittest из PARI, в памяти по адресу .mm сидит битовый вектор, в R13 индекс (остаток по модулю 221, он не может быть больше 220), прочитанный бит возвращается в флаге переноса CF cmovc R14,RAX это условное копирование, если флаг переноса CF=1, то RAX (где лежит подготовленное значение R14++, вот и пригодилось) копируется в R14 обновляя его на большее значение всё. Первые 4 команды дополнительно к основному счётчику i в R15 организуют дополнительный счётчик в R13 величины i%221. Последние 3 команды реализуют операцию if(bittest(.mm, R13), R14++); в синтаксисе PARI. -- 03.10.2025, 19:09 --Заметьте, начиная с 2.5 и 3 вариантов, нигде в цикле не используется R15, т.е. вся работа идёт только по малым модулям, в арифметике по модулю. А значит совершенно не важно сколь большое i в R15, хоть 20 цифр в нём, главное правильно выставить начальное значение остатков по простым модулям до начала цикла и всё, потом будет работать как есть и с той же скоростью, без всяких модификаций, для абсолютно любого интервала (в пределах  , что легко решается увеличением разрядности i, тогда можно будет для счётчика количества итераций завести отдельный регистр, всё равно 1.8e19 итераций выполнить нереально, а столько в регистр влезает). -- 03.10.2025, 19:21 --И я кстати ошибся в 2.5 и 3 вариантах, надо сначала выполнить проверку, а потом уж увеличивать остаток. Сейчас начальный остаток не проверяется, зато проверяется 2210000000, чего не нужно. Но на время не влияет, только на коэффициент фильтрации (максимум плюс-минус 2шт). Так что исправлять специально не буду (хотя достаточно инициализировать R12 и R13 значением -1 вместо 0), всё равно это лишь тесты. -- 03.10.2025, 19:32 --Ускорение у меня не совпадает с Вашим вероятно по причине запущенных фоновых процессов у меня, даже мышка дёрганно движется, хоть вроде половина ядер и свободна, но то ли винда, то ли диск, то ли кэш проца, то ли память переполнилась и пошёл своп, но кто-то явно гадит. А прерывать фоновые процессы я не хочу. Так что Ваши цифры должны быть точнее (и повторяемее) моих.
|