Skip to content

Commit ed9e844

Browse files
committed
Half sieve size, flip meaning of zero and one
1 parent 6ac5045 commit ed9e844

File tree

2 files changed

+49
-61
lines changed

2 files changed

+49
-61
lines changed

PrimeCPP/solution_2/PrimeCPP_PAR.cpp

+49-61
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
// ---------------------------------------------------------------------------
2-
// PrimeCPP.cpp : Pol Marcet's Modified version of Dave's Garage Prime Sieve
3-
// Some great ideas taken from Rust's implementation from Michael Barber
4-
// @mike-barber https://www.github.com/mike-barber (bit-storage-rotate)
2+
// PrimeCPP.cpp : Optimized version of Dave's Garage Prime Sieve
3+
// Optimization: Use a BitArray that represents only odd numbers to reduce memory usage
4+
// and improve cache performance.
55
// ---------------------------------------------------------------------------
66

77
#include <chrono>
@@ -26,83 +26,64 @@ const uint64_t DEFAULT_UPPER_LIMIT = 10'000'000LLU;
2626
class BitArray {
2727
uint8_t *array;
2828
size_t arrSize;
29+
size_t logicalSize;
2930

30-
inline static size_t arraySize(size_t size)
31+
static constexpr size_t arraySize(size_t size)
3132
{
3233
return (size >> 3) + ((size & 7) > 0);
3334
}
3435

35-
inline static size_t index(size_t n)
36+
static constexpr size_t index(size_t n)
3637
{
3738
return (n >> 3);
3839
}
3940

40-
inline static uint8_t getSubindex(size_t n, uint8_t d)
41-
{
42-
return d & uint8_t(uint8_t(0x01) << (n % 8));
43-
}
44-
45-
inline void setFalseSubindex(size_t n, uint8_t &d)
46-
{
47-
d &= ~uint8_t(uint8_t(0x01) << (n % 8));
48-
}
49-
5041
public:
51-
explicit BitArray(size_t size) : arrSize(size)
42+
explicit BitArray(size_t size) : logicalSize(size)
5243
{
53-
array = new uint8_t[arraySize(size)];
54-
std::memset(array, 0xFF, arraySize(size));
44+
arrSize = (size + 1) / 2; // Only store bits for odd numbers
45+
array = new uint8_t[arraySize(arrSize)];
46+
// Bits are left at zero default, so no need to initialize them
47+
// std::memset(array, 0x00, arraySize(arrSize));
5548
}
5649

5750
~BitArray() { delete[] array; }
5851

5952
bool get(size_t n) const
6053
{
61-
return (array[index(n)] & (uint8_t(1) << (n % 8))) != 0;
54+
if (n % 2 == 0)
55+
return false; // Even numbers > 2 are not prime
56+
n = n / 2; // Map the actual number to the index in the array
57+
return !(array[index(n)] & (uint8_t(1) << (n % 8)));
6258
}
6359

64-
static constexpr uint8_t rol(uint8_t x, uint8_t n)
60+
void set(size_t n)
6561
{
66-
n %= 8;
67-
if (n == 0)
68-
return x;
69-
else
70-
return (x << n) | (x >> (8 - n));
62+
n = n / 2; // Map the actual number to the index in the array
63+
array[index(n)] |= (uint8_t(1) << (n % 8));
7164
}
7265

73-
void setFlagsFalse(size_t n, size_t skip)
74-
{
75-
auto rolling_mask = ~uint8_t(1 << n % 8);
76-
auto roll_bits = skip % 8;
77-
while (n < arrSize) {
78-
array[index(n)] &= rolling_mask;
79-
n += skip;
80-
if (roll_bits != 0)
81-
rolling_mask = rol(rolling_mask, roll_bits);
82-
}
83-
}
84-
8566
inline size_t size() const
8667
{
87-
return arrSize;
68+
return logicalSize;
8869
}
8970
};
9071

9172

9273
// prime_sieve
9374
//
94-
// Represents the data comprising the sieve (an array of N bits, where N is the upper limit prime being tested)
95-
// as well as the code needed to eliminate non-primes from its array, which you perform by calling runSieve.
75+
// Represents the data comprising the sieve (an array of bits representing odd numbers starting from 3)
76+
// and includes the code needed to eliminate non-primes from its array by calling runSieve.
9677

9778
class prime_sieve
9879
{
9980
private:
10081

101-
BitArray Bits; // Sieve data, where 1==prime, 0==not
82+
BitArray Bits; // Sieve data, where 0==prime, 1==not
10283

10384
public:
10485

105-
prime_sieve(uint64_t n) : Bits(n) // Initialize all to true (potential primes)
86+
prime_sieve(uint64_t n) : Bits(n) // Initialize bits to zero default
10687
{
10788
}
10889

@@ -122,15 +103,21 @@ class prime_sieve
122103

123104
while (factor <= q)
124105
{
125-
for (uint64_t num = factor; num < Bits.size(); num += 2)
106+
// Find the next prime number
107+
for (; factor <= q; factor += 2)
126108
{
127-
if (Bits.get(num))
109+
if (Bits.get(factor))
128110
{
129-
factor = num;
130111
break;
131112
}
132113
}
133-
Bits.setFlagsFalse(factor * factor, factor + factor);
114+
115+
// Mark multiples of the prime number as not prime
116+
uint64_t start = factor * factor;
117+
for (uint64_t num = start; num <= Bits.size(); num += factor * 2)
118+
{
119+
Bits.set(num);
120+
}
134121

135122
factor += 2;
136123
}
@@ -142,9 +129,9 @@ class prime_sieve
142129

143130
size_t countPrimes() const
144131
{
145-
size_t count = (Bits.size() >= 2); // Count 2 as prime if within range
146-
for (int i = 3; i < Bits.size(); i+=2)
147-
if (Bits.get(i))
132+
size_t count = (Bits.size() >= 2); // Count 2 as prime if within range
133+
for (uint64_t num = 3; num <= Bits.size(); num += 2)
134+
if (Bits.get(num))
148135
count++;
149136
return count;
150137
}
@@ -155,23 +142,24 @@ class prime_sieve
155142

156143
bool isPrime(uint64_t n) const
157144
{
158-
if (n & 1)
159-
return Bits.get(n);
160-
else
145+
if (n == 2)
146+
return true;
147+
if (n < 2 || n % 2 == 0)
161148
return false;
149+
return Bits.get(n);
162150
}
163151

164152
// validateResults
165153
//
166-
// Checks to see if the number of primes found matches what we should expect. This data isn't used in the
154+
// Checks to see if the number of primes found matches what we should expect. This data isn't used in the
167155
// sieve processing at all, only to sanity check that the results are right when done.
168156

169157
bool validateResults() const
170158
{
171159
const std::map<const uint64_t, const int> resultsDictionary =
172160
{
173-
{ 10LLU, 4 }, // Historical data for validating our results - the number of primes
174-
{ 100LLU, 25 }, // to be found under some limit, such as 168 primes under 1000
161+
{ 10LLU, 4 }, // Historical data for validating our results - the number of primes
162+
{ 100LLU, 25 }, // to be found under some limit, such as 168 primes under 1000
175163
{ 1'000LLU, 168 },
176164
{ 10'000LLU, 1229 },
177165
{ 100'000LLU, 9592 },
@@ -195,8 +183,8 @@ class prime_sieve
195183
if (showResults)
196184
cout << "2, ";
197185

198-
size_t count = (Bits.size() >= 2); // Count 2 as prime if in range
199-
for (uint64_t num = 3; num <= Bits.size(); num+=2)
186+
size_t count = (Bits.size() >= 2); // Count 2 as prime if in range
187+
for (uint64_t num = 3; num <= Bits.size(); num += 2)
200188
{
201189
if (Bits.get(num))
202190
{
@@ -215,7 +203,7 @@ class prime_sieve
215203
<< "Average: " << duration/passes << ", "
216204
<< "Limit: " << Bits.size() << ", "
217205
<< "Counts: " << count << "/" << countPrimes() << ", "
218-
<< "Valid : " << (validateResults() ? "Pass" : "FAIL!")
206+
<< "Valid: " << (validateResults() ? "Pass" : "FAIL!")
219207
<< "\n";
220208

221209
// Following 2 lines added by rbergen to conform to drag race output format
@@ -322,7 +310,7 @@ int main(int argc, char **argv)
322310
}
323311

324312
if (bOneshot)
325-
cout << "Oneshot is on. A single pass will be used to simulate a 5 second run." << endl;
313+
cout << "Oneshot is on. A single pass will be used to simulate a 5 second run." << endl;
326314

327315
if (bOneshot && (cSecondsRequested > 0 || cThreadsRequested > 1))
328316
{
@@ -357,8 +345,8 @@ int main(int argc, char **argv)
357345
else
358346
{
359347
auto tStart = steady_clock::now();
360-
std::thread threads[cThreads];
361-
uint64_t l_passes[cThreads];
348+
std::vector<std::thread> threads(cThreads);
349+
std::vector<uint64_t> l_passes(cThreads);
362350
for (unsigned int i = 0; i < cThreads; i++)
363351
threads[i] = std::thread([i, &l_passes, &tStart](size_t llUpperLimit)
364352
{

PrimeCPP/solution_2/primes_par.exe

60.8 KB
Binary file not shown.

0 commit comments

Comments
 (0)