How to solve canister stack overflow problem and change canister cycel limit?

I want to get the time of searching 100 random numbers from 1000_000_000 numbers in a list, but I get the exception : “cycles limit for single message execution”. How can I change the canister cycle limitation?

source code :
private func createNumber() : Text{
var i : Nat32 = 10_000_000;
let before = Time.now();
while (i != Nat32.fromNat(0)) {
list := List.append(list, ?(i, null));
i-=1;
};
let end = Time.now();
let elapsedSeconds = (end - before) / 1000_000_000;
Int.toText(elapsedSeconds)
};

public query func findNumber() : async Text{
    var create_time = createNumber();
    
    var i = 100;
    let before = Time.now();
    while (i != 0){
        var randomNumber = switch(random.range(Nat8.fromNat(23))){
            case (?num) { num };
            case _ { 0 };
        };
        var r_num = Nat32.fromNat(randomNumber);
        switch(List.find<Nat32>(list, func (n :Nat32) : Bool{
            if (n == r_num) { true }
            else{ false }
        })){
            case (?num) { () };
            case _ { () };
        };
        i -= 1;
    };
    let after = Time.now();
    let elapsedSeconds = (after - before) / 1000_000_000;
    "find time : " # Int.toText(elapsedSeconds)
};
1 Like

The per-message cycle limit is unfortunately hard-coded in the Internet Computer. If you are hitting it, you might want to refactor your code.

Note that List is a very inefficient data structure if you use functions like append. It might already help to add new elements to the beginning of the list in your createNumber function.

Also, it won’t help to compare Time.now() within one message execution; this is the logical block time and does not advance within a function.

5 Likes

Your title (but not the body) mentions a stackoverflow - did you actually encounter this or just the cycle limit? Curious to know because I’m wondering if there may also be an issue with the compiler, not just the platform cycle limit.

1 Like

List.append is actually recursive, so it might well be a stack overflow too.

I would suggest creating a array of size 10_000_000 using Array.tabulate instead.

(Edited to suggest Array.tabulate instead of Array.tabulateVar, since there’s probably no need for mutability. Array.tabulate takes a generation function and that is probably sufficient.)

Yes, I have met a stack overflow exception.When I call the createNumber function, the exception will be triggered.
I agree with nomeata that the exception was caused by List.append , which is recursive.

My OS version : Ubuntu 20.04
dfx version 0.7.1
problem code:

private func createNumber() : Text{
        var i : Nat32 = 10_000_000;
        let before = Time.now();
        while (i != Nat32.fromNat(0)) {
            list := List.append(list, ?(i, null));
            i-=1;
        };
        let end = Time.now();
        let elapsedSeconds = (end - before) / 1000_000_000;
        Int.toText(elapsedSeconds)
    };

thank you for your attention.

Thank you, I have learned what you said :slight_smile:

Great, that explains it. I overlooked the code for the function that was badly formatted, assuming it was the same as the formatted code. We should probably have a better implementation of append though…

1 Like

As general advice, if you run into stack overflows with list functions, then that is a strong indicator that a list is probably the wrong choice of data structure for the problem at hand. Keep in mind that e.g. the cost of something like append is linear in the length of the list.

It would be nice to configure the per-message cycle limit for a local replica. I am writing tests and would like to run them from within a canister, so that my tests execute in the IC environment. I am pretty sure I will run into this cycle limit because of my tests, which are designed to use a lot of computation (I am doing property-based tests and hope to run 100,000s to 1,000,000s of iterations per test). Right now I do a full integration test and call into the IC, so I can only do 10-100 iterations at a time.

Have you tried using the emulator? I believe it has no cycle limits & lower latency but slower execution on compute heavy tests. YMMV.

(‘dfx start --emulator’ or some permutation of that)

3 Likes