Skip to content

Commit

Permalink
Half sieve size, flip meaning of zero and one
Browse files Browse the repository at this point in the history
  • Loading branch information
davepl committed Oct 19, 2024
1 parent 6ac5045 commit ed9e844
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 61 deletions.
110 changes: 49 additions & 61 deletions PrimeCPP/solution_2/PrimeCPP_PAR.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// ---------------------------------------------------------------------------
// PrimeCPP.cpp : Pol Marcet's Modified version of Dave's Garage Prime Sieve
// Some great ideas taken from Rust's implementation from Michael Barber
// @mike-barber https://www.github.com/mike-barber (bit-storage-rotate)
// PrimeCPP.cpp : Optimized version of Dave's Garage Prime Sieve
// Optimization: Use a BitArray that represents only odd numbers to reduce memory usage
// and improve cache performance.
// ---------------------------------------------------------------------------

#include <chrono>
Expand All @@ -26,83 +26,64 @@ const uint64_t DEFAULT_UPPER_LIMIT = 10'000'000LLU;
class BitArray {
uint8_t *array;
size_t arrSize;
size_t logicalSize;

inline static size_t arraySize(size_t size)
static constexpr size_t arraySize(size_t size)
{
return (size >> 3) + ((size & 7) > 0);
}

inline static size_t index(size_t n)
static constexpr size_t index(size_t n)
{
return (n >> 3);
}

inline static uint8_t getSubindex(size_t n, uint8_t d)
{
return d & uint8_t(uint8_t(0x01) << (n % 8));
}

inline void setFalseSubindex(size_t n, uint8_t &d)
{
d &= ~uint8_t(uint8_t(0x01) << (n % 8));
}

public:
explicit BitArray(size_t size) : arrSize(size)
explicit BitArray(size_t size) : logicalSize(size)
{
array = new uint8_t[arraySize(size)];
std::memset(array, 0xFF, arraySize(size));
arrSize = (size + 1) / 2; // Only store bits for odd numbers
array = new uint8_t[arraySize(arrSize)];
// Bits are left at zero default, so no need to initialize them
// std::memset(array, 0x00, arraySize(arrSize));
}

~BitArray() { delete[] array; }

bool get(size_t n) const
{
return (array[index(n)] & (uint8_t(1) << (n % 8))) != 0;
if (n % 2 == 0)
return false; // Even numbers > 2 are not prime
n = n / 2; // Map the actual number to the index in the array
return !(array[index(n)] & (uint8_t(1) << (n % 8)));
}

static constexpr uint8_t rol(uint8_t x, uint8_t n)
void set(size_t n)
{
n %= 8;
if (n == 0)
return x;
else
return (x << n) | (x >> (8 - n));
n = n / 2; // Map the actual number to the index in the array
array[index(n)] |= (uint8_t(1) << (n % 8));
}

void setFlagsFalse(size_t n, size_t skip)
{
auto rolling_mask = ~uint8_t(1 << n % 8);
auto roll_bits = skip % 8;
while (n < arrSize) {
array[index(n)] &= rolling_mask;
n += skip;
if (roll_bits != 0)
rolling_mask = rol(rolling_mask, roll_bits);
}
}

inline size_t size() const
{
return arrSize;
return logicalSize;
}
};


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

class prime_sieve
{
private:

BitArray Bits; // Sieve data, where 1==prime, 0==not
BitArray Bits; // Sieve data, where 0==prime, 1==not

public:

prime_sieve(uint64_t n) : Bits(n) // Initialize all to true (potential primes)
prime_sieve(uint64_t n) : Bits(n) // Initialize bits to zero default
{
}

Expand All @@ -122,15 +103,21 @@ class prime_sieve

while (factor <= q)
{
for (uint64_t num = factor; num < Bits.size(); num += 2)
// Find the next prime number
for (; factor <= q; factor += 2)
{
if (Bits.get(num))
if (Bits.get(factor))
{
factor = num;
break;
}
}
Bits.setFlagsFalse(factor * factor, factor + factor);

// Mark multiples of the prime number as not prime
uint64_t start = factor * factor;
for (uint64_t num = start; num <= Bits.size(); num += factor * 2)
{
Bits.set(num);
}

factor += 2;
}
Expand All @@ -142,9 +129,9 @@ class prime_sieve

size_t countPrimes() const
{
size_t count = (Bits.size() >= 2); // Count 2 as prime if within range
for (int i = 3; i < Bits.size(); i+=2)
if (Bits.get(i))
size_t count = (Bits.size() >= 2); // Count 2 as prime if within range
for (uint64_t num = 3; num <= Bits.size(); num += 2)
if (Bits.get(num))
count++;
return count;
}
Expand All @@ -155,23 +142,24 @@ class prime_sieve

bool isPrime(uint64_t n) const
{
if (n & 1)
return Bits.get(n);
else
if (n == 2)
return true;
if (n < 2 || n % 2 == 0)
return false;
return Bits.get(n);
}

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

bool validateResults() const
{
const std::map<const uint64_t, const int> resultsDictionary =
{
{ 10LLU, 4 }, // Historical data for validating our results - the number of primes
{ 100LLU, 25 }, // to be found under some limit, such as 168 primes under 1000
{ 10LLU, 4 }, // Historical data for validating our results - the number of primes
{ 100LLU, 25 }, // to be found under some limit, such as 168 primes under 1000
{ 1'000LLU, 168 },
{ 10'000LLU, 1229 },
{ 100'000LLU, 9592 },
Expand All @@ -195,8 +183,8 @@ class prime_sieve
if (showResults)
cout << "2, ";

size_t count = (Bits.size() >= 2); // Count 2 as prime if in range
for (uint64_t num = 3; num <= Bits.size(); num+=2)
size_t count = (Bits.size() >= 2); // Count 2 as prime if in range
for (uint64_t num = 3; num <= Bits.size(); num += 2)
{
if (Bits.get(num))
{
Expand All @@ -215,7 +203,7 @@ class prime_sieve
<< "Average: " << duration/passes << ", "
<< "Limit: " << Bits.size() << ", "
<< "Counts: " << count << "/" << countPrimes() << ", "
<< "Valid : " << (validateResults() ? "Pass" : "FAIL!")
<< "Valid: " << (validateResults() ? "Pass" : "FAIL!")
<< "\n";

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

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

if (bOneshot && (cSecondsRequested > 0 || cThreadsRequested > 1))
{
Expand Down Expand Up @@ -357,8 +345,8 @@ int main(int argc, char **argv)
else
{
auto tStart = steady_clock::now();
std::thread threads[cThreads];
uint64_t l_passes[cThreads];
std::vector<std::thread> threads(cThreads);
std::vector<uint64_t> l_passes(cThreads);
for (unsigned int i = 0; i < cThreads; i++)
threads[i] = std::thread([i, &l_passes, &tStart](size_t llUpperLimit)
{
Expand Down
Binary file added PrimeCPP/solution_2/primes_par.exe
Binary file not shown.

0 comments on commit ed9e844

Please sign in to comment.