Findings from calculating the cycle consumption of Messity: a universal example

DBOX Foundation
4 min readSep 18, 2022

--

The process of estimating cycle consumption on the Internet Computer can be complicated and it is difficult to make a one-size-fits-all solution. That being said, we at dbox have tried to come up with a pretty universal example that covers most of the cycle transactions. We hope that this resource can be of great value for developers on the Internet Computer to estimate the costs of their canisters.

Data from this table in the official documentation will be used to determine cycle consumption for different operations.

Background

We at dbox have decided to extract the messaging functionality of dbox into its own open protocol that anyone can use. The protocol goes by the name Messity and can be used by any dapp or user to send and retrieve messages on the Internet Computer. Messity handles the storage of messages on-chain with infinite scalability.

Without a protocol like Messity, every dapp on the Internet Computer would have to re-invent the wheel each time there is a need for on-chain messaging.

Since the storage of messages is handled by the protocol, the messages are not bound to a certain client. Dbox is an example of one such client that uses Messity for dapp-to-user and user-to-user email-like communication, but of course, Messity can be used for any kind of messaging.

When designing Messity, we made sure to optimize the efficiency and ensure sufficient scalability of the protocol. In order to achieve that, we needed to have solid predictions on the cycle consumption of the protocol. This blog post aims to help other developers in a similar situation and the Internet Computer ecosystem as a whole.

A universal example of cycles consumption

Setup

We have two canisters: main and secondary. A client makes an update call to entry() in the main canister with a payload of 100 kB. entry() makes an inter-canister call to the secondary canister’s store() containing the 100 kB payload. store() then stores the 100 kB, and we assume that the total memory usage to store the payload in the secondary canister becomes exactly 100 kB.

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 the 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 exactly 10 kB.

Furthermore, we assume that the reserved compute allocation and the reserved memory allocation are set to 0 (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

Final thoughts

As mentioned earlier, it can be quite tricky to get the exact numbers regarding cycle consumption on the Internet Computer. We hope that this example can be used as a reference to help more developers estimate the cost of their dapps to ensure longevity and sustainability.

Huge thank you to Dominic Wörner and Dimitris Sarlis at DFINITY for assisting us in our research on cycle consumption!

dbox — The dapp inbox.

Stay tuned for more releases detailing the technologies behind dbox.

Twitter: https://twitter.com/DBOXFoundation
Discord:
https://discord.gg/y4CEQWqkrr

--

--

DBOX Foundation

The decentralized inbox built on Dfinity's Internet Computer.