Correct BigInt Fibonacci Implementation in Zig Lang

I am new to Zig Lang, and I was searching for an existing implementation for Big Int Fibonacci to no avail.

So, I went through the source code of Zig Lang, Specifically Big Int Source & Big Int Tests, to figure out how to use the Big Int functionality and then winged the desired solution.

It's sort of a tradition I follow while trying to gauge the speed of a language before learning. Like I did with: Big Int Fibonacci Benchmark for Go & Rust. (N.B. I didn't have to write the solution for those languages!)

Now, here's my implementation in Zig, for your kind perusal:

const std = @import("std");
const Managed = std.math.big.int.Managed;

pub fn main() anyerror!void {
    // var arena = std.heap.ArenaAllocator.init(std.heap.c_allocator);
    // defer arena.deinit();
    // const allocator = arena.allocator();

    const allocator = std.heap.c_allocator;

    var a = try Managed.initSet(allocator, 0);
    defer a.deinit();
    var b = try Managed.initSet(allocator, 1);
    defer b.deinit();
    var i: u128 = 0;

    while (i < 50000) : (i += 1) {
        var c = try Managed.init(allocator);

        try c.add(a.toConst(), b.toConst());

        a = b;
        b = try c.clone();

        c.deinit();
    }

    const as = try a.toString(allocator, 10, std.fmt.Case.lower);
    defer allocator.free(as);
    std.log.info("Fib: {s}", .{as});
}

If I try to increase the number to 500,000, the memory usage increases to over 10 GB.

I want the program to run without memory leak for n =

    50,000 (Runs fine, with Arena & General Purpose Alloc!)
   500,000 (Mem Leak)
 1,000,000 (Mem Leak)
 5,000,000 (Mem Leak)
10,000,000 (Mem Leak)

I tried most of the allocators, page_allocator, ArenaAllocator, GeneralPurposeAllocator, to speed up the process and arrived at c_allocator. As for how to plug the memory leak, I have no clue!

P.S. I have just skimmed the documentation of ZigLang & ZigLearn to get to this point. I have no grasp of the entire language. So please go easy on me!

P.P.S. Command I used to build the app:

zig build-exe ./src/main.zig -O ReleaseFast --strip -lc

System Info: Mac Mini 2020, base variant.


Solution 1:

I figured out the solution: (Using inbuilt swap method for efficient memory swaps!)

const std = @import("std");
const Managed = std.math.big.int.Managed;

pub fn main() anyerror!void {
    const allocator = std.heap.c_allocator;

    var a = try Managed.initSet(allocator, 0);
    defer a.deinit();
    var b = try Managed.initSet(allocator, 1);
    defer b.deinit();
    var i: u128 = 0;

    var c = try Managed.init(allocator);
    defer c.deinit();

    while (i < 1000000) : (i += 1) {
        try c.add(a.toConst(), b.toConst());

        a.swap(&b); // This is efficient than using Clone!
        b.swap(&c); // This reduced memory leak.
    }

    const as = try a.toString(allocator, 10, std.fmt.Case.lower);
    defer allocator.free(as);
    std.log.info("Fib: {s}", .{as});
}

Using this, the memory usage went upto 3.4 mb, max, for n = 1,000,000!!

But it took a minute to finish, far slower than Rust or Golang!

P.S. I created an issue on Git: Super slow Big Int Implementation in Zig