последний ответ Аарона Мугатройда из переведенного источника Python для Sieve of Atkin (SoA) не так уж и плох, но его можно улучшить в нескольких отношениях, поскольку в нем отсутствуют некоторые важные оптимизации, а именно:
В его ответе используется не полная исходная версия сита Аткина и Бернштейна по модулю 60, а скорее немного улучшенная вариация псевдокода из статьи в Википедии, поэтому используется коэффициент 0,36 от числового диапазона сита, объединенного операций переключения / отбраковки; в моем приведенном ниже коде используется достаточно эффективный псевдокод внестраничного сегмента в соответствии с моими комментариями в ответе, комментирующем решето Аткина который использует коэффициент примерно в 0,26 раза больше числового диапазона, чтобы уменьшить объем выполняемой работы примерно до двух седьмых раз.
Его код уменьшает размер буфера, имея только представления нечетных чисел, тогда как мой код содержит дополнительные битовые пакеты, чтобы исключить любое представление чисел, делимых на три и пять, а также тех, которые делятся на два, подразумеваемых "только нечетными"; это снижает потребность в памяти почти вдвое (до 8/15) и помогает лучше использовать кеши ЦП для дальнейшего увеличения скорости за счет уменьшения среднего времени доступа к памяти.
Мой код подсчитывает количество простых чисел, используя технику подсчета всплывающих окон быстрой таблицы поиска (LUT), чтобы на подсчет почти не уходило времени по сравнению с примерно одной секундой, используя технику побитовой обработки, которую он использует; однако в этом примере кода даже это небольшое время отведено из временной части кода.
Наконец, мой код оптимизирует операции манипулирования битами для минимума кода на внутренний цикл. Например, он не использует непрерывный сдвиг вправо на единицу для получения индекса нечетного представления, а фактически совсем немного сдвигает бит, записывая все внутренние циклы как операции с постоянным модулем (равным битовой позиции). Кроме того, переведенный код Аарона довольно неэффективен в операциях, так как, например, при отбраковке без простых квадратов он добавляет квадрат простого числа к индексу, а затем проверяет нечетный результат, а не просто добавляет два раза больше квадрата, чтобы не требовать проверки. ; затем он делает даже проверку избыточной, сдвигая число вправо на единицу (деление на два) перед выполнением операции отсечения во внутреннем цикле, как это происходит для всех циклов. Этот неэффективный код не будет иметь большого значения во времени выполнения для больших диапазонов с использованием метода «большого ситового буферного массива», поскольку большая часть времени на операцию используется для доступа к оперативной памяти (около 37 тактовых циклов ЦП или более для диапазон в один миллиард), но сделает время выполнения намного медленнее, чем нужно для меньших диапазонов, которые помещаются в кеш-память ЦП; другими словами, он устанавливает слишком высокий нижний предел скорости выполнения для каждой операции.
Код выглядит следующим образом:
//Sieve of Atkin based on full non page segmented modulo 60 implementation...
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace NonPagedSoA {
//implements the non-paged Sieve of Atkin (full modulo 60 version)...
class SoA : IEnumerable<ulong> {
private ushort[] buf = null;
private long cnt = 0;
private long opcnt = 0;
private static byte[] modPRMS = { 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 49, 53, 59, 61 };
private static ushort[] modLUT;
private static byte[] cntLUT;
//initialize the private LUT's...
static SoA() {
modLUT = new ushort[60];
for (int i = 0, m = 0; i < modLUT.Length; ++i) {
if ((i & 1) != 0 || (i + 7) % 3 == 0 || (i + 7) % 5 == 0) modLUT[i] = 0;
else modLUT[i] = (ushort)(1 << (m++));
}
cntLUT = new byte[65536];
for (int i = 0; i < cntLUT.Length; ++i) {
var c = 0;
for (int j = i; j > 0; j >>= 1) c += j & 1;
cntLUT[i] = (byte)c;
}
}
//initialization and all the work producing the prime bit array done in the constructor...
public SoA(ulong range) {
this.opcnt = 0;
if (range < 7) {
if (range > 1) {
cnt = 1;
if (range > 2) this.cnt += (long)(range - 1) / 2;
}
this.buf = new ushort[0];
}
else {
this.cnt = 3;
var nrng = range - 7; var lmtw = nrng / 60;
//initialize sufficient wheels to non-prime
this.buf = new ushort[lmtw + 1];
//Put in candidate primes:
//for the 4 * x ^ 2 + y ^ 2 quadratic solution toggles - all x odd y...
ulong n = 6; // equivalent to 13 - 7 = 6...
for (uint x = 1, y = 3; n <= nrng; n += (x << 3) + 4, ++x, y = 1) {
var cb = n; if (x <= 1) n -= 8; //cancel the effect of skipping the first one...
for (uint i = 0; i < 15 && cb <= range; cb += (y << 2) + 4, y += 2, ++i) {
var cbd = cb / 60; var cm = modLUT[cb % 60];
if (cm != 0)
for (uint c = (uint)cbd, my = y + 15; c < buf.Length; c += my, my += 30) {
buf[c] ^= cm; // ++this.opcnt;
}
}
}
//for the 3 * x ^ 2 + y ^ 2 quadratic solution toggles - x odd y even...
n = 0; // equivalent to 7 - 7 = 0...
for (uint x = 1, y = 2; n <= nrng; n += ((x + x + x) << 2) + 12, x += 2, y = 2) {
var cb = n;
for (var i = 0; i < 15 && cb <= range; cb += (y << 2) + 4, y += 2, ++i) {
var cbd = cb / 60; var cm = modLUT[cb % 60];
if (cm != 0)
for (uint c = (uint)cbd, my = y + 15; c < buf.Length; c += my, my += 30) {
buf[c] ^= cm; // ++this.opcnt;
}
}
}
//for the 3 * x ^ 2 - y ^ 2 quadratic solution toggles all x and opposite y = x - 1...
n = 4; // equivalent to 11 - 7 = 4...
for (uint x = 2, y = x - 1; n <= nrng; n += (ulong)(x << 2) + 4, y = x, ++x) {
var cb = n; int i = 0;
for ( ; y > 1 && i < 15 && cb <= nrng; cb += (ulong)(y << 2) - 4, y -= 2, ++i) {
var cbd = cb / 60; var cm = modLUT[cb % 60];
if (cm != 0) {
uint c = (uint)cbd, my = y;
for ( ; my >= 30 && c < buf.Length; c += my - 15, my -= 30) {
buf[c] ^= cm; // ++this.opcnt;
}
if (my > 0 && c < buf.Length) { buf[c] ^= cm; /* ++this.opcnt; */ }
}
}
if (y == 1 && i < 15) {
var cbd = cb / 60; var cm = modLUT[cb % 60];
if ((cm & 0x4822) != 0 && cbd < (ulong)buf.Length) { buf[cbd] ^= cm; /* ++this.opcnt; */ }
}
}
//Eliminate squares of base primes, only for those on the wheel:
for (uint i = 0, w = 0, pd = 0, pn = 0, msk = 1; w < this.buf.Length ; ++i) {
uint p = pd + modPRMS[pn];
ulong sqr = (ulong)p * (ulong)p; //to handle ranges above UInt32.MaxValue
if (sqr > range) break;
if ((this.buf[w] & msk) != 0) { //found base prime, square free it...
ulong s = sqr - 7;
for (int j = 0; s <= nrng && j < modPRMS.Length; s = sqr * modPRMS[j] - 7, ++j) {
var cd = s / 60; var cm = (ushort)(modLUT[s % 60] ^ 0xFFFF);
//may need ulong loop index for ranges larger than two billion
//but buf length only good to about 2^31 * 60 = 120 million anyway,
//even with large array setting and half that with 32-bit...
for (ulong c = cd; c < (ulong)this.buf.Length; c += sqr) {
this.buf[c] &= cm; // ++this.opcnt;
}
}
}
if (msk >= 0x8000) { msk = 1; pn = 0; ++w; pd += 60; }
else { msk <<= 1; ++pn; }
}
//clear any overflow primes in the excess space in the last wheel/word:
var ndx = nrng % 60; //clear any primes beyond the range
for (; modLUT[ndx] == 0; --ndx) ;
this.buf[lmtw] &= (ushort)((modLUT[ndx] << 1) - 1);
}
}
//uses a fast pop count Look Up Table to return the total number of primes...
public long Count {
get {
long cnt = this.cnt;
for (int i = 0; i < this.buf.Length; ++i) cnt += cntLUT[this.buf[i]];
return cnt;
}
}
//returns the number of toggle/cull operations used to sieve the prime bit array...
public long Ops {
get {
return this.opcnt;
}
}
//generate the enumeration of primes...
public IEnumerator<ulong> GetEnumerator() {
yield return 2; yield return 3; yield return 5;
ulong pd = 0;
for (uint i = 0, w = 0, pn = 0, msk = 1; w < this.buf.Length; ++i) {
if ((this.buf[w] & msk) != 0) //found a prime bit...
yield return pd + modPRMS[pn]; //add it to the list
if (msk >= 0x8000) { msk = 1; pn = 0; ++w; pd += 60; }
else { msk <<= 1; ++pn; }
}
}
//required for the above enumeration...
IEnumerator IEnumerable.GetEnumerator() {
return this.GetEnumerator();
}
}
class Program {
static void Main(string[] args) {
Console.WriteLine("This program calculates primes by a simple full version of the Sieve of Atkin.\r\n");
const ulong n = 1000000000;
var elpsd = -DateTime.Now.Ticks;
var gen = new SoA(n);
elpsd += DateTime.Now.Ticks;
Console.WriteLine("{0} primes found to {1} using {2} operations in {3} milliseconds.", gen.Count, n, gen.Ops, elpsd / 10000);
//Output prime list for testing...
//Console.WriteLine();
//foreach (var p in gen) {
// Console.Write(p + " ");
//}
//Console.WriteLine();
//Test options showing what one can do with the enumeration, although more slowly...
// Console.WriteLine("\r\nThere are {0} primes with the last one {1} and the sum {2}.",gen.Count(),gen.Last(),gen.Sum(x => (long)x));
Console.Write("\r\nPress any key to exit:");
Console.ReadKey(true);
Console.WriteLine();
}
}
}
Этот код работает примерно в два раза быстрее, чем код Аарона (около 2,7 секунды в 64-разрядном или 32-разрядном режиме на i7-2700K (3,5 ГГц) с буфером около 16,5 мегабайт и около 0,258 миллиарда комбинированных операций отсечения без квадратов с переключением / простыми квадратами). (что можно показать, раскомментировав операторы "++ this.opcnt") для диапазона сита в один миллиард, по сравнению с 5,4 / 6,2 секунды (32-бит / 64-бит) для его кода без учета времени и почти вдвое больше памяти, используя около 0,359 миллиарда комбинированных операций переключения / отсеивания для просеивания до одного миллиарда.
Хотя это быстрее, чем его наиболее оптимизированная наивная реализация непрозрачного «Сита Эратосфена» (SoE), который не делает Сито Аткина быстрее, чем Сито Эратосфена, как будто применяет методы, аналогичные тем, которые использовались в приведенной выше реализации SoA, к SoE плюс использует максимальную факторизацию колеса, SoE будет примерно с той же скоростью, что и эта.
Анализ. Хотя количество операций для полностью оптимизированной SoE примерно такое же, как количество операций для SoA для диапазона сит в один миллиард, основным узким местом для этих невыгружаемых реализаций является память. доступ, когда размер ситового буфера превышает размеры кеш-памяти ЦП (32 килобайта кеш-памяти L1 при доступе за один тактовый цикл, 256 килобайт кеш-памяти L2 при времени доступа около четырех тактовых циклов и 8 мегабайт кеш-памяти L3 при времени доступа примерно 20 тактовых циклов для моего i7), после чего доступ к памяти может превышать сотню тактов.
Теперь у обоих есть примерно восьмикратное улучшение скорости доступа к памяти, если адаптировать алгоритмы к сегментации страниц, чтобы можно было отсеивать диапазоны, которые иначе не поместились бы в доступную память. Тем не менее, SoE продолжает выигрывать по сравнению с SoA, поскольку диапазон сита начинает становиться очень большим из-за трудностей в реализации части алгоритма «без квадратов простых чисел» из-за огромных успехов в сканировании отбраковки, которые быстро увеличиваются до многих сотен раз. размер буферов страницы. Кроме того, и, возможно, более серьезно, он требует очень много памяти и / или вычислительных ресурсов для вычисления новой начальной точки для каждого значения 'x' относительно значения 'y' в самом низком представлении каждого буфера страницы для дальнейшего довольно большая потеря эффективности выгружаемой SoA по сравнению с SoE по мере увеличения диапазона.
EDIT_ADD: SoE только для вероятностей, используемый Аароном Мургатройдом, использует около 1,026 миллиарда операций отбраковки для диапазона сита в один миллиард, что примерно в четыре раза больше операций, чем SoA, и, следовательно, должно выполняться примерно в четыре раза медленнее. , но SoA, даже реализованная здесь, имеет более сложный внутренний цикл, и особенно из-за гораздо более высокой доли отбраковок SoE только с учетом вероятности имеют гораздо более короткий шаг в сканировании отсева, чем шаги SoA. имеет гораздо лучшее среднее время доступа к памяти, несмотря на то, что размер ситового буфера значительно превышает размер кеш-памяти ЦП (лучшее использование ассоциативности кеша). Это объясняет, почему вышеупомянутая SoA всего примерно в два раза быстрее, чем SoE, рассчитанная только на разногласия, хотя теоретически может показаться, что она выполняет только четверть работы.
Если бы можно было использовать аналогичный алгоритм с использованием внутренних циклов с постоянным модулем по модулю, как для приведенной выше SoA, и реализовать ту же факторизацию колеса 2/3/5, SoE уменьшил бы количество операций отсечения примерно до 0,405 миллиарда операций, так что только примерно на 50% больше операций, чем SoA, и теоретически будет работать немного медленнее, чем SoA, но может работать примерно с той же скоростью, потому что шаги отбраковки все еще немного меньше, чем для SoA в среднем при таком "наивном" использовании большого буфера памяти. Увеличение факторизации колеса до колеса 2/3/5/7 означает, что операции отбраковки SoE снижаются примерно до 0,314 для диапазона отбраковки в один миллиард, и может заставить эту версию SoE работать примерно с той же скоростью для этого алгоритма.
Дальнейшее использование факторизации колеса может быть выполнено путем предварительного отбора массива сит (копирование в шаблон) для основных факторов 2/3/5/7/11/13/17/19 почти без затрат времени выполнения, чтобы уменьшить общее количество операций отбраковки составляет около 0,251 миллиарда для диапазона сит в один миллиард, и SoE будет работать быстрее или примерно с той же скоростью, чем даже эта оптимизированная версия SoA, даже для этих больших версий буфера памяти, при этом SoE все еще имеет много меньшая сложность кода, чем указано выше.
Таким образом, можно увидеть, что количество операций для SoE может быть значительно сокращено по сравнению с наивной или даже версией факторизации колеса только с коэффициентами 2/3/5, так что количество операций примерно такое же, как для SoA, в то время как в то же время время на операцию может быть меньше из-за менее сложных внутренних циклов и более эффективного доступа к памяти. END_EDIT_ADD
EDIT_ADD2: здесь я добавляю код для SoE, используя аналогичную технику постоянного модуля / битовой позиции для самых внутренних циклов, как для SoA выше, в соответствии с псевдокодом далее по ответу, как указано выше. Код несколько менее сложен, чем приведенный выше SoA, несмотря на то, что в нем применена высокая факторизация колеса и предварительная отбраковка, так что общее количество операций отбраковки на самом деле меньше, чем объединенные операции переключения / отбраковки для SoA вплоть до диапазона просеивания. около двух миллиардов. Код следующий:
EDIT_FINAL исправленный код ниже и комментарии к нему END_EDIT_FINAL
//Sieve of Eratosthenes based on maximum wheel factorization and pre-culling implementation...
using System;
using System.Collections;
using System.Collections.Generic;
namespace NonPagedSoE {
//implements the non-paged Sieve of Eratosthenes (full modulo 210 version with preculling)...
class SoE : IEnumerable<ulong> {
private ushort[] buf = null;
private long cnt = 0;
private long opcnt = 0;
private static byte[] basePRMS = { 2, 3, 5, 7, 11, 13, 17, 19 };
private static byte[] modPRMS = { 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, //positions + 23
97, 101, 103, 107, 109, 113, 121, 127, 131, 137, 139, 143, 149, 151, 157, 163,
167, 169, 173, 179, 181 ,187 ,191 ,193, 197, 199, 209, 211, 221, 223, 227, 229 };
private static byte[] gapsPRMS = { 6, 2, 6, 4, 2, 4, 6, 6, 2, 6, 4, 2, 6, 4, 6, 8,
4, 2, 4, 2, 4, 8, 6, 4, 6, 2, 4, 6, 2, 6, 6, 4,
2, 4, 6, 2, 6, 4, 2, 4, 2, 10, 2, 10, 2, 4, 2, 4 };
private static ulong[] modLUT;
private static byte[] cntLUT;
//initialize the private LUT's...
static SoE() {
modLUT = new ulong[210];
for (int i = 0, m = 0; i < modLUT.Length; ++i) {
if ((i & 1) != 0 || (i + 23) % 3 == 0 || (i + 23) % 5 == 0 || (i + 23) % 7 == 0) modLUT[i] = 0;
else modLUT[i] = 1UL << (m++);
}
cntLUT = new byte[65536];
for (int i = 0; i < cntLUT.Length; ++i) {
var c = 0;
for (int j = i ^ 0xFFFF; j > 0; j >>= 1) c += j & 1; //reverse logic; 0 is prime; 1 is composite
cntLUT[i] = (byte)c;
}
}
//initialization and all the work producing the prime bit array done in the constructor...
public SoE(ulong range) {
this.opcnt = 0;
if (range < 23) {
if (range > 1) {
for (int i = 0; i < modPRMS.Length; ++i) if (modPRMS[i] <= range) this.cnt++; else break;
}
this.buf = new ushort[0];
}
else {
this.cnt = 8;
var nrng = range - 23; var lmtw = nrng / 210; var lmtwt3 = lmtw * 3;
//initialize sufficient wheels to prime
this.buf = new ushort[lmtwt3 + 3]; //initial state of all zero's is all potential prime.
//initialize array to account for preculling the primes of 11, 13, 17, and 19;
//(2, 3, 5, and 7 already eliminated by the bit packing to residues).
for (int pn = modPRMS.Length - 4; pn < modPRMS.Length; ++pn) {
uint p = modPRMS[pn] - 210u; ulong pt3 = p * 3;
ulong s = p * p - 23;
ulong xrng = Math.Min(9699709, nrng); // only do for the repeating master pattern size
ulong nwrds = (ulong)Math.Min(138567, this.buf.Length);
for (int j = 0; s <= xrng && j < modPRMS.Length; s += p * gapsPRMS[(pn + j++) % 48]) {
var sm = modLUT[s % 210];
var si = (sm < (1UL << 16)) ? 0UL : ((sm < (1UL << 32)) ? 1UL : 2UL);
var cd = s / 210 * 3 + si; var cm = (ushort)(sm >> (int)(si << 4));
for (ulong c = cd; c < nwrds; c += pt3) { //tight culling loop for size of master pattern
this.buf[c] |= cm; // ++this.opcnt; //reverse logic; mark composites with ones.
}
}
}
//Now copy the master pattern so it repeats across the main buffer, allow for overflow...
for (long i = 138567; i < this.buf.Length; i += 138567)
if (i + 138567 <= this.buf.Length)
Array.Copy(this.buf, 0, this.buf, i, 138567);
else Array.Copy(this.buf, 0, this.buf, i, this.buf.Length - i);
//Eliminate all composites which are factors of base primes, only for those on the wheel:
for (uint i = 0, w = 0, wi = 0, pd = 0, pn = 0, msk = 1; w < this.buf.Length; ++i) {
uint p = pd + modPRMS[pn];
ulong sqr = (ulong)p * (ulong)p;
if (sqr > range) break;
if ((this.buf[w] & msk) == 0) { //found base prime, mark its composites...
ulong s = sqr - 23; ulong pt3 = p * 3;
for (int j = 0; s <= nrng && j < modPRMS.Length; s += p * gapsPRMS[(pn + j++) % 48]) {
var sm = modLUT[s % 210];
var si = (sm < (1UL << 16)) ? 0UL : ((sm < (1UL << 32)) ? 1UL : 2UL);
var cd = s / 210 * 3 + si; var cm = (ushort)(sm >> (int)(si << 4));
for (ulong c = cd; c < (ulong)this.buf.Length; c += pt3) { //tight culling loop
this.buf[c] |= cm; // ++this.opcnt; //reverse logic; mark composites with ones.
}
}
}
++pn;
if (msk >= 0x8000) { msk = 1; ++w; ++wi; if (wi == 3) { wi = 0; pn = 0; pd += 210; } }
else msk <<= 1;
}
//clear any overflow primes in the excess space in the last wheel/word:
var ndx = nrng % 210; //clear any primes beyond the range
for (; modLUT[ndx] == 0; --ndx) ;
var cmsk = (~(modLUT[ndx] - 1)) << 1; //force all bits above to be composite ones.
this.buf[lmtwt3++] |= (ushort)cmsk;
this.buf[lmtwt3++] |= (ushort)(cmsk >> 16);
this.buf[lmtwt3] |= (ushort)(cmsk >> 32);
}
}
//uses a fast pop count Look Up Table to return the total number of primes...
public long Count {
get {
long cnt = this.cnt;
for (int i = 0; i < this.buf.Length; ++i) cnt += cntLUT[this.buf[i]];
return cnt;
}
}
//returns the number of cull operations used to sieve the prime bit array...
public long Ops {
get {
return this.opcnt;
}
}
//generate the enumeration of primes...
public IEnumerator<ulong> GetEnumerator() {
yield return 2; yield return 3; yield return 5; yield return 7;
yield return 11; yield return 13; yield return 17; yield return 19;
ulong pd = 0;
for (uint i = 0, w = 0, wi = 0, pn = 0, msk = 1; w < this.buf.Length; ++i) {
if ((this.buf[w] & msk) == 0) //found a prime bit...
yield return pd + modPRMS[pn];
++pn;
if (msk >= 0x8000) { msk = 1; ++w; ++wi; if (wi == 3) { wi = 0; pn = 0; pd += 210; } }
else msk <<= 1;
}
}
//required for the above enumeration...
IEnumerator IEnumerable.GetEnumerator() {
return this.GetEnumerator();
}
}
class Program {
static void Main(string[] args) {
Console.WriteLine("This program calculates primes by a simple maximually wheel factorized version of the Sieve of Eratosthenes.\r\n");
const ulong n = 1000000000;
var elpsd = -DateTime.Now.Ticks;
var gen = new SoE(n);
elpsd += DateTime.Now.Ticks;
Console.WriteLine("{0} primes found to {1} using {2} operations in {3} milliseconds.", gen.Count, n, gen.Ops, elpsd / 10000);
// Console.WriteLine();
// foreach (var p in gen) {
// Console.Write(p + " ");
// }
// Console.WriteLine();
// Console.WriteLine("\r\nThere are {0} primes with the last one {1} and the sum {2}.",gen.Count(),gen.Last(),gen.Sum(x => (long)x));
Console.Write("\r\nPress any key to exit:");
Console.ReadKey(true);
Console.WriteLine();
}
}
}
Этот код на самом деле работает на несколько процентов быстрее, чем приведенный выше SoA, как и должно быть, поскольку операций немного меньше, а основным узким местом для этого большого размера массива в диапазоне от миллиарда является время доступа к памяти от примерно 40 до более 100 тактовых циклов ЦП. в зависимости от характеристик процессора и памяти; это означает, что оптимизация кода (кроме уменьшения общего количества операций) неэффективна, поскольку большую часть времени тратится на ожидание доступа к памяти. В любом случае, использование огромного буфера памяти - не самый эффективный способ отсеивания больших диапазонов, с почти восьмикратным улучшением SoE с использованием сегментации страниц с той же максимальной факторизацией колеса (что также открывает путь для мультиобработка).
Именно при реализации сегментации страниц и множественной обработки SoA действительно не хватает для диапазонов, намного превышающих четыре миллиарда по сравнению с SoE, поскольку любые выгоды из-за уменьшения асимптотической сложности SoA быстро съедаются факторами накладных расходов на обработку страниц, связанных с обработка без простых квадратов и вычисление гораздо большего количества начальных адресов страницы; в качестве альтернативы можно решить эту проблему, сохраняя маркеры в оперативной памяти, что требует огромных затрат на потребление памяти и дополнительной неэффективности доступа к этим структурам хранилища маркеров. END_EDIT_ADD2
Короче говоря, SoA на самом деле не является практичным ситом по сравнению с SoE с полной колесной факторизацией, поскольку, как только выигрыш в асимптотической сложности начинает приближать его по производительности к полностью оптимизированной SoE, он начинает терять эффективность из-за подробности практической реализации в отношении относительного времени доступа к памяти и сложности сегментации страниц, а также того, что в целом сложнее и труднее написать. На мой взгляд, это скорее интересная интеллектуальная концепция и умственное упражнение, чем практическое сито по сравнению с SoE.
Когда-нибудь я адаптирую эти методы к многопоточному сегментированному Сито Эратосфена, чтобы оно было примерно таким же быстрым в C #, как "primegen" реализация SoA в 'C' Аткина и Бернштейна, и вынесу его из воды для больших диапазонов более четырех миллиардов, даже однопоточных, с дополнительным увеличением скорости примерно до четырех при многопоточности на моем i7 (восемь ядер, включая Hyper Threading).
person
GordonBGood
schedule
28.04.2014