Faster Sieve of EratosthenesPrime Number Generator in SwiftSieve of Eratosthenes - PythonSieve of...

Gears on left are inverse to gears on right?

Sequence of Tenses: Translating the subjunctive

How do I find the solutions of the following equation?

Term for the "extreme-extension" version of a straw man fallacy?

Do sorcerers' subtle spells require a skill check to be unseen?

Why didn't Theresa May consult with Parliament before negotiating a deal with the EU?

What is the best translation for "slot" in the context of multiplayer video games?

Purchasing a ticket for someone else in another country?

Detecting if an element is found inside a container

Why are there no referendums in the US?

How does Loki do this?

Is `x >> pure y` equivalent to `liftM (const y) x`

Short story about space worker geeks who zone out by 'listening' to radiation from stars

Is exact Kanji stroke length important?

Sort a list by elements of another list

How can a function with a hole (removable discontinuity) equal a function with no hole?

What is the difference between "behavior" and "behaviour"?

How to check is there any negative term in a large list?

Do all network devices need to make routing decisions, regardless of communication across networks or within a network?

Hostile work environment after whistle-blowing on coworker and our boss. What do I do?

CREATE opcode: what does it really do?

What does the word "Atten" mean?

What is the opposite of 'gravitas'?

Applicability of Single Responsibility Principle



Faster Sieve of Eratosthenes


Prime Number Generator in SwiftSieve of Eratosthenes - PythonSieve of Erathosthenes speedupSieve of Eratosthenes - segmented to increase speed and rangeSieve32Fast - A very fast, memory efficient, multi-threaded Sieve of EratosthenesSieve of Eratosthenes Primes EfficiencyUnbounded Sieve of Eratosthenes in SwiftPostponed Prime Sieve in SwiftMaximum performance for Pollard's P-1 functionFunctional Sieve of Eratosthenes in ClojureSieve of Eratosthenes in Rust













2












$begingroup$


This is an implementation of the Sieve of Eratosthenes :




  • It takes advantages of the fact that all primes from 5 and above can be written as 6X-1 or 6X+1,


  • For better space complexity, it uses a pretty accurate upperbound. Better estimations of the upper bound can be found here. I've observed a very slight increase in performance with this.





func eratosthenesSieve(to n: Int) -> [Int] {

guard 2 <= n else { return [] }

var composites = Array(repeating: false, count: n + 1)
var primes: [Int] = []
let d = Double(n)
let upperBound = Int((d / log(d)) * (1.0 + 1.2762/log(d)))
primes.reserveCapacity(upperBound)
let squareRootN = Int(d.squareRoot())

//2 and 3
var p = 2
let twoOrThree = min(n, 3)
while p <= twoOrThree {
primes.append(p)
var q = p * p
let step = p * (p - 1)
while q <= n {
composites[q] = true
q += step
}
p += 1
}

//5 and above
p += 1
while p <= squareRootN {
for i in 0..<2 {
let nbr = p + 2 * i
if !composites[nbr] {
primes.append(nbr)
var q = nbr * nbr
var coef = 2 * (i + 1)
while q <= n {
composites[q] = true
q += coef * nbr
coef = 6 - coef
}
}
}
p += 6
}

while p <= n {
for i in 0..<2 {
let nbr = p + 2 * i
if nbr <= n && !composites[nbr] {
primes.append(nbr)
}
}
p += 6
}

return primes
}




It was inspired by this code by Mr Martin.



Using the same benchmarking code in that answer, adding a fourth fractional digit in the timing results, plus some formatting, here are the results :



        ---------------------------------------------------------------
| | Nbr | Time (sec) |
| Up to | of |------------------------------|
| | Primes | Martin's | This |
|----------------|-------------|------------------------------|
| 100_000 | 9592 | 0.0008 | 0.0004 |
|----------------|-------------|--------------|---------------|
| 1_000_000 | 78_498 | 0.0056 | 0.0026 |
|----------------|-------------|--------------|---------------|
| 10_000_000 | 664_579 | 0.1233 | 0.0426 |
|----------------|-------------|--------------|---------------|
| 100_000_000 | 5_761_455 | 1.0976 | 0.5089 |
|----------------|-------------|--------------|---------------|
| 1_000_000_000 | 50_847_534 | 12.1328 | 5.9759 |
|----------------|-------------|--------------|---------------|
| 10_000_000_000 | 455_052_511 | 165.5658 | 84.5477 |
|----------------|-------------|--------------|---------------|


Using Attabench, here is a visual representation of the performance of both codes while n is less than 2^16:



Attabench results





One thing I observe is some elements in the composites array are marked with true multiple times. This is expected (but unwanted) behavior since 6X-1 or 6X+1 aren't all primes.



What I'm looking for is making this Sieve of Eratosthenes quicker. I'm well aware of faster methods of finding primes.



Naming, code clarity, conciseness, consistency, etc, are welcome but are not the main point here.










share|improve this question











$endgroup$

















    2












    $begingroup$


    This is an implementation of the Sieve of Eratosthenes :




    • It takes advantages of the fact that all primes from 5 and above can be written as 6X-1 or 6X+1,


    • For better space complexity, it uses a pretty accurate upperbound. Better estimations of the upper bound can be found here. I've observed a very slight increase in performance with this.





    func eratosthenesSieve(to n: Int) -> [Int] {

    guard 2 <= n else { return [] }

    var composites = Array(repeating: false, count: n + 1)
    var primes: [Int] = []
    let d = Double(n)
    let upperBound = Int((d / log(d)) * (1.0 + 1.2762/log(d)))
    primes.reserveCapacity(upperBound)
    let squareRootN = Int(d.squareRoot())

    //2 and 3
    var p = 2
    let twoOrThree = min(n, 3)
    while p <= twoOrThree {
    primes.append(p)
    var q = p * p
    let step = p * (p - 1)
    while q <= n {
    composites[q] = true
    q += step
    }
    p += 1
    }

    //5 and above
    p += 1
    while p <= squareRootN {
    for i in 0..<2 {
    let nbr = p + 2 * i
    if !composites[nbr] {
    primes.append(nbr)
    var q = nbr * nbr
    var coef = 2 * (i + 1)
    while q <= n {
    composites[q] = true
    q += coef * nbr
    coef = 6 - coef
    }
    }
    }
    p += 6
    }

    while p <= n {
    for i in 0..<2 {
    let nbr = p + 2 * i
    if nbr <= n && !composites[nbr] {
    primes.append(nbr)
    }
    }
    p += 6
    }

    return primes
    }




    It was inspired by this code by Mr Martin.



    Using the same benchmarking code in that answer, adding a fourth fractional digit in the timing results, plus some formatting, here are the results :



            ---------------------------------------------------------------
    | | Nbr | Time (sec) |
    | Up to | of |------------------------------|
    | | Primes | Martin's | This |
    |----------------|-------------|------------------------------|
    | 100_000 | 9592 | 0.0008 | 0.0004 |
    |----------------|-------------|--------------|---------------|
    | 1_000_000 | 78_498 | 0.0056 | 0.0026 |
    |----------------|-------------|--------------|---------------|
    | 10_000_000 | 664_579 | 0.1233 | 0.0426 |
    |----------------|-------------|--------------|---------------|
    | 100_000_000 | 5_761_455 | 1.0976 | 0.5089 |
    |----------------|-------------|--------------|---------------|
    | 1_000_000_000 | 50_847_534 | 12.1328 | 5.9759 |
    |----------------|-------------|--------------|---------------|
    | 10_000_000_000 | 455_052_511 | 165.5658 | 84.5477 |
    |----------------|-------------|--------------|---------------|


    Using Attabench, here is a visual representation of the performance of both codes while n is less than 2^16:



    Attabench results





    One thing I observe is some elements in the composites array are marked with true multiple times. This is expected (but unwanted) behavior since 6X-1 or 6X+1 aren't all primes.



    What I'm looking for is making this Sieve of Eratosthenes quicker. I'm well aware of faster methods of finding primes.



    Naming, code clarity, conciseness, consistency, etc, are welcome but are not the main point here.










    share|improve this question











    $endgroup$















      2












      2








      2


      1



      $begingroup$


      This is an implementation of the Sieve of Eratosthenes :




      • It takes advantages of the fact that all primes from 5 and above can be written as 6X-1 or 6X+1,


      • For better space complexity, it uses a pretty accurate upperbound. Better estimations of the upper bound can be found here. I've observed a very slight increase in performance with this.





      func eratosthenesSieve(to n: Int) -> [Int] {

      guard 2 <= n else { return [] }

      var composites = Array(repeating: false, count: n + 1)
      var primes: [Int] = []
      let d = Double(n)
      let upperBound = Int((d / log(d)) * (1.0 + 1.2762/log(d)))
      primes.reserveCapacity(upperBound)
      let squareRootN = Int(d.squareRoot())

      //2 and 3
      var p = 2
      let twoOrThree = min(n, 3)
      while p <= twoOrThree {
      primes.append(p)
      var q = p * p
      let step = p * (p - 1)
      while q <= n {
      composites[q] = true
      q += step
      }
      p += 1
      }

      //5 and above
      p += 1
      while p <= squareRootN {
      for i in 0..<2 {
      let nbr = p + 2 * i
      if !composites[nbr] {
      primes.append(nbr)
      var q = nbr * nbr
      var coef = 2 * (i + 1)
      while q <= n {
      composites[q] = true
      q += coef * nbr
      coef = 6 - coef
      }
      }
      }
      p += 6
      }

      while p <= n {
      for i in 0..<2 {
      let nbr = p + 2 * i
      if nbr <= n && !composites[nbr] {
      primes.append(nbr)
      }
      }
      p += 6
      }

      return primes
      }




      It was inspired by this code by Mr Martin.



      Using the same benchmarking code in that answer, adding a fourth fractional digit in the timing results, plus some formatting, here are the results :



              ---------------------------------------------------------------
      | | Nbr | Time (sec) |
      | Up to | of |------------------------------|
      | | Primes | Martin's | This |
      |----------------|-------------|------------------------------|
      | 100_000 | 9592 | 0.0008 | 0.0004 |
      |----------------|-------------|--------------|---------------|
      | 1_000_000 | 78_498 | 0.0056 | 0.0026 |
      |----------------|-------------|--------------|---------------|
      | 10_000_000 | 664_579 | 0.1233 | 0.0426 |
      |----------------|-------------|--------------|---------------|
      | 100_000_000 | 5_761_455 | 1.0976 | 0.5089 |
      |----------------|-------------|--------------|---------------|
      | 1_000_000_000 | 50_847_534 | 12.1328 | 5.9759 |
      |----------------|-------------|--------------|---------------|
      | 10_000_000_000 | 455_052_511 | 165.5658 | 84.5477 |
      |----------------|-------------|--------------|---------------|


      Using Attabench, here is a visual representation of the performance of both codes while n is less than 2^16:



      Attabench results





      One thing I observe is some elements in the composites array are marked with true multiple times. This is expected (but unwanted) behavior since 6X-1 or 6X+1 aren't all primes.



      What I'm looking for is making this Sieve of Eratosthenes quicker. I'm well aware of faster methods of finding primes.



      Naming, code clarity, conciseness, consistency, etc, are welcome but are not the main point here.










      share|improve this question











      $endgroup$




      This is an implementation of the Sieve of Eratosthenes :




      • It takes advantages of the fact that all primes from 5 and above can be written as 6X-1 or 6X+1,


      • For better space complexity, it uses a pretty accurate upperbound. Better estimations of the upper bound can be found here. I've observed a very slight increase in performance with this.





      func eratosthenesSieve(to n: Int) -> [Int] {

      guard 2 <= n else { return [] }

      var composites = Array(repeating: false, count: n + 1)
      var primes: [Int] = []
      let d = Double(n)
      let upperBound = Int((d / log(d)) * (1.0 + 1.2762/log(d)))
      primes.reserveCapacity(upperBound)
      let squareRootN = Int(d.squareRoot())

      //2 and 3
      var p = 2
      let twoOrThree = min(n, 3)
      while p <= twoOrThree {
      primes.append(p)
      var q = p * p
      let step = p * (p - 1)
      while q <= n {
      composites[q] = true
      q += step
      }
      p += 1
      }

      //5 and above
      p += 1
      while p <= squareRootN {
      for i in 0..<2 {
      let nbr = p + 2 * i
      if !composites[nbr] {
      primes.append(nbr)
      var q = nbr * nbr
      var coef = 2 * (i + 1)
      while q <= n {
      composites[q] = true
      q += coef * nbr
      coef = 6 - coef
      }
      }
      }
      p += 6
      }

      while p <= n {
      for i in 0..<2 {
      let nbr = p + 2 * i
      if nbr <= n && !composites[nbr] {
      primes.append(nbr)
      }
      }
      p += 6
      }

      return primes
      }




      It was inspired by this code by Mr Martin.



      Using the same benchmarking code in that answer, adding a fourth fractional digit in the timing results, plus some formatting, here are the results :



              ---------------------------------------------------------------
      | | Nbr | Time (sec) |
      | Up to | of |------------------------------|
      | | Primes | Martin's | This |
      |----------------|-------------|------------------------------|
      | 100_000 | 9592 | 0.0008 | 0.0004 |
      |----------------|-------------|--------------|---------------|
      | 1_000_000 | 78_498 | 0.0056 | 0.0026 |
      |----------------|-------------|--------------|---------------|
      | 10_000_000 | 664_579 | 0.1233 | 0.0426 |
      |----------------|-------------|--------------|---------------|
      | 100_000_000 | 5_761_455 | 1.0976 | 0.5089 |
      |----------------|-------------|--------------|---------------|
      | 1_000_000_000 | 50_847_534 | 12.1328 | 5.9759 |
      |----------------|-------------|--------------|---------------|
      | 10_000_000_000 | 455_052_511 | 165.5658 | 84.5477 |
      |----------------|-------------|--------------|---------------|


      Using Attabench, here is a visual representation of the performance of both codes while n is less than 2^16:



      Attabench results





      One thing I observe is some elements in the composites array are marked with true multiple times. This is expected (but unwanted) behavior since 6X-1 or 6X+1 aren't all primes.



      What I'm looking for is making this Sieve of Eratosthenes quicker. I'm well aware of faster methods of finding primes.



      Naming, code clarity, conciseness, consistency, etc, are welcome but are not the main point here.







      performance primes swift sieve-of-eratosthenes






      share|improve this question















      share|improve this question













      share|improve this question




      share|improve this question








      edited 11 mins ago







      ielyamani

















      asked Jan 13 at 21:33









      ielyamaniielyamani

      355213




      355213






















          1 Answer
          1






          active

          oldest

          votes


















          1












          $begingroup$


          It takes advantages of the fact that all primes from 5 and above can be written as 6X-1 or 6X+1,




          I don't think it does, really. It structures the code around that fact, but to take advantage of it, at a minimum you should replace




              while p <= twoOrThree {
          primes.append(p)
          var q = p * p
          let step = p * (p - 1)
          while q <= n {
          composites[q] = true
          q += step
          }
          p += 1
          }



          with



              while p <= twoOrThree {
          primes.append(p)
          p += 1
          }


          which in my testing gives a significant speedup.





          To maximise the advantage, you could reduce composites to only store flags for $6X pm 1$. Proof of concept code (could be tidier):



              var pidx = 1
          p = 5
          while p <= squareRootN {
          if !composites[pidx] {
          primes.append(p)

          var qidx = 3 * pidx * (pidx + 2) + 1 + (pidx & 1)
          let delta = p << 1
          let off = (4 - 2 * (pidx & 1)) * pidx + 1
          while qidx < composites.count {
          composites[qidx - off] = true
          composites[qidx] = true
          qidx += delta
          }
          if qidx - off < composites.count {
          composites[qidx - off] = true
          }
          }

          pidx += 1
          p += 2 + 2 * (pidx & 1)
          }

          while p <= n {
          if !composites[pidx] { primes.append(p) }
          pidx += 1
          p += 2 + 2 * (pidx & 1)
          }


          This gives a moderate speedup in my testing.






          share|improve this answer









          $endgroup$













          • $begingroup$
            Thank you for the answer. Here are the benchmarks, and they favor the code in the question (original being Martin's, and eratosthenes2 is the code in your answer). Attabench confirms the benchmarks.
            $endgroup$
            – ielyamani
            Jan 14 at 13:06










          • $begingroup$
            @Carpsen90, I don't know Swift and there seem to be some subtleties around imports which both this question and the answer you reference brush under the table, but I compared a tweaked version of your code with my code on tio.run . Full tested code. I see user time: 12.470 s for your code and 7.002 s for mine. tio.run isn't ideal for benchmarking, but that's a significant improvement.
            $endgroup$
            – Peter Taylor
            Jan 14 at 14:02










          • $begingroup$
            (I suspect the problem is that you've benchmarked my code sieving three times as far as your code).
            $endgroup$
            – Peter Taylor
            Jan 14 at 14:04










          • $begingroup$
            The benchmarks were correct and can still be reproduced. You didn't mention in your answer this line var composites = Array(repeating: false, count: n / 3 + 1), which makes all the difference. Here are the new benchmarks which favor your code.
            $endgroup$
            – ielyamani
            Jan 14 at 15:03










          • $begingroup$
            The answer is intended to be a code review, not a patch.
            $endgroup$
            – Peter Taylor
            Jan 14 at 15:27











          Your Answer





          StackExchange.ifUsing("editor", function () {
          return StackExchange.using("mathjaxEditing", function () {
          StackExchange.MarkdownEditor.creationCallbacks.add(function (editor, postfix) {
          StackExchange.mathjaxEditing.prepareWmdForMathJax(editor, postfix, [["\$", "\$"]]);
          });
          });
          }, "mathjax-editing");

          StackExchange.ifUsing("editor", function () {
          StackExchange.using("externalEditor", function () {
          StackExchange.using("snippets", function () {
          StackExchange.snippets.init();
          });
          });
          }, "code-snippets");

          StackExchange.ready(function() {
          var channelOptions = {
          tags: "".split(" "),
          id: "196"
          };
          initTagRenderer("".split(" "), "".split(" "), channelOptions);

          StackExchange.using("externalEditor", function() {
          // Have to fire editor after snippets, if snippets enabled
          if (StackExchange.settings.snippets.snippetsEnabled) {
          StackExchange.using("snippets", function() {
          createEditor();
          });
          }
          else {
          createEditor();
          }
          });

          function createEditor() {
          StackExchange.prepareEditor({
          heartbeatType: 'answer',
          autoActivateHeartbeat: false,
          convertImagesToLinks: false,
          noModals: true,
          showLowRepImageUploadWarning: true,
          reputationToPostImages: null,
          bindNavPrevention: true,
          postfix: "",
          imageUploader: {
          brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
          contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
          allowUrls: true
          },
          onDemand: true,
          discardSelector: ".discard-answer"
          ,immediatelyShowMarkdownHelp:true
          });


          }
          });














          draft saved

          draft discarded


















          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f211437%2ffaster-sieve-of-eratosthenes%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown

























          1 Answer
          1






          active

          oldest

          votes








          1 Answer
          1






          active

          oldest

          votes









          active

          oldest

          votes






          active

          oldest

          votes









          1












          $begingroup$


          It takes advantages of the fact that all primes from 5 and above can be written as 6X-1 or 6X+1,




          I don't think it does, really. It structures the code around that fact, but to take advantage of it, at a minimum you should replace




              while p <= twoOrThree {
          primes.append(p)
          var q = p * p
          let step = p * (p - 1)
          while q <= n {
          composites[q] = true
          q += step
          }
          p += 1
          }



          with



              while p <= twoOrThree {
          primes.append(p)
          p += 1
          }


          which in my testing gives a significant speedup.





          To maximise the advantage, you could reduce composites to only store flags for $6X pm 1$. Proof of concept code (could be tidier):



              var pidx = 1
          p = 5
          while p <= squareRootN {
          if !composites[pidx] {
          primes.append(p)

          var qidx = 3 * pidx * (pidx + 2) + 1 + (pidx & 1)
          let delta = p << 1
          let off = (4 - 2 * (pidx & 1)) * pidx + 1
          while qidx < composites.count {
          composites[qidx - off] = true
          composites[qidx] = true
          qidx += delta
          }
          if qidx - off < composites.count {
          composites[qidx - off] = true
          }
          }

          pidx += 1
          p += 2 + 2 * (pidx & 1)
          }

          while p <= n {
          if !composites[pidx] { primes.append(p) }
          pidx += 1
          p += 2 + 2 * (pidx & 1)
          }


          This gives a moderate speedup in my testing.






          share|improve this answer









          $endgroup$













          • $begingroup$
            Thank you for the answer. Here are the benchmarks, and they favor the code in the question (original being Martin's, and eratosthenes2 is the code in your answer). Attabench confirms the benchmarks.
            $endgroup$
            – ielyamani
            Jan 14 at 13:06










          • $begingroup$
            @Carpsen90, I don't know Swift and there seem to be some subtleties around imports which both this question and the answer you reference brush under the table, but I compared a tweaked version of your code with my code on tio.run . Full tested code. I see user time: 12.470 s for your code and 7.002 s for mine. tio.run isn't ideal for benchmarking, but that's a significant improvement.
            $endgroup$
            – Peter Taylor
            Jan 14 at 14:02










          • $begingroup$
            (I suspect the problem is that you've benchmarked my code sieving three times as far as your code).
            $endgroup$
            – Peter Taylor
            Jan 14 at 14:04










          • $begingroup$
            The benchmarks were correct and can still be reproduced. You didn't mention in your answer this line var composites = Array(repeating: false, count: n / 3 + 1), which makes all the difference. Here are the new benchmarks which favor your code.
            $endgroup$
            – ielyamani
            Jan 14 at 15:03










          • $begingroup$
            The answer is intended to be a code review, not a patch.
            $endgroup$
            – Peter Taylor
            Jan 14 at 15:27
















          1












          $begingroup$


          It takes advantages of the fact that all primes from 5 and above can be written as 6X-1 or 6X+1,




          I don't think it does, really. It structures the code around that fact, but to take advantage of it, at a minimum you should replace




              while p <= twoOrThree {
          primes.append(p)
          var q = p * p
          let step = p * (p - 1)
          while q <= n {
          composites[q] = true
          q += step
          }
          p += 1
          }



          with



              while p <= twoOrThree {
          primes.append(p)
          p += 1
          }


          which in my testing gives a significant speedup.





          To maximise the advantage, you could reduce composites to only store flags for $6X pm 1$. Proof of concept code (could be tidier):



              var pidx = 1
          p = 5
          while p <= squareRootN {
          if !composites[pidx] {
          primes.append(p)

          var qidx = 3 * pidx * (pidx + 2) + 1 + (pidx & 1)
          let delta = p << 1
          let off = (4 - 2 * (pidx & 1)) * pidx + 1
          while qidx < composites.count {
          composites[qidx - off] = true
          composites[qidx] = true
          qidx += delta
          }
          if qidx - off < composites.count {
          composites[qidx - off] = true
          }
          }

          pidx += 1
          p += 2 + 2 * (pidx & 1)
          }

          while p <= n {
          if !composites[pidx] { primes.append(p) }
          pidx += 1
          p += 2 + 2 * (pidx & 1)
          }


          This gives a moderate speedup in my testing.






          share|improve this answer









          $endgroup$













          • $begingroup$
            Thank you for the answer. Here are the benchmarks, and they favor the code in the question (original being Martin's, and eratosthenes2 is the code in your answer). Attabench confirms the benchmarks.
            $endgroup$
            – ielyamani
            Jan 14 at 13:06










          • $begingroup$
            @Carpsen90, I don't know Swift and there seem to be some subtleties around imports which both this question and the answer you reference brush under the table, but I compared a tweaked version of your code with my code on tio.run . Full tested code. I see user time: 12.470 s for your code and 7.002 s for mine. tio.run isn't ideal for benchmarking, but that's a significant improvement.
            $endgroup$
            – Peter Taylor
            Jan 14 at 14:02










          • $begingroup$
            (I suspect the problem is that you've benchmarked my code sieving three times as far as your code).
            $endgroup$
            – Peter Taylor
            Jan 14 at 14:04










          • $begingroup$
            The benchmarks were correct and can still be reproduced. You didn't mention in your answer this line var composites = Array(repeating: false, count: n / 3 + 1), which makes all the difference. Here are the new benchmarks which favor your code.
            $endgroup$
            – ielyamani
            Jan 14 at 15:03










          • $begingroup$
            The answer is intended to be a code review, not a patch.
            $endgroup$
            – Peter Taylor
            Jan 14 at 15:27














          1












          1








          1





          $begingroup$


          It takes advantages of the fact that all primes from 5 and above can be written as 6X-1 or 6X+1,




          I don't think it does, really. It structures the code around that fact, but to take advantage of it, at a minimum you should replace




              while p <= twoOrThree {
          primes.append(p)
          var q = p * p
          let step = p * (p - 1)
          while q <= n {
          composites[q] = true
          q += step
          }
          p += 1
          }



          with



              while p <= twoOrThree {
          primes.append(p)
          p += 1
          }


          which in my testing gives a significant speedup.





          To maximise the advantage, you could reduce composites to only store flags for $6X pm 1$. Proof of concept code (could be tidier):



              var pidx = 1
          p = 5
          while p <= squareRootN {
          if !composites[pidx] {
          primes.append(p)

          var qidx = 3 * pidx * (pidx + 2) + 1 + (pidx & 1)
          let delta = p << 1
          let off = (4 - 2 * (pidx & 1)) * pidx + 1
          while qidx < composites.count {
          composites[qidx - off] = true
          composites[qidx] = true
          qidx += delta
          }
          if qidx - off < composites.count {
          composites[qidx - off] = true
          }
          }

          pidx += 1
          p += 2 + 2 * (pidx & 1)
          }

          while p <= n {
          if !composites[pidx] { primes.append(p) }
          pidx += 1
          p += 2 + 2 * (pidx & 1)
          }


          This gives a moderate speedup in my testing.






          share|improve this answer









          $endgroup$




          It takes advantages of the fact that all primes from 5 and above can be written as 6X-1 or 6X+1,




          I don't think it does, really. It structures the code around that fact, but to take advantage of it, at a minimum you should replace




              while p <= twoOrThree {
          primes.append(p)
          var q = p * p
          let step = p * (p - 1)
          while q <= n {
          composites[q] = true
          q += step
          }
          p += 1
          }



          with



              while p <= twoOrThree {
          primes.append(p)
          p += 1
          }


          which in my testing gives a significant speedup.





          To maximise the advantage, you could reduce composites to only store flags for $6X pm 1$. Proof of concept code (could be tidier):



              var pidx = 1
          p = 5
          while p <= squareRootN {
          if !composites[pidx] {
          primes.append(p)

          var qidx = 3 * pidx * (pidx + 2) + 1 + (pidx & 1)
          let delta = p << 1
          let off = (4 - 2 * (pidx & 1)) * pidx + 1
          while qidx < composites.count {
          composites[qidx - off] = true
          composites[qidx] = true
          qidx += delta
          }
          if qidx - off < composites.count {
          composites[qidx - off] = true
          }
          }

          pidx += 1
          p += 2 + 2 * (pidx & 1)
          }

          while p <= n {
          if !composites[pidx] { primes.append(p) }
          pidx += 1
          p += 2 + 2 * (pidx & 1)
          }


          This gives a moderate speedup in my testing.







          share|improve this answer












          share|improve this answer



          share|improve this answer










          answered Jan 14 at 11:23









          Peter TaylorPeter Taylor

          18.2k2963




          18.2k2963












          • $begingroup$
            Thank you for the answer. Here are the benchmarks, and they favor the code in the question (original being Martin's, and eratosthenes2 is the code in your answer). Attabench confirms the benchmarks.
            $endgroup$
            – ielyamani
            Jan 14 at 13:06










          • $begingroup$
            @Carpsen90, I don't know Swift and there seem to be some subtleties around imports which both this question and the answer you reference brush under the table, but I compared a tweaked version of your code with my code on tio.run . Full tested code. I see user time: 12.470 s for your code and 7.002 s for mine. tio.run isn't ideal for benchmarking, but that's a significant improvement.
            $endgroup$
            – Peter Taylor
            Jan 14 at 14:02










          • $begingroup$
            (I suspect the problem is that you've benchmarked my code sieving three times as far as your code).
            $endgroup$
            – Peter Taylor
            Jan 14 at 14:04










          • $begingroup$
            The benchmarks were correct and can still be reproduced. You didn't mention in your answer this line var composites = Array(repeating: false, count: n / 3 + 1), which makes all the difference. Here are the new benchmarks which favor your code.
            $endgroup$
            – ielyamani
            Jan 14 at 15:03










          • $begingroup$
            The answer is intended to be a code review, not a patch.
            $endgroup$
            – Peter Taylor
            Jan 14 at 15:27


















          • $begingroup$
            Thank you for the answer. Here are the benchmarks, and they favor the code in the question (original being Martin's, and eratosthenes2 is the code in your answer). Attabench confirms the benchmarks.
            $endgroup$
            – ielyamani
            Jan 14 at 13:06










          • $begingroup$
            @Carpsen90, I don't know Swift and there seem to be some subtleties around imports which both this question and the answer you reference brush under the table, but I compared a tweaked version of your code with my code on tio.run . Full tested code. I see user time: 12.470 s for your code and 7.002 s for mine. tio.run isn't ideal for benchmarking, but that's a significant improvement.
            $endgroup$
            – Peter Taylor
            Jan 14 at 14:02










          • $begingroup$
            (I suspect the problem is that you've benchmarked my code sieving three times as far as your code).
            $endgroup$
            – Peter Taylor
            Jan 14 at 14:04










          • $begingroup$
            The benchmarks were correct and can still be reproduced. You didn't mention in your answer this line var composites = Array(repeating: false, count: n / 3 + 1), which makes all the difference. Here are the new benchmarks which favor your code.
            $endgroup$
            – ielyamani
            Jan 14 at 15:03










          • $begingroup$
            The answer is intended to be a code review, not a patch.
            $endgroup$
            – Peter Taylor
            Jan 14 at 15:27
















          $begingroup$
          Thank you for the answer. Here are the benchmarks, and they favor the code in the question (original being Martin's, and eratosthenes2 is the code in your answer). Attabench confirms the benchmarks.
          $endgroup$
          – ielyamani
          Jan 14 at 13:06




          $begingroup$
          Thank you for the answer. Here are the benchmarks, and they favor the code in the question (original being Martin's, and eratosthenes2 is the code in your answer). Attabench confirms the benchmarks.
          $endgroup$
          – ielyamani
          Jan 14 at 13:06












          $begingroup$
          @Carpsen90, I don't know Swift and there seem to be some subtleties around imports which both this question and the answer you reference brush under the table, but I compared a tweaked version of your code with my code on tio.run . Full tested code. I see user time: 12.470 s for your code and 7.002 s for mine. tio.run isn't ideal for benchmarking, but that's a significant improvement.
          $endgroup$
          – Peter Taylor
          Jan 14 at 14:02




          $begingroup$
          @Carpsen90, I don't know Swift and there seem to be some subtleties around imports which both this question and the answer you reference brush under the table, but I compared a tweaked version of your code with my code on tio.run . Full tested code. I see user time: 12.470 s for your code and 7.002 s for mine. tio.run isn't ideal for benchmarking, but that's a significant improvement.
          $endgroup$
          – Peter Taylor
          Jan 14 at 14:02












          $begingroup$
          (I suspect the problem is that you've benchmarked my code sieving three times as far as your code).
          $endgroup$
          – Peter Taylor
          Jan 14 at 14:04




          $begingroup$
          (I suspect the problem is that you've benchmarked my code sieving three times as far as your code).
          $endgroup$
          – Peter Taylor
          Jan 14 at 14:04












          $begingroup$
          The benchmarks were correct and can still be reproduced. You didn't mention in your answer this line var composites = Array(repeating: false, count: n / 3 + 1), which makes all the difference. Here are the new benchmarks which favor your code.
          $endgroup$
          – ielyamani
          Jan 14 at 15:03




          $begingroup$
          The benchmarks were correct and can still be reproduced. You didn't mention in your answer this line var composites = Array(repeating: false, count: n / 3 + 1), which makes all the difference. Here are the new benchmarks which favor your code.
          $endgroup$
          – ielyamani
          Jan 14 at 15:03












          $begingroup$
          The answer is intended to be a code review, not a patch.
          $endgroup$
          – Peter Taylor
          Jan 14 at 15:27




          $begingroup$
          The answer is intended to be a code review, not a patch.
          $endgroup$
          – Peter Taylor
          Jan 14 at 15:27


















          draft saved

          draft discarded




















































          Thanks for contributing an answer to Code Review Stack Exchange!


          • Please be sure to answer the question. Provide details and share your research!

          But avoid



          • Asking for help, clarification, or responding to other answers.

          • Making statements based on opinion; back them up with references or personal experience.


          Use MathJax to format equations. MathJax reference.


          To learn more, see our tips on writing great answers.




          draft saved


          draft discarded














          StackExchange.ready(
          function () {
          StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f211437%2ffaster-sieve-of-eratosthenes%23new-answer', 'question_page');
          }
          );

          Post as a guest















          Required, but never shown





















































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown

































          Required, but never shown














          Required, but never shown












          Required, but never shown







          Required, but never shown







          Popular posts from this blog

          Webac Holding Inhaltsverzeichnis Geschichte | Organisationsstruktur | Tochterfirmen |...

          What's the meaning of a knight fighting a snail in medieval book illustrations?What is the meaning of a glove...

          Salamanca Inhaltsverzeichnis Lage und Klima | Bevölkerungsentwicklung | Geschichte | Kultur und...