Actor classes latencies

I have this very simple test project where an actor class is calling another actor class in the same canister. The delay is several seconds which is unacceptable.

Could someone provide an explanation or a diagram on what’s going on and it is so slow?
Is there a better way to code this?

main.mo:

import ClassOne "ActorClass1";

import D "mo:base/Debug";
import Time "mo:base/Time";

actor {

    public func test_delay() : async Text {
        D.print(debug_show(Time.now()));
        let actorClassOne = await ClassOne.ClassOne();
        D.print(debug_show(Time.now()));
        return await actorClassOne.callClassOne();
    };
};

ActorClass1.mo

import ClassTwo "ActorClass2";

import D "mo:base/Debug";
import Time "mo:base/Time";

actor class ClassOne() {
    
    public func callClassOne(): async Text {
        let actorClassTwo= await ClassTwo.ClassTwo();
        return await actorClassTwo.callClassTwo();
    }
}

ActorClass2.mo

import D "mo:base/Debug";

actor class ClassTwo() {
    public func callClassTwo(): async Text {
        return "classTwo has been called";
    }
}

Calling an actor class constructor is essentially installing a fresh canister on the platform.
The latency is high for two reasons:

  1. Installing a canister take two System update calls, one to allocate a canister id, and a second to install its code.

  2. The code generated by the Motoko compiler currently introduces two additional async (update) calls, one for the Motoko constructor itself, and an additional one for an internal helper that does step 1.

The Motoko compiler could probably optimize all of the overhead of step 2 away entirely, but currently does not.

Lowering the cost of step 1 would require the platform to support a more efficient, one step installation method. The two step procedure is provided to enable mutually recursive canisters.

Hope that helps,
Claudio

Thank you. I didn’t know that every time you declare an actor a new canister will be created…
When I just use class I get the warning that it is going to be deprecated. Are modules the only way to avoid creating a new canister while you can encapsulate logic and state?

Modules are really intended for state-less libraries of functions.

Motoko also supports object literals and classes to let you define more OO style objects with encapsulated state.

I’m not sure about the deprecation you are seeing, but if you were trying to import a plain object class then you are meant to wrap the library in a module:

Eg.

Counters.mo:

module {
   public class Counter(init: Int) { 
      var c = init;
      public func inc() { c += 1 };
   };
}

main.mo:

import Counters "Counters";
actor  {
     let c = Counters.Counter(0);
     c.inc();
     ...
}

(not checked with compiler)

1 Like

ah, that works! Thank you @claudio

I couldn’t find any documentation that showed that you can publicly expose a class from a module…