Node.js's BigInteger.+ beats attaswift.BigInt.+ by about 3x for the specific case that's being exercised here (n = 10,000), which is precisely within expectations for a naive implementation.
Swift code
import BigInt
func fibonacci(_ n: Int) -> BigInt {
precondition(n > 0)
if n == 1 {
return 0
}
var previous: BigInt = 0
var current: BigInt = 1
for _ in 2 ... n {
let next = previous + current
previous = current
current = next
}
return current
}
let counts = [10000, 30000, 100000, 300000, 1000000]
for c in counts {
print(c)
var f: BigInt = 0
let clock = ContinuousClock()
var duration = clock.measure {
f = fibonacci(c)
}
print("fibonacci: \(duration)")
duration = clock.measure {
print(f)
}
print("print: \(duration)")
}
Node.js code
function fib_loop(n) {
if (n <= 1) {
return BigInt(n);
} else {
let a = BigInt(0);
let b = BigInt(1);
let temp;
for (let i = 2; i <= n; i++) {
temp = a + b;
a = b;
b = temp;
}
return b.toString();
}
}
let counts = [10000, 30000, 100000, 300000, 1000000];
for (let i = 0; i < counts.length; i++) {
let c = counts[i];
console.log(c);
console.time("fib_loop");
let f = fib_loop(c);
console.timeEnd("fib_loop");
console.time("print");
console.log(f);
console.timeEnd("print");
}
Interesting, but ultimately irrelevant benchmark data
Interestingly, the gap widens to ~6x on larger values:
Count | node.js | Swift | ratio |
---|---|---|---|
10,000 | 1.558ms | 4.345ms | 2.78x |
30,000 | 8.794ms | 33.127ms | 3.76x |
100,000 | 69.763ms | 350.025ms | 5.02x |
300,000 | 552.623ms | 3056.003ms | 5.52x |
1,000,000 | 5.944s | 34.368s | 5.78x |
(Switching to using BigUInt
and +=
is expected to eliminate the allocation overhead. Doing that shaves off a few percentages, but it doesn't meaningfully close the gap.)
Radix conversions are particularly slow in my old library. For n = 10,000, attaswift does its decimal conversion about 7x slower than Node.js. Other values demonstrate that the latter may have an algorithmic advantage:
Count | node.js | Swift | ratio |
---|---|---|---|
10,000 | 0.024ms | 0.164ms | 6.8x |
30,000 | 0.047ms | 1.199ms | 25.5x |
100,000 | 0.076ms | 12.006ms | 158x |
300,000 | 0.18ms | 109.942ms | 611x |
1,000,000 | 0.536ms | 1273.589ms | 2376x |
It would be useful to follow up on these to rule out Standard Library issues. However, these are most likely due to a multitude of package-level issues. (I wrote this stuff almost a decade ago, in the Swift 2 era. Swift 4 was in beta the last time I was able to update this code.)