diff --git a/PrimeOdin/solution_2/BitSieve.odin b/PrimeOdin/solution_2/BitSieve.odin new file mode 100644 index 000000000..0cb1b00f3 --- /dev/null +++ b/PrimeOdin/solution_2/BitSieve.odin @@ -0,0 +1,70 @@ +package prime_drag_race + +import "base:intrinsics" +import "core:container/bit_array" +import "core:math" +import "core:fmt" +import "core:time" +import "core:mem" + +BitSieve :: struct { + size : int, + bits : ^bit_array.Bit_Array +} + +RunBitSieve :: proc( sieveSize : int) -> (result :BitSieve) +{ + factor := 3 + + bitSieve := bit_array.create( sieveSize) + // q := int( math.sqrt_f64( f64(sieveSize))) + + for factor*factor <= sieveSize { // faster than "factor <= q" + // find the first "confirmed" prime in the Sieve + #no_bounds_check { + for num := factor; num < sieveSize; num += 2 { + if !bit_array.unsafe_get( bitSieve, num) { + factor = num + break; + } + } + + // then "nullify" the subsequent multiples of that prime. + step := factor * 2 + for num := factor * factor; num < sieveSize; num += step { + bit_array.unsafe_set( bitSieve, num) + } + + factor += 2 + } + } + result.size = sieveSize + result.bits = bitSieve + + return +} + +GoBitSieve :: proc ( sieveSize : int) +{ + fiveSecs :: time.Duration(5_000_000_000) // nano seconds + + passCount := 0 + + timer : time.Stopwatch + time.stopwatch_start( &timer) + defer time.stopwatch_stop( &timer) + + for { + bitSieve := RunBitSieve( sieveSize) + passCount += 1 + + duration := time.stopwatch_duration( timer) + if duration > fiveSecs { + fmt.printfln( "arenol;%d;%.5f;1;algorithm=base,faithful=yes,bits=1", passCount, f64(duration) * 1.0e-9) + bit_array.destroy( bitSieve.bits) + break + } + bit_array.destroy( bitSieve.bits) + } + +} diff --git a/PrimeOdin/solution_2/ByteSieve.odin b/PrimeOdin/solution_2/ByteSieve.odin new file mode 100644 index 000000000..4f5df05ee --- /dev/null +++ b/PrimeOdin/solution_2/ByteSieve.odin @@ -0,0 +1,114 @@ +package prime_drag_race + +import "core:fmt" +import "core:time" +import "core:mem" + + +ByteSieve :: struct { + size : int, + bits : []bool, +} + +RunByteSieve :: proc( size :int) -> (result :ByteSieve) +{ + upper := size / 2; + theSieve : []bool = make( []bool, upper) + + for factor := 3; factor*factor < size; factor +=2 { + #no_bounds_check { // speeds up the algorithm with a factor of 2.5 + // find the first "confirmed" prime in the Sieve + for num := factor/2; num < upper; num += 1 { + if !theSieve[num] { + factor = num * 2 + 1 + break; + } + } + + // then "nullify" the subsequent multiples of that prime. + for num := factor * factor / 2; num < upper; num += factor { + theSieve[num] = true + } + + } + } + result.size = size + result.bits = theSieve + return +} + +PrintPrimes :: proc( calculatedSieve : []bool ) +{ + fmt.print( "2") + + for b, i in calculatedSieve[1:] { + if !b { + fmt.print( ",", i*2+3) + } + } + + fmt.println( "") +} + +CountPrimes :: proc( calculatedSieve :[]bool) -> int +{ + n := len( calculatedSieve) + count := sieveSize >= 2 ? 1 : 0 + for b in calculatedSieve[1:] { + if !b { + count += 1 + } + } + return count +} + +ExpectedPrimeCount :: proc( siveSize :int) -> int{ + switch sieveSize { + case 10: return 4 + case 100: return 25 + case 1_000: return 168 + case 10_000: return 1_229 + case 100_000: return 9_592 + case 1_000_000: return 78_498 + case 10_000_000: return 664_579 + case 100_000_000: return 5_761_455 + } + panic("Invalid sieve size") +} + + +GoByteSieve :: proc( sieveSize : int) +{ + + fiveSecs :: time.Duration(5_000_000_000) // nano seconds + + passCount := 0 + + /* + note that the default value of bool in odin is false. + rather than initialize the sieve with trues and make them + false, as we go, we reverse the logic. + */ + + + timer : time.Stopwatch + time.stopwatch_start( &timer) + defer time.stopwatch_stop( &timer) + + for { + theSieve := RunByteSieve( sieveSize) + passCount += 1 + + duration := time.stopwatch_duration( timer) + if duration > fiveSecs { + assert( CountPrimes( theSieve.bits[:]) == ExpectedPrimeCount( theSieve.size)) + fmt.printfln( "arenol;%d,%.5f;1;algorithm=base,faithful=yes,bits=8", passCount, f64(duration) * 1.0e-9) + // PrintPrimes( theSieve.bits[:]) + free( (^rawptr)(&theSieve.bits)^) + break + } + free( (^rawptr)(&theSieve.bits)^) + } + + +} \ No newline at end of file diff --git a/PrimeOdin/solution_2/Dockerfile b/PrimeOdin/solution_2/Dockerfile new file mode 100644 index 000000000..ea1407042 --- /dev/null +++ b/PrimeOdin/solution_2/Dockerfile @@ -0,0 +1,27 @@ +# Dockerfile to build an environment for running Odin code on Ubuntu Linux. + +FROM ubuntu:latest + +RUN apt-get update -qq && \ + apt-get install -y llvm clang git build-essential + +WORKDIR /odin-build + +RUN git clone https://github.com/odin-lang/Odin.git /odin-build && \ + git checkout dev-2025-08 && \ + make + +RUN mkdir /opt/odin && \ + cp -R ./base ./core ./shared ./vendor ./odin /opt/odin + +WORKDIR / +RUN rm -rf /odin-build + +ENV PATH="/opt/odin:${PATH}" + +WORKDIR /app +COPY *.odin /app + +RUN odin build . -o:speed -out:odinprimes + +CMD ./odinprimes \ No newline at end of file diff --git a/PrimeOdin/solution_2/README.md b/PrimeOdin/solution_2/README.md new file mode 100644 index 000000000..94867fd6d --- /dev/null +++ b/PrimeOdin/solution_2/README.md @@ -0,0 +1,26 @@ +# Odin solution by Agnar Renolen +Just implemented the original C++ implementation in Odin. + +Could not compile the multithreaded and bit versions of Ginger Bill and Kelimion's solution with my current odin installation (version dev-2025-08) on my laptop. But I got a significant improvement on the single-threaded byte version. + +The bit version was just an experiment to figure out how the *core:collection/byte_array* package would perform. Surprisingly well, I'd say. + +## Tested variations +Rather than allocate and deallocate the sieve for each iteration, I tried to create one sieve, and reset it by "memcopy-ing" from a blank sieve between each iteration. I was surprised to see that this was slightly slower. + +## Run instructions +This solution was developed under odin *dev-2025-8*. It should work on later versions, and may also work on some earlier versions, though the *bit_array* package implementation used in *dev-2025-8* is quite new. + +You should run the program using **odin run . -o:speed**. +I also tried the *aggressive* optimization without any improvement. + +## Output +On my laptop: + + arenol;8377,5.0000;1;algorithm=base,faithful=yes,bits=8 + arenol;7731;5.0006;1;algorithm=base,faithful=yes,bits=1 + +Compared to Ginger Bill and Kelimion's solution with the same laptop and compiler: + + odin_byte_moe;7461;5.0005955;1;algorithm=base,faithful=yes,bits=8 + diff --git a/PrimeOdin/solution_2/main.odin b/PrimeOdin/solution_2/main.odin new file mode 100644 index 000000000..de679fe58 --- /dev/null +++ b/PrimeOdin/solution_2/main.odin @@ -0,0 +1,14 @@ + +package prime_drag_race + +import "core:math" + +sieveSize :: 1_000_000 + +// calculate q once and for all + +main :: proc() +{ + GoByteSieve( sieveSize) + GoBitSieve( sieveSize) +} \ No newline at end of file