2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




На страницу Пред.  1 ... 27, 28, 29, 30, 31  След.
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 02:15 
wrest в сообщении #1711875 писал(а):
Но попытка попробовать переполнять числа малыми делителями (ждать пока станет nf>nu) приводит к увеличению времени на пару порядков.
Да:
Dmitriy40 в сообщении #1711793 писал(а):
Не всегда: может набраться nf>nu делителей и тогда на простоту проверять не придётся (потому я и добавил проверку nf>nu, по инерции). Можете закомментировать проверку nf==nu и посмотреть насколько плохо фильтруется без неё, только по nf>nu (ну и квадратам). У меня фильтрация упала с 0.1млн:16 (без условия nf<nu, но с проверкой квадратов) до 0.1млн:2932, при этом время выросло с 4.7с до 134с.
1млн я не дождался, наверное больше получаса работало бы.

Я даже проверил что будет если проверку nf<nu как самую хорошо фильтрующую выполнять не после всех простых, а почаще, несколько раз за 2^20, но всегда было лишь замедление, даже на всего 4 разах (а пара раз просто не даст заметного эффекта).

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 02:20 
Yadryara в сообщении #1711877 писал(а):
Но, если можно, подскажите, пожалуйста поточнее, как именно мне изменить рабочую программу, чтобы она быстрее работала.

Ну так это вам виднее. Вот есть функция которую я опубликовал последней, em3() выше на этой странице.
В ней внутри есть несколько констант, после комментария
\\ переменные специфичные для паттерна
Меняете их на свои.
Нужно ещё понять ваши требования к выводу на экран/логированию в файл. Сейчас функция выводит только некоторую статистику.

Ну и всё - компилируете, запускаете и ждёте когда отработает...

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 02:22 
Yadryara
Только v[], (nu[]?), n0, придётся вычислять для каждой расстановки, здесь они заданы для одной конкретной.
Ну и всё что от них зависит.

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 02:36 
Dmitriy40
У меня мыслей по радикальному ускорению пока нет...
Обычно же скорость может повышаться за счёт увеличения расходов на память, а у нас память можно сказать бездействует практически. Значит можно сделать какие-то предвычисления очень быстрые, которыми как-то пользоваться с толком потом по ходу. Чтобы ещё меньше делить, например. Ну может предрассчитать остатки сразу скажем для начала 10 следующих цепочек.

Плюс сейчас как-то немного за кадром у меня остались предфильтры которые есть у вас в эталоне,
if((n+#v-1)%3^6==21-19, next);\\Такие n не подходят
я там не понял что к чему, а их можно дополнить и применять не только к началу цепочки а и ко всем членам?

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 02:46 
Аватара пользователя
wrest в сообщении #1711881 писал(а):
Вот есть функция которую я опубликовал последней, em3() выше на этой странице.

Вот на этой (на 30-й) странице как раз и не вижу. Вижу на 29-й. Пост, который в 17 часов по Москве. Разбираюсь пока.

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 02:59 
wrest в сообщении #1711883 писал(а):
Плюс сейчас как-то немного за кадром у меня остались предфильтры которые есть у вас в эталоне,
if((n+#v-1)%3^6==21-19, next);\\Такие n не подходят
я там не понял что к чему, а их можно дополнить и применять не только к началу цепочки а и ко всем членам?
Я их у себя объединил в два:
if((n+#v-1)%3^6<#v, next);
forprime(p=5,53, if((n+#v-1)%p^3<#v, next(2)); );

А делают они следующее: вот есть в паттерне v[] число 3^5, если при каком-то n на него попадёт ещё одна тройка, то число станет 3^6, что даст множитель в numdiv равный 7, но 48 не делится на 7 и потому такие n не подходят. Вероятность попасть сразу двум тройкам (т.е. 3^2) в место с 3^5 чтобы дать 3^7 и допустимый множитель 8 в numdiv пренебрежимо мала.
Но тройка (точнее 3^6) не может попасть ни на какие позиции v[] кроме как с 3^5, значит проверив просто что 3^6 в паттерн не попала мы полностью исключаем её и все высшие степени 3 в любой позиции. И их проверять уже не надо.
Аналогично и для простых 5...53, только они есть лишь в квадратах, потому проверяем не превратились ли они в кубы (и более старшие степени).
Где-то куб может оказаться и допустимым, но тогда там не хватит квадрата, а квадрат простого намного реже первой степени и на такие n тоже плюём ради скорости (задача поиска строго наименьшего решения не ставилась).

На самом деле эти длинные деления можно сделать короткими пересчитав ((n=n0+m*2*i)+#v-1)%p<#v в i%p<>C[p] (ведь остальное известные константы) где C[p] будет зависеть от места квадрата данного простого p в v[] (как и было в эталоне, видите эти 21-19, это оно, позиция в v[]), но мне лень, и на дальнейшую факторизацию это не влияет. В рабочей программе это сделано. Я даже проверил, на скорость замена в пределах погрешности не влияет.

-- 07.12.2025, 03:20 --

wrest в сообщении #1711883 писал(а):
Чтобы ещё меньше делить, например. Ну может предрассчитать остатки сразу скажем для начала 10 следующих цепочек.
Чтобы меньше делить надо полностью перейти к арифметике по модулю, тогда останутся только сложения/вычитания вместо умножений и делений. Но придётся складывать по модулю два вектора. Я же писал про это вчера днём (5 декабря):
Dmitriy40 в сообщении #1711746 писал(а):
Другое дело что да, можно заменить деление на сложение по модулю, но тогда вместо одного n или x придётся хранить и вычислять много разных n[] или x[], для каждого p своё. Про матрицы был не прав, достаточно вектора длиной с количество простых.
И да, сложение по модулю двух не слишком больших векторов вполне может быть быстрее деления длинного числа на короткое и тогда выгоднее посчитать больше сложений чем одно деление. Даже на PARI. Пробовать надо.
Например вот сложение двух векторов n[] и s[] по модулю третьего p[]: n+=s; t=n-p; for(j=1,#n, if(t[j]>0, n[j]=t[j]);, n,s,t,p - вектора длиной с количество проверяемых простых. И все они могут быть int32 (или даже int16 для порога до 2^14, но это вероятно медленнее).
...
И эта операция прекрасно ложится в SSE/AVX2. Например в AVX2 она реализуется тремя командами над регистром длиной 256 битов или 8 штук int32 и все три команды выполняются в среднем за полтора такта, плюс две команды накопления флага равенства нулю любого из остатков за такт, т.е. скорость такого суммирования длинных векторов составляет примерно 3.3 элементов int32 за такт. Или 6.6 элементов int16 за такт. Деление 192 битового числа (до 57 знаков) занимает 100-300 тактов в самом лучшем случае. Разница на три порядка.
Сможет ли gcc выдать столь же быстрый код - очень сомневаюсь, но это не мои проблемы, у меня есть ассемблер.
И ранее:
Dmitriy40 в сообщении #1711726 писал(а):
Умножение тоже не нужно: переход от предыдущего n к следующему производится просто прибавлением 2m. Соответственно переход от n%p к следующему n%p производится командой (n%p+m%p)%p (тут и далее двойка уже учтена в m) - оба слагаемых уже известны, достаточно сложить и проверить не превысило ли p, и если да, то просто вычесть p, ведь больше 2p стать не может: np=np+mp;if(np>=p,np-=p);. Разумеется np и mp это массивы. И я об этом уже писал где-то выше. Именно так работают мои программе на асме.

Да, проблема в том что их нельзя взять и оборвать досрочно если где-то остаток обнулился (т.е. разделилось на простое), надо просуммировать до конца - для следующих n. Зато весь (ну или начальная часть) цикл forprime с длинным делением внутри заменяется на сложение векторов, которое относительно быстрое. Как его превратить в быстрое сложение по модулю это вопрос для исследования. Можно как показал с циклом и if, можно типа n=[t=n[j]+s[j];if(t<p[j], t, t-p[j]) | j<-[1..#n]], можно объявить их (n[], s[]) с Mod(x,p) и тогда простое сложение прокатит, но вот проверка на наличие нуля вместо простого vecmin(n)==0 превратится снова в цикл, может ещё что придумать ...

-- 07.12.2025, 03:27 --

Если вдруг gcc в убунте поддерживает встроенный ассемблер или интриксы для вставки AVX команд в С код, то можно прямо на C поправить цикл для быстрого суммирования по модулю векторов. А их инициализацию и дальнейшие действия оставить на PARI. Но с этим надо разбираться. А у меня ни убунты ни gcc нету.

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 04:04 
Dmitriy40 в сообщении #1711885 писал(а):
но вот проверка на наличие нуля вместо простого vecmin(n)==0 превратится снова в цикл
Не, всё сложнее, можно сразу получить список делителей командой divs=select(t->t<#v,n,1) и если он не пуст то разбираться с ним как-то так: foreach(Vec(divs),j, p=pr[j]; d=#v-n[j]; и дальше как было, только без деления, d уже получили.
С наскока сделать не получилось.
Ох чувствую не будет это быстрее длинных делений ...

-- 07.12.2025, 04:17 --

wrest
Смотрите как можно извратить строку
if(nf[d]==nu[d], q1++; if(!ispseudoprime((n+d-1)/v[d]/nn[d],1), q2++; next(2)));
превратив её в
nf[d]==nu[d] && q1++ && !ispseudoprime((n+d-1)/v[d]/nn[d],1) && q2++ && next(2);
не знаю правда зачем ...
Главное чтобы счётчики q1,q2 случайно не обнулились (не были -1 до этого).

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 06:59 
Аватара пользователя
wrest в сообщении #1711881 писал(а):
Нужно ещё понять ваши требования к выводу на экран/логированию в файл.

Это как раз не проблема. С файлами я пока разобрался.

wrest в сообщении #1711881 писал(а):
Сейчас функция выводит только некоторую статистику.

Я вашу функцию раздербанил и внёс изменения в рабочую программу в нескольких местах. И в интерпретаторе она работала без проблем. Однако после компиляции начала было работать, но почти сразу отказалась:

Код:
  ***   at top-level: init_Rab_63_1()
  ***                 ^---------------
  *** init_Rab_63_1: bug in PARI/GP (Segmentation Fault), please report.
  ***   Break loop: type 'break' to go back to GP prompt
break>

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 09:51 
Аватара пользователя
Вылетание по ошибке происходит вот здесь:

Код:
ostm  = vectorsmall( #predp, i, m  % predp[i] ); \\вектор с остатками от m
ostn0 = vectorsmall( #predp, i, n0 % predp[i] ); \\вектор с остатками от n0

Кстати, вот вы меня ругаете за малое количество коментов. Но зато я стараюсь давать переменным информативные названия.

Предварительно вычисленные простые это что? Правильно, предпростые — predp. Остаток от деления m на эти самые предпростые — ostm. Остаток от деления n0 на них же — ostn0.

То есть коменты эти уже и не нужны: и так понятно, не правда ли?

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 10:27 
Yadryara в сообщении #1711893 писал(а):
Вылетание по ошибке происходит вот здесь:

Посмотрите как у меня декларируются переменные. В частности эти векторы объявлены так:
my(pmods_n0:vecsmall=vectorsmall(pr_q,i,n0%pr[i])); \\вектор с остатками по простым по n0
my(pmods_m:vecsmall=vectorsmall(pr_q,i,m%pr[i])); \\ вектор с остатками по простым по m

После имени переменной через двоеточие её тип. Это указание для gp2c, интерпретатор это игнорирует.
И надо чтобы потом эти переменные использовались так, чтобы длинные типы не присваивались коротким, а конвертировались.
В общем, надо помогать gp2c
Просто "раздербанить" не выйдет :D
Я с этим segmentation fault боролся довольно долго.
Кстати во время трансляции и компиляции обращайте внимание на диагностику, которую выводит gp2c и компилятор.

Yadryara в сообщении #1711893 писал(а):
То есть коменты эти уже и не нужны: и так понятно, не правда ли?

Правда, но только если потребитель этого кода вы сами и ваш pari/gp, а если предполагается что читать будут другие кожаные мешки, кроме вас, то комментарии именно для этого.

-- 07.12.2025, 10:40 --

Dmitriy40 в сообщении #1711885 писал(а):
Можно как показал с циклом и if, можно типа n=[t=n[j]+s[j];if(t<p[j], t, t-p[j]) | j<-[1..#n]], можно объявить их (n[], s[]) с Mod(x,p) и тогда простое сложение прокатит, но вот проверка на наличие нуля вместо простого vecmin(n)==0 превратится снова в цикл, может ещё что придумать ...

Этот синтаксический сахар хорошо выглядит и даже имеет ускорительный смысл когда вы программируете интерпретатор. Но в Си этих конструкций нет, так что это всё заменяется на старые добрые циклы, и очередной ноль там проходит через ваши руки непосредственно.

-- 07.12.2025, 11:04 --

Dmitriy40 в сообщении #1711885 писал(а):
Если вдруг gcc в убунте поддерживает встроенный ассемблер или интриксы для вставки AVX команд в С код, то можно прямо на C поправить цикл для быстрого суммирования по модулю векторов. А их инициализацию и дальнейшие действия оставить на PARI. Но с этим надо разбираться. А у меня ни убунты ни gcc нету.

Ну AVX это вы уже немного через край хватанули, имхо. У меня вот например под рукой две архитектуры aarch64 (она же arm64) и amd64 (она же x86-64), при том для развлечений с pari основная aarch64

(Оффтоп)

И кстати, я из пентадекатлонной темы понял что у вас там есть общественное достояние в виде opensource программки pcoul, тогда уже лучше смотреть в ту сторону. А pcoul этот кстати как без gmp оходится - там длинная арифметика своя?

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 11:30 
Аватара пользователя
wrest в сообщении #1711897 писал(а):
После имени переменной через двоеточие её тип. Это указание для gp2c, интерпретатор это игнорирует.

Поставил через двоеточие её тип. Но пока что он эту строчку не преодолел:

ostm:vecsmall = vectorsmall( #predp, i, m % predp[i] ); \\вектор с остатками от m

Что, указание my( ) обязательно?

wrest в сообщении #1711897 писал(а):
Кстати во время трансляции и компиляции обращайте внимание на диагностику, которую выводит gp2c и компилятор.

Так я вам показывал. Он вроде ругается на quit, но работает:

Код:
yadryara@DESKTOP-QPP2F5P:~/D48-22$ gp2c-run -g Rab_63_2.gp
Rab_63_2.gp.c: In function ‘init_Rab_63_2’:
Rab_63_2.gp.c:1132:3: warning: implicit declaration of function ‘gp_quit’ [-Wimplicit-function-declaration]
1132 |   gp_quit(0);
      |   ^~~~~~~
Reading GPRC: /etc/gprc
GPRC Done.

wrest в сообщении #1711897 писал(а):
Правда, но только если потребитель этого кода вы сами и ваш pari/gp, а если предполагается что читать будут другие кожаные мешки, кроме вас, то комментарии именно для этого.

А вы помните что Копчёный ответил на подобное Жеглову?

Не надо общих слов. Лично вам понятно? Могу я убрать комент конкретно из этих двух строк?

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 11:37 
Dmitriy40 в сообщении #1711885 писал(а):
Да, проблема в том что их нельзя взять и оборвать досрочно если где-то остаток обнулился (т.е. разделилось на простое), надо просуммировать до конца - для следующих n.

Вот именно, а это 80 тысяч на каждую цепочку.
При том что в среднем нам нужны сотня-другая остатков.

-- 07.12.2025, 11:47 --

Yadryara в сообщении #1711900 писал(а):
Могу я убрать комент конкретно из этих двух строк?

Мы же свободные люди в свободных странах, делайте как считаете нужным :)

Yadryara в сообщении #1711900 писал(а):
Что, указание my( ) обязательно?

В случае функции - да, это делает переменную локальной и она не портится остальным кодом который вне функции.
В случае если у вас глобальный код - не знаю как действует my, надо читать или экспериментировать. Но лучше наверное убрать. Тогда может вылезти другая бяка, что слева у вас то чему нельзя присвоить значение. Но попробуйте так и так
Yadryara в сообщении #1711900 писал(а):
. Но пока что он эту строчку не преодолел:
ага, как всегда информативно, ну спросите у ИИ что делать если "не преодолел"

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 11:54 
Аватара пользователя
wrest в сообщении #1711901 писал(а):
ага, как всегда информативно, ну спросите у ИИ что делать если "не преодолел"

Не преодолел это значит что остановился и указал ровно на ту же самую ошибку:

bug in PARI/GP (Segmentation Fault),

-- 07.12.2025, 11:56 --

wrest в сообщении #1711901 писал(а):
В случае если у вас глобальный код

У меня глобальный код. Ни одной функции нету. И пока вроде не надо.

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 13:12 
Yadryara в сообщении #1711904 писал(а):
У меня глобальный код. Ни одной функции нету. И пока вроде не надо.

Хорошо, нет так нет. А я предупреждал:
wrest в сообщении #1708713 писал(а):
Yadryara в сообщении #1708711 писал(а):
Если я Вам пришлю программу, сможете её потестить на предмет ускорения?

Да, если это будет законченная функция (t_CLOSURE)
То есть это не должен быть код как вы его вписываете в интерпретаторе, а должно быть всё обёрнуто в my_function(args)={...}
Соответственно, функция не должна использовать глобальные переменные внутри своего кода, т.е. всё что ей надо должно передаваться через аргументы.
Ну и конечно все локальные переменные надо объявить через my().

Более того, я вам уже переделывал вашу программу в функцию, посмотрите как там сделано.

В общем, вам надо смотреть в Си-код и как-то догадаться, что именно приводит к segmentation fault.
Например, расставить в коде тестовую печать и смотреть докуда доходит исполнение и где падает, комментировать какие-то строчки и постепенно продвигаться и лечить.

Другой подход -- взять мою функцию и очень постепенно добавлять в неё что вам надо, каждый раз компилируя и запуская и смотря нет ли ошибок.
Большая работа, понимаю. У меня тоже функция что на 29 страницы долго рождалась из той, что на 28 странице.

 
 
 
 Re: Как писать быстрые программы
Сообщение07.12.2025, 13:29 
Аватара пользователя
wrest в сообщении #1711910 писал(а):
Например, расставить в коде тестовую печать и смотреть докуда доходит исполнение и где падает,

??
А вы думаете я этого не сделал? Откуда же я тогда знаю докуда доходит прога и на чём стопорится? Отладчика у меня никакого нет.

Есть команда напечатать: print("Ya zdes!");

 
 
 [ Сообщений: 453 ]  На страницу Пред.  1 ... 27, 28, 29, 30, 31  След.


Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group