Skip to content

Commit

Permalink
Add Primes in Beef (#953)
Browse files Browse the repository at this point in the history
  • Loading branch information
rzuckerm authored Feb 4, 2024
1 parent 1b3cc27 commit e0e6c8d
Show file tree
Hide file tree
Showing 10 changed files with 332 additions and 0 deletions.
1 change: 1 addition & 0 deletions PrimeBeef/solution_1/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
build/
8 changes: 8 additions & 0 deletions PrimeBeef/solution_1/Dockerfile
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"]
5 changes: 5 additions & 0 deletions PrimeBeef/solution_1/Primes/BeefProj.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FileVersion = 1

[Project]
Name = "Primes"
StartupObject = "Primes.Program"
5 changes: 5 additions & 0 deletions PrimeBeef/solution_1/Primes/BeefSpace.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
FileVersion = 1
Projects = {Primes = {Path = "."}}

[Workspace]
StartupProject = "Primes"
256 changes: 256 additions & 0 deletions PrimeBeef/solution_1/Primes/src/Primes.bf
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;
}
}
44 changes: 44 additions & 0 deletions PrimeBeef/solution_1/README.md
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 added PrimeBeef/solution_1/arch-amd64
Empty file.
2 changes: 2 additions & 0 deletions PrimeBeef/solution_1/build.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
docker build . -t primes_beef:latest
2 changes: 2 additions & 0 deletions PrimeBeef/solution_1/run.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
#!/bin/bash
docker run --rm primes_beef:latest "$@"
9 changes: 9 additions & 0 deletions PrimeBeef/solution_1/run_primes.sh
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

0 comments on commit e0e6c8d

Please sign in to comment.