Большое спасибо за ваш отклик.
Чтобы не выглядело совсем уж как только ругань Вашей программы, позволю себе дать пару советов по ускорению генераци простых чисел. За что заранее извиняюсь. :)
1. До 510510 всего 42331 простое число, можно в set_pr17 найти их все и сохранить в массив pr[], расширив его соответственно. Буфер в 510510 boolean у Вас и так уже есть, а больше ничего и не нужно. Самым наипростым решетом вычеркиваем оттуда числа и всё. Это займёт намного меньше секунды. Правда сильно это не ускорит.
2. Вы вычеркиваете не только простые, но и составные числа, всего 92160 штук на каждый сегмент. В то же время простых среди них не более половины, а чем дальше сегмент от начала, тем меньше. В интересном сейчас диапазоне около
их уже меньше 15 тысяч, т.е. в 6 раз меньше. Потому предлагаю вычеркивать лишь точно простые, а находить их вспомогательным решетом на базе уже найденных в п.1 первых простых чисел. Для этого используйте новый массив p1[Nseg] of boolean, который двигайте от Nseg (досюда простые числа уже найдены в п.1) дальше шагами по Nseg пока не пройдёте границу
, вычёркивайте из него составные числа (используя найденный в начале массив простых), оставшиеся простые по мере нахождения вычеркивайте из основного массива.
Оценим количество операций для сравнения даст ли это ускорение. Оценку проведём для практически интересного сейчас Nseg=
(около
). Чтобы вычеркнуть из основного решета все составные числа надо не менее
первых простых чисел (до
). Чтобы их все найти надо обработать 196 первых сегментов. Считаем, из 196 сегментов проводится не более 1229 вычёркиваний (простых меньших
), из основного решета менее 5818159 вычёркиваний, в сумме менее 6млн. Сейчас Вы делаете
вычёркиваний. В
158 раз больше! Конечно будут накладыне расходы на организацию двойного решета, но они уменьшат выигрыш на несколько процентов всего лишь. А все процедуры у Вас уже написаны, осталось добавить пару массивов и вызывать их в другом порядке. Требования к памяти увеличатся всего на один массив p1[Nseg] (а можно и вообще основной использовать) и массив простых pr1[1229]. Слёзы. Зато можно ускорить раз в 100!
После реализации этого пункта размер сегмента перестаёт быть привязанным к произведению простых чисел и можно его выбирать произвольно (пригодится в п.5 и далее). Я для себя выбрал ровно 1млн, такой влазит в L2 кэш (см. п.5-п.7) и удобен для человека.
3. Если не гнаться за числами
и более, то можно просто сначала найти все простые числа до
, сохранить их в массив pr[6млн] (для чисел до
) и потом просто из каждого нового сегмента их вычёркивать. Когда их не хватит (сегменты уйдут далеко вперёд), то один раз запустить вспомогательное решето, нагенерить ещё десяток тысяч простых чисел (ну сколько найдётся в одном сегменте), сохранить их в массив (увеличив его размер или выделив заранее с большим запасом) и продолжить работу. 6млн простых чисел хапнут всего 25МБ памяти (они гарантировано int32), не так уж и много. Можно выделить и 70МБ памяти, туда влезут все простые до 316млн, их хватит для проверки чисел до
. Ну а для чисел около
памяти будет нужно уже 200МБ. Что тоже не смертельно. Правда ускорения почти не даст, полторы тысячи вспомогательных вычёркиваний на фоне 6млн незаметны.
4. Если реализовать п.3, то удвоив требования к памяти можно отказаться от одной из самых долгих операций в цикле вычеркиваний: "jj:=k-nsi mod k;". Деление (и взятие остатка) выполняется весьма медленно, ну по сравнению с другими операциями. На моих тестах исключение операции mod ускоряет программу в 6 раз. Но это уже нетривиальное усложнение программы.
5. Стоит уменьшить размер сегмента до размеров L2 кэша процессоров, а он сейчас обычно 256КБ (или даже L1 в 32КБ). Это может ускорить работу раза в три ... А может и меньше, растут повторные действия.
6. Для уменьшения повторных действий применить битовую упаковку boolean (в дельфи он байтовый, не знаю как в Вашем компиляторе), это позволит путём нестрашного усложнения программы увеличить размер сегмента в 8 раз. С 8-ми кратным сокращением повторяемых действий.
7. Ну и тупо сделать хранение в p0,p[Nseg] только нечётных чисел, это позволит удвоить размер сегмента вообще без накладных расходов.
Прошу прощения если это всё известные Вам вещи. Или если они уже частично реализованы, а я не заметил. :)
PS. Если не гнаться за рекордами, я бы реализовал пункты 1, 2, 5, 7 и всё. 6-й по желанию можно и потом. Дальше ускорение уже не критично, а вот усложнение программы и требования к памяти ... А для первых двух пунктов у Вас уже практически всё готово.
-- 25.09.2014, 18:16 --Предложения по дальнейшему ускорению генерации простых чисел есть и ещё, но их я и сам ещё не реализовал, потому советовать не осмелюсь. Хотя в primeseive они почти все сделаны ... Просто перечислю:
8. Многопоточность.
9. Вообще не вычёркивать числа, точно не попадающие в сегмент. И определять это заранее, ещё до обработки сегмента.
10. Планировать работу на много сегментов вперёд (особенно предыдущий пункт).
11. В основном сегменте не хранить не только чётных чисел, но также и кратных 3, 5, 7, 11, ...
12. Оптимизировать цикл вычёркиваний чисел, попадающих в сегмент ровно один раз и более одного раза (разбить один цикл на два).
13. Частично развернуть циклы вычёркиваний, чтобы было больше одинаковых процессорных команд подряд (для обработки разных чисел) - для более полной загрузки множества АЛУ в процессоре. Потребует переписываний циклов на ассемблере.
14. Для чтения всего кроме текущего сегмента использовать команды некэшируемой загрузки XMM регистров чтобы не сбивать кэш сегмента. Тоже pure asm required.
15. Ну а дальше уже совсем дебри.