-
Notifications
You must be signed in to change notification settings - Fork 574
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
332 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
build/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
FROM rzuckerm/beef:0.43.5a2-4 | ||
|
||
WORKDIR /opt/app | ||
COPY *.sh ./ | ||
COPY Primes/*.toml ./Primes/ | ||
COPY Primes/src/*.bf ./Primes/src/ | ||
RUN BeefBuild -workspace=Primes -config=Release | ||
ENTRYPOINT ["./run_primes.sh"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
FileVersion = 1 | ||
|
||
[Project] | ||
Name = "Primes" | ||
StartupObject = "Primes.Program" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
FileVersion = 1 | ||
Projects = {Primes = {Path = "."}} | ||
|
||
[Workspace] | ||
StartupProject = "Primes" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
using System; | ||
using System.Diagnostics; | ||
|
||
namespace Primes; | ||
|
||
class PrimeSieve | ||
{ | ||
public readonly uint64 mSieveSize; | ||
public readonly uint64 mNumBits; | ||
private uint32[] mSieveBits ~ delete _; | ||
|
||
public this(uint64 sieveSize) | ||
{ | ||
mSieveSize = sieveSize; | ||
mNumBits = (sieveSize - 1) / 2; | ||
int numItems = (int)((mNumBits + 31) / 32); | ||
mSieveBits = new uint32[numItems]; | ||
Internal.MemSet(mSieveBits.CArray(), 0xff, sizeof(uint32) * numItems); | ||
} | ||
|
||
[Optimize] | ||
[DisableChecks] | ||
public void Sieve() | ||
{ | ||
uint64 q = (uint64)((-3 + Math.Sqrt(3 + 2 * mNumBits)) / 2); | ||
for (uint64 bit = 0; bit <= q; bit++) | ||
{ | ||
if (GetBit(bit)) | ||
{ | ||
ClearBits(2 * (bit + 1) * (bit + 2) - 1, 2 * bit + 3); | ||
} | ||
} | ||
} | ||
|
||
[Optimize] | ||
[DisableChecks] | ||
[Inline] | ||
private bool GetBit(uint64 bit) | ||
{ | ||
return mSieveBits[(int)(bit >> 5)] & (1 << (bit & 0x1f)) != 0; | ||
} | ||
|
||
[Optimize] | ||
[DisableChecks] | ||
[Inline] | ||
private void ClearBits(uint64 startBit, uint64 inc) | ||
{ | ||
int bit = (int)(startBit & 0x1f); | ||
int index = (int)(startBit >> 5); | ||
int bitInc = (int)(inc & 0x1f); | ||
int indexInc = (int)(inc >> 5); | ||
for (uint64 currBit = startBit; currBit < mNumBits; currBit += inc) | ||
{ | ||
mSieveBits[index] &= ~(1 << bit); | ||
bit += bitInc; | ||
index += indexInc; | ||
if (bit >= 32) { | ||
bit -= 32; | ||
index++; | ||
} | ||
} | ||
} | ||
|
||
public uint64 CountPrimes(bool showResults) | ||
{ | ||
uint64 numPrimes = 0; | ||
if (mSieveSize >= 2) | ||
{ | ||
numPrimes++; | ||
if (showResults) | ||
{ | ||
Console.Write("2, "); | ||
} | ||
} | ||
|
||
for (uint64 bit = 0; bit < mNumBits; bit++) | ||
{ | ||
if (GetBit(bit)) | ||
{ | ||
numPrimes++; | ||
if (showResults) | ||
{ | ||
Console.Write($"{2 * bit + 3}, "); | ||
} | ||
} | ||
} | ||
|
||
Console.WriteLine(); | ||
|
||
return numPrimes; | ||
} | ||
|
||
public uint64 GetExpectedPrimesCount() | ||
{ | ||
switch (mSieveSize) | ||
{ | ||
case 10: return 4; | ||
case 100: return 25; | ||
case 1000: return 168; | ||
case 10000: return 1229; | ||
case 100000: return 9592; | ||
case 1000000: return 78498; | ||
case 10000000: return 664579; | ||
case 100000000: return 5761455; | ||
case 1000000000: return 50847534; | ||
case 10000000000: return 455052511; | ||
} | ||
|
||
return 0; | ||
} | ||
|
||
public bool ValidatePrimesCount(uint64 primesCount) | ||
{ | ||
return primesCount == GetExpectedPrimesCount(); | ||
} | ||
|
||
public void ShowResults(bool showResults, uint64 passes, int64 elapsedTimeMsec) | ||
{ | ||
uint64 primeCount = CountPrimes(showResults); | ||
bool valid = ValidatePrimesCount(primeCount); | ||
Console.WriteLine( | ||
"Passes: {}, Time: {}ms, Avg: {}ms, Limit: {}, Count: {}, Valid: {}", | ||
passes, | ||
elapsedTimeMsec, | ||
(double)elapsedTimeMsec / passes, | ||
mSieveSize, | ||
primeCount, | ||
valid | ||
); | ||
Console.WriteLine( | ||
"rzuckerm;{};{};1;algorithm=base,faithful=yes,bits=1", | ||
passes, | ||
(double)elapsedTimeMsec / 1000.0 | ||
); | ||
} | ||
} | ||
|
||
struct PrimeOptions | ||
{ | ||
public uint64 sieveSize = 1'000'000; | ||
public uint32 timeLimit = 5; | ||
public bool showResults = false; | ||
} | ||
|
||
class Program | ||
{ | ||
public static void ShowHelp(StringView errorMessage="") | ||
{ | ||
int statusCode = 0; | ||
if (errorMessage.Length > 0) | ||
{ | ||
Console.WriteLine(errorMessage); | ||
Console.WriteLine(); | ||
statusCode = 1; | ||
} | ||
|
||
Console.WriteLine("Options:\n"); | ||
Console.WriteLine("--limit=<sieveSize> Upper limit for calculating primes"); | ||
Console.WriteLine("--time=<timeLimit> Time limit in seconds"); | ||
Console.WriteLine("--show Print found prime numbers"); | ||
Console.WriteLine("--help Show help message"); | ||
Environment.Exit(statusCode); | ||
} | ||
|
||
public static Result<T> ParseInt<T>(StringView str) | ||
where T : IParseable<T> | ||
{ | ||
StringView trimmedStr = scope String(str); | ||
trimmedStr.Trim(); | ||
return T.Parse(trimmedStr); | ||
} | ||
|
||
public static T ParseIntOrDie<T>(StringView str, StringView errorMessage) | ||
where T : IParseable<T> | ||
{ | ||
T val = default(T); | ||
switch (ParseInt<T>(str)) | ||
{ | ||
case .Err: | ||
Console.WriteLine(errorMessage); | ||
Environment.Exit(1); | ||
case .Ok(out val): | ||
} | ||
|
||
return val; | ||
} | ||
|
||
public static PrimeOptions ParseArgs(String[] args) | ||
{ | ||
PrimeOptions options = PrimeOptions(); | ||
for (String arg in args) | ||
{ | ||
int index = arg.IndexOf('='); | ||
StringView optionName = ""; | ||
StringView optionValue = ""; | ||
if (index < 0) | ||
{ | ||
optionName = arg; | ||
} | ||
else | ||
{ | ||
optionName = arg.Substring(0, index); | ||
optionValue = arg.Substring(index + 1); | ||
} | ||
|
||
if (optionName == "--limit") | ||
{ | ||
options.sieveSize = ParseIntOrDie<uint64>(optionValue, @"{arg}: Invalid sieve size"); | ||
} | ||
else if (optionName == "--time") | ||
{ | ||
options.timeLimit = ParseIntOrDie<uint32>(optionValue, @"{arg}: Invalid time limit"); | ||
} | ||
else if (arg == "--show") | ||
{ | ||
options.showResults = true; | ||
} | ||
else if (arg == "--help") | ||
{ | ||
ShowHelp(); | ||
} | ||
else | ||
{ | ||
ShowHelp(@"Invalid option '{arg}'"); | ||
} | ||
} | ||
|
||
return options; | ||
} | ||
|
||
public static void TimedRunSieve(PrimeOptions options) | ||
{ | ||
uint64 passes = 0; | ||
int64 timeLimitMsec = (int64)options.timeLimit * 1000; | ||
Stopwatch watch = scope Stopwatch(true); | ||
while (true) | ||
{ | ||
passes++; | ||
PrimeSieve sieve = scope PrimeSieve(options.sieveSize); | ||
sieve.Sieve(); | ||
if (watch.ElapsedMilliseconds >= timeLimitMsec) | ||
{ | ||
watch.Stop(); | ||
sieve.ShowResults(options.showResults, passes, watch.ElapsedMilliseconds); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
public static int Main(String[] args) | ||
{ | ||
PrimeOptions options = ParseArgs(args); | ||
TimedRunSieve(options); | ||
return 0; | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
# Beef solution by rzuckerm | ||
|
||
![Algorithm](https://img.shields.io/badge/Algorithm-base-green) | ||
![Faithfulness](https://img.shields.io/badge/Faithful-yes-green) | ||
![Parallelism](https://img.shields.io/badge/Parallel-no-green) | ||
![Bit count](https://img.shields.io/badge/Bits-1-green) | ||
|
||
* `Primes/src/Primes.bf` uses 1 bit for each sieve item | ||
|
||
## Caveats | ||
|
||
The docker image only works on an AMD64 architecture. I was not able to get the | ||
[Beef docker image](https://hub.docker.com/r/rzuckerm/beef) to build using ARM64. | ||
|
||
## Run instructions | ||
|
||
Build the docker image with this: | ||
|
||
```bash | ||
./build.sh | ||
``` | ||
|
||
You should only need to do this once. Run the docker image: | ||
|
||
```bash | ||
./run.sh [<options>] | ||
``` | ||
|
||
where `<options>` are optional command-line arguments: | ||
|
||
* `--limit=<limit>` - Upper limit for calculating prime. Default value is 1000000 | ||
* `--time=<time>` - Time limit in seconds. Default value is 5 | ||
* `--show` - Print found prime numbers | ||
* `--help` - Show help | ||
|
||
## Output | ||
|
||
On a 12th Gen Intel(R) Core(TM) i7-12850HX 2.10 GHz with 32 GB of memory on a Windows 10 | ||
laptop running a Ubuntu 22.04 VM in VirtualBox 7.0.6: | ||
|
||
``` | ||
Passes: 7401, Time: 5000ms, Avg: 0.6755843805ms, Limit: 1000000, Count: 78498, Valid: true | ||
rzuckerm;7041;5;1;algorithm=base,faithful=yes,bits=1 | ||
``` |
Empty file.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#!/bin/bash | ||
docker build . -t primes_beef:latest |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
#!/bin/bash | ||
docker run --rm primes_beef:latest "$@" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
#!/bin/bash | ||
ARCH="$(uname -m)" | ||
case "${ARCH}" in | ||
x86_64) ./Primes/build/Release_Linux64/Primes/Primes "$@" | ||
;; | ||
*) | ||
echo "Unknown architecture ${ARCH}" | ||
;; | ||
esac |