Calculate an estimate of cycles consumption: a universal example

Huge thank you to @domwoe for digging into this, and also @dsarlis for the clarifications.

So I’ve used the information gathered to fill in the missing pieces and demonstrate how cycles are consumed in an example scenario. I try to break each cycle of the consumptions down and explain what they’re for and from where it is deducted.

Setup
Same as the original post but I’ve decided to ignore the priv() function since it does not add anything to the example where.

Assumptions
We assume that the Wasm code of both canisters is 10 kB in size and that the Wasm heap, and global variables are 0 in size for simplicity. We also assume that stable memory is 0 in size initially. The number of Wasm instructions for each function is assumed to be 10. Each function is assumed to return a payload of the same size as in the request. We also ignore the size of any data structures, i.e it is assumed that the memory allocation to store, e.g, 10 kB of data is 10 kB.

Furthermore, we assume that the reserved compute allocation and the reserved memory allocation is set to zero (which is the default). If you want to check this in your canister you may do so with dfx canister status.

Initial cycle consumption
Create canisters

  • 100,000,000,000 cycles deducted from the deployer’s wallet for Canister Created, i.e creating the main canister
  • 100,000,000,000 cycles deducted from the deployer’s wallet for Canister Created, i.e creating the secondary canister

Memory & compute allocation

  • 127,000 * 10*10^-6 cycles per second deducted from main for GB Storage Per Second, i.e the main canister’s code storage (10 kB)
  • 127,000 * 10*10^-6 cycles per second deducted from secondary for GB Storage Per Second, i.e the secondary canister’s code storage (10 kB)
  • 0 cycles per second for Compute Percent Allocated Per Second, since we do not have reserved this (see assumption above)

Dynamic cycle consumption
Update call

  • 1,200,000 cycles deducted from main for Ingress Message Reception, i.e the user’s update call to entry()
  • 1,200 * 100,000 cycles deducted from main for Ingress Byte Reception, i.e the 100 kB payload in the user’s update call to entry()

Execution

  • 590,000 cycles deducted from main for Update Message Execution, i.e a fixed cost for the execution of the update call to entry()
  • 4 cycles deducted from main for Ten Update Instructions Execution, i.e executing 10 Wasm instructions in entry()

Inter-canister call (request)

  • 260,000 cycles deducted from main for Xnet Call, i.e the inter-canister call from main to secondary
  • 1,000 * 100,000 deducted from main for Xnet Byte Transmission, i.e the 100 kB payload in the request of the inter-canister call
  • 1,000 * 100,000 deducted from main for Xnet Byte Transmission, i.e the 100 kB payload in the response from the inter-canister call (remember that we assumed response size = request size)

Execution

  • 590,000 cycles deducted from secondary for Update Message Execution, i.e a fixed cost for the execution of the update call to store()
  • 4 cycles deducted from secondary for Ten Update Instructions Execution, i.e executing 10 Wasm instructions in store()

Memory allocation

  • + 127,000 * 100*10^-6 cycles per second deducted from secondary for GB Storage Per Second, i.e the stable memory in the secondary canister becomes 100 kB (note that this is in addition to the previous memory allocation)

Inter-canister call (response)

  • 1,000 * 100,000 deducted from main for Xnet Byte Transmission, i.e the 100 kB payload in the response from the inter-canister call

Hope this comes in handy for other fellow devs!

11 Likes