1
1
// ---------------------------------------------------------------------------
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.
5
5
// ---------------------------------------------------------------------------
6
6
7
7
#include < chrono>
@@ -26,83 +26,64 @@ const uint64_t DEFAULT_UPPER_LIMIT = 10'000'000LLU;
26
26
class BitArray {
27
27
uint8_t *array;
28
28
size_t arrSize;
29
+ size_t logicalSize;
29
30
30
- inline static size_t arraySize (size_t size)
31
+ static constexpr size_t arraySize (size_t size)
31
32
{
32
33
return (size >> 3 ) + ((size & 7 ) > 0 );
33
34
}
34
35
35
- inline static size_t index (size_t n)
36
+ static constexpr size_t index (size_t n)
36
37
{
37
38
return (n >> 3 );
38
39
}
39
40
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
-
50
41
public:
51
- explicit BitArray (size_t size) : arrSize (size)
42
+ explicit BitArray (size_t size) : logicalSize (size)
52
43
{
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));
55
48
}
56
49
57
50
~BitArray () { delete[] array; }
58
51
59
52
bool get (size_t n) const
60
53
{
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 )));
62
58
}
63
59
64
- static constexpr uint8_t rol ( uint8_t x, uint8_t n)
60
+ void set ( size_t n)
65
61
{
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 ));
71
64
}
72
65
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
-
85
66
inline size_t size () const
86
67
{
87
- return arrSize ;
68
+ return logicalSize ;
88
69
}
89
70
};
90
71
91
72
92
73
// prime_sieve
93
74
//
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.
96
77
97
78
class prime_sieve
98
79
{
99
80
private:
100
81
101
- BitArray Bits; // Sieve data, where 1 ==prime, 0 ==not
82
+ BitArray Bits; // Sieve data, where 0 ==prime, 1 ==not
102
83
103
84
public:
104
85
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
106
87
{
107
88
}
108
89
@@ -122,15 +103,21 @@ class prime_sieve
122
103
123
104
while (factor <= q)
124
105
{
125
- for (uint64_t num = factor; num < Bits.size (); num += 2 )
106
+ // Find the next prime number
107
+ for (; factor <= q; factor += 2 )
126
108
{
127
- if (Bits.get (num ))
109
+ if (Bits.get (factor ))
128
110
{
129
- factor = num;
130
111
break ;
131
112
}
132
113
}
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
+ }
134
121
135
122
factor += 2 ;
136
123
}
@@ -142,9 +129,9 @@ class prime_sieve
142
129
143
130
size_t countPrimes () const
144
131
{
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 ))
148
135
count++;
149
136
return count;
150
137
}
@@ -155,23 +142,24 @@ class prime_sieve
155
142
156
143
bool isPrime (uint64_t n) const
157
144
{
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 )
161
148
return false ;
149
+ return Bits.get (n);
162
150
}
163
151
164
152
// validateResults
165
153
//
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
167
155
// sieve processing at all, only to sanity check that the results are right when done.
168
156
169
157
bool validateResults () const
170
158
{
171
159
const std::map<const uint64_t , const int > resultsDictionary =
172
160
{
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
175
163
{ 1' 000LLU, 168 },
176
164
{ 10' 000LLU, 1229 },
177
165
{ 100' 000LLU, 9592 },
@@ -195,8 +183,8 @@ class prime_sieve
195
183
if (showResults)
196
184
cout << " 2, " ;
197
185
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 )
200
188
{
201
189
if (Bits.get (num))
202
190
{
@@ -215,7 +203,7 @@ class prime_sieve
215
203
<< " Average: " << duration/passes << " , "
216
204
<< " Limit: " << Bits.size () << " , "
217
205
<< " Counts: " << count << " /" << countPrimes () << " , "
218
- << " Valid : " << (validateResults () ? " Pass" : " FAIL!" )
206
+ << " Valid: " << (validateResults () ? " Pass" : " FAIL!" )
219
207
<< " \n " ;
220
208
221
209
// Following 2 lines added by rbergen to conform to drag race output format
@@ -322,7 +310,7 @@ int main(int argc, char **argv)
322
310
}
323
311
324
312
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;
326
314
327
315
if (bOneshot && (cSecondsRequested > 0 || cThreadsRequested > 1 ))
328
316
{
@@ -357,8 +345,8 @@ int main(int argc, char **argv)
357
345
else
358
346
{
359
347
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) ;
362
350
for (unsigned int i = 0 ; i < cThreads; i++)
363
351
threads[i] = std::thread ([i, &l_passes, &tStart](size_t llUpperLimit)
364
352
{
0 commit comments