Skip to content

Commit

Permalink
NBKPrimeSieve capacity reservation (#114).
Browse files Browse the repository at this point in the history
I made the Sieve of Eratosthenes 10% faster by adding a capacity reservation option.
  • Loading branch information
oscbyspro committed Dec 5, 2023
1 parent 02f6df7 commit 3aed644
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 27 deletions.
26 changes: 17 additions & 9 deletions Sources/NBKCoreKit/Models/NBKPrimeSieve.swift
Original file line number Diff line number Diff line change
Expand Up @@ -71,15 +71,17 @@ public final class NBKPrimeSieve: CustomStringConvertible {
///
/// - Parameter culls: A collection of cyclical patterns used to cull multiples of small primes.
///
/// - Parameter capacity: The prime buffer's minimum capacity.
///
/// - Note: A page contains `1` odd number per bit in `cache`.
///
/// - Note: The defaults strike a balance between size and performance.
///
public init(cache: Cache = .KiB(32), wheel: Wheel = .x07, culls: Culls = .x11) {
public init(cache: Cache = .KiB(32), wheel: Wheel = .x07, culls: Culls = .x11, capacity: Int? = nil) {
self.cache = cache
self.wheel = wheel
self.culls = culls
self.state = Self.makeInitialState(&self.cache, self.wheel, self.culls)
self.state = Self.makeInitialState(&self.cache, self.wheel, self.culls, capacity)
}

//=------------------------------------------------------------------------=
Expand Down Expand Up @@ -117,10 +119,12 @@ extension NBKPrimeSieve {

/// Sieves the next page of ``NBKPrimeSieve/increment`` number of values.
@inline(never) @inlinable public func increment() {
Swift.assert((self.cache.base).allSatisfy({ $0.onesComplement().isZero }))
Swift.assert(self.cache.base.allSatisfy({ $0.onesComplement().isZero }))
//=--------------------------------------=
// trap overflow and max-on-setup-sieve
//=--------------------------------------=
let start = self.limit + 0000000000000002
let limit = self.limit + self.cache.count * 2 as UInt // traps max sieve (!)
let limit = self.limit + self.cache.count * 2 as UInt
var inner = NBK.CyclicIterator(self.wheel.increments)!
//=--------------------------------------=
// mark composites not hit by the wheel
Expand Down Expand Up @@ -162,7 +166,7 @@ extension NBKPrimeSieve {
// MARK: Utilities
//=------------------------------------------------------------------------=

/// - Note: It wraps around, so preconditions should be checked prior to it.
/// - Important: It wraps around, so preconditions should be checked prior to it.
@inline(never) @inlinable static func commit(_ cache: inout Cache, to state: inout State) {
for index in cache.base.indices {
var chunk = cache.base[index]
Expand All @@ -182,7 +186,7 @@ extension NBKPrimeSieve {
}
}

@inline(never) @inlinable static func makeInitialState(_ cache: inout Cache, _ wheel: Wheel, _ culls: Culls) -> State {
@inline(never) @inlinable static func makeInitialState(_ cache: inout Cache, _ wheel: Wheel, _ culls: Culls, _ capacity: Int?) -> State {
Swift.assert(wheel.primes.first == 00000000000000002)
Swift.assert(culls.primes.first != 00000000000000002)
precondition(wheel.primes.last! <= culls.primes.last!, "must cull multiples of each element in wheel")
Expand All @@ -193,6 +197,10 @@ extension NBKPrimeSieve {
var outer = NBK.CyclicIterator(wheel.increments)!
var inner = NBK.CyclicIterator(wheel.increments)!
var state = State(limit: UInt.max, elements: [])

if let capacity {
state.elements.reserveCapacity(capacity)
}
//=--------------------------------------=
// mark each number in: 1, culls
//=--------------------------------------=
Expand Down Expand Up @@ -220,8 +228,8 @@ extension NBKPrimeSieve {

Self.commit(&cache, to: &state)

state.elements.removeLast(state.elements.reversed().prefix(while:{ $0 > state.limit }).count) // arch, culls
return state as State as State as State as State
state.elements.removeLast(state.elements.reversed().prefix(while:{ $0 > limit }).count) // if limit < culls
return state as State as State
}
}

Expand Down Expand Up @@ -564,7 +572,7 @@ extension NBKPrimeSieve {
// MARK: Utilities
//=--------------------------------------------------------------------=

/// Patterns grow multiplicatively, so chunking reduces the memory cost.
/// Patterns grow multiplicatively, so chunking reduces memory cost.
@usableFromInline static func patterns(primes: [UInt]) -> [[UInt]] {
var patterns = [[UInt]]()
var lhsIndex = primes.startIndex
Expand Down
32 changes: 16 additions & 16 deletions Tests/NBKCoreKitBenchmarks/Models/NBKPrimeSieve.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,43 +25,43 @@ final class NBKPrimeSieveBenchmarks: XCTestCase {
//=------------------------------------------------------------------------=

func testLimit1E6() {
let ((sieve)) = T(cache: .KiB(128), wheel: .x11, culls: .x31)
let ((sieve)) = T(cache: .KiB(128), wheel: .x11, culls: .x31, capacity: 0000155611)

while sieve.limit < 1000000 {
while sieve.limit < 0001000000 {
((sieve)).increment()
}

XCTAssertEqual(sieve.limit, 2097151)
XCTAssertEqual(sieve.elements.last!, 2097143)
XCTAssertEqual(sieve.elements.count, 0155611)
XCTAssertEqual(sieve.limit, 0002097151)
XCTAssertEqual(sieve.elements.last!, 0002097143)
XCTAssertEqual(sieve.elements.count, 0000155611)
}

func testLimit1E7() {
let ((sieve)) = T(cache: .KiB(128), wheel: .x11, culls: .x31)
let ((sieve)) = T(cache: .KiB(128), wheel: .x11, culls: .x31, capacity: 0000694716)

while sieve.limit < 10000000 {
while sieve.limit < 0010000000 {
((sieve)).increment()
}

XCTAssertEqual(sieve.limit, 10485759)
XCTAssertEqual(sieve.elements.last!, 10485751)
XCTAssertEqual(sieve.elements.count, 00694716)
XCTAssertEqual(sieve.limit, 0010485759)
XCTAssertEqual(sieve.elements.last!, 0010485751)
XCTAssertEqual(sieve.elements.count, 0000694716)
}

func testLimit1E8() {
let ((sieve)) = T(cache: .KiB(128), wheel: .x11, culls: .x31)
let ((sieve)) = T(cache: .KiB(128), wheel: .x11, culls: .x31, capacity: 0005797406)

while sieve.limit < 100000000 {
while sieve.limit < 0100000000 {
((sieve)).increment()
}

XCTAssertEqual(sieve.limit, 100663295)
XCTAssertEqual(sieve.elements.last!, 100663291)
XCTAssertEqual(sieve.elements.count, 005797406)
XCTAssertEqual(sieve.limit, 0100663295)
XCTAssertEqual(sieve.elements.last!, 0100663291)
XCTAssertEqual(sieve.elements.count, 0005797406)
}

func testLimit1E9() {
let ((sieve)) = T(cache: .KiB(128), wheel: .x11, culls: .x31)
let ((sieve)) = T(cache: .KiB(128), wheel: .x11, culls: .x31, capacity: 0050863957)

while sieve.limit < 1000000000 {
((sieve)).increment()
Expand Down
4 changes: 2 additions & 2 deletions Tests/NBKCoreKitTests/Models/NBKPrimeSieve.swift
Original file line number Diff line number Diff line change
Expand Up @@ -292,10 +292,10 @@ final class NBKPrimeSieveTests: XCTestCase {
//=------------------------------------------------------------------------=

func testSettings() {
let/**/ cache: T.Cache = T.Cache.words(0256 / UInt.bitWidth)
let/**/ cache: T.Cache = T.Cache.words((0256) / UInt.bitWidth)
for/**/ wheel: T.Wheel in [.x02, .x03, .x05, .x07, .x11] {
for culls: T.Culls in [.x11, .x13, .x17, .x19, .x23, .x29, .x31] {
let ((sieve)) = T(cache: cache, wheel: wheel, culls: culls)
let ((sieve)) = T(cache: cache, wheel: wheel, culls: culls, capacity: 0309)
check(sieve, limit: 0511, count: 0097, last: 0509)

sieve.increment()
Expand Down

0 comments on commit 3aed644

Please sign in to comment.