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
$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-1or6X+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:

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
$endgroup$
add a comment |
$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-1or6X+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:

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
$endgroup$
add a comment |
$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-1or6X+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:

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
$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-1or6X+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:

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
performance primes swift sieve-of-eratosthenes
edited 11 mins ago
ielyamani
asked Jan 13 at 21:33
ielyamaniielyamani
355213
355213
add a comment |
add a comment |
1 Answer
1
active
oldest
votes
$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.
$endgroup$
$begingroup$
Thank you for the answer. Here are the benchmarks, and they favor the code in the question (originalbeing Martin's, anderatosthenes2is 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 linevar 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
add a comment |
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
});
}
});
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
$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.
$endgroup$
$begingroup$
Thank you for the answer. Here are the benchmarks, and they favor the code in the question (originalbeing Martin's, anderatosthenes2is 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 linevar 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
add a comment |
$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.
$endgroup$
$begingroup$
Thank you for the answer. Here are the benchmarks, and they favor the code in the question (originalbeing Martin's, anderatosthenes2is 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 linevar 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
add a comment |
$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.
$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.
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 (originalbeing Martin's, anderatosthenes2is 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 linevar 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
add a comment |
$begingroup$
Thank you for the answer. Here are the benchmarks, and they favor the code in the question (originalbeing Martin's, anderatosthenes2is 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 linevar 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
add a comment |
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.
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
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
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Required, but never shown
Sign up or log in
StackExchange.ready(function () {
StackExchange.helpers.onClickDraftSave('#login-link');
});
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
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