compute-units.ts 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228
  1. import * as anchor from "@coral-xyz/anchor";
  2. import * as token from "@coral-xyz/spl-token";
  3. import { Bench } from "../target/types/bench";
  4. import { BenchData, ComputeUnits } from "../scripts/utils";
  5. describe("Compute units", () => {
  6. // Configure the client to use the local cluster
  7. anchor.setProvider(anchor.AnchorProvider.env());
  8. const program = anchor.workspace.Bench as anchor.Program<Bench>;
  9. const owner = program.provider.publicKey!;
  10. let mintPk: anchor.web3.PublicKey;
  11. let tokenPk: anchor.web3.PublicKey;
  12. const computeUnits: ComputeUnits = {};
  13. const measureComputeUnits = async (
  14. ixName: string,
  15. options?: Partial<{
  16. accountCounts: number[];
  17. generateKeypair: (accountName: string) => anchor.web3.Keypair;
  18. generatePublicKey: (accountName: string) => anchor.web3.PublicKey;
  19. }>
  20. ) => {
  21. options ??= {};
  22. options.accountCounts ??= [1, 2, 4, 8];
  23. options.generateKeypair ??= () => anchor.web3.Keypair.generate();
  24. for (const accountCount of options.accountCounts) {
  25. // Check whether the init version of the instruction exists
  26. const ixNameInit = `${ixName}Init`;
  27. const hasInitVersion = program.idl.instructions.some((ix) =>
  28. ix.name.startsWith(ixNameInit)
  29. );
  30. const ixNames = [ixName];
  31. if (hasInitVersion) {
  32. // Init version has priority
  33. ixNames.unshift(ixNameInit);
  34. }
  35. const accounts: { [key: string]: anchor.web3.PublicKey } = {};
  36. const signers = [];
  37. for (const ixName of ixNames) {
  38. const method =
  39. `${ixName}${accountCount}` as keyof typeof program.methods;
  40. // Remove signers when it's not init instruction
  41. if (ixName !== ixNameInit) {
  42. signers.splice(0);
  43. }
  44. for (const ix of program.idl.instructions) {
  45. if (ix.name !== method) continue;
  46. for (const account of ix.accounts) {
  47. // Only set account keys if it hasn't been set before
  48. if (accounts[account.name]) {
  49. continue;
  50. }
  51. if (account.name === "payer") {
  52. accounts[account.name] = owner;
  53. continue;
  54. }
  55. // Skip other accounts to not override Anchor defaults
  56. if (!account.name.startsWith("account")) {
  57. continue;
  58. }
  59. if (options.generatePublicKey) {
  60. accounts[account.name] = options.generatePublicKey(account.name);
  61. continue;
  62. }
  63. const keypair = options.generateKeypair(account.name);
  64. accounts[account.name] = keypair.publicKey;
  65. if (account.signer) {
  66. signers.push(keypair);
  67. }
  68. }
  69. }
  70. // Send tx
  71. console.log({ method });
  72. const txHash = await program.methods[method]()
  73. .accounts(accounts)
  74. .signers(signers)
  75. .rpc();
  76. // Confirm tx
  77. await program.provider.connection.confirmTransaction(
  78. txHash,
  79. "confirmed"
  80. );
  81. // Get tx
  82. const tx = await program.provider.connection.getTransaction(txHash, {
  83. commitment: "confirmed",
  84. });
  85. computeUnits[method] = tx!.meta!.computeUnitsConsumed!;
  86. }
  87. }
  88. };
  89. before(async () => {
  90. // Create necessary accounts
  91. const tokenProgram = token.splTokenProgram({
  92. provider: anchor.AnchorProvider.local(),
  93. });
  94. const tx = new anchor.web3.Transaction();
  95. // Create mint account
  96. const mintKp = new anchor.web3.Keypair();
  97. mintPk = mintKp.publicKey;
  98. const createMintIx = await tokenProgram.account.mint.createInstruction(
  99. mintKp
  100. );
  101. const initMintIx = await tokenProgram.methods
  102. .initializeMint2(0, owner, null)
  103. .accounts({ mint: mintPk })
  104. .instruction();
  105. tx.add(createMintIx, initMintIx);
  106. // Create token account
  107. const tokenKp = new anchor.web3.Keypair();
  108. tokenPk = tokenKp.publicKey;
  109. const createTokenIx = await tokenProgram.account.account.createInstruction(
  110. tokenKp
  111. );
  112. const initTokenIx = await tokenProgram.methods
  113. .initializeAccount3(owner)
  114. .accounts({ account: tokenPk, mint: mintPk })
  115. .instruction();
  116. tx.add(createTokenIx, initTokenIx);
  117. await tokenProgram.provider.sendAndConfirm!(tx, [mintKp, tokenKp]);
  118. });
  119. it("AccountInfo", async () => {
  120. await measureComputeUnits("accountInfo");
  121. });
  122. it("Account Empty", async () => {
  123. await measureComputeUnits("accountEmpty");
  124. });
  125. it("Account Sized", async () => {
  126. await measureComputeUnits("accountSized");
  127. });
  128. it("Account Unsized", async () => {
  129. await measureComputeUnits("accountUnsized");
  130. });
  131. it("Boxed Account Empty", async () => {
  132. await measureComputeUnits("boxedAccountEmpty");
  133. });
  134. it("Boxed Account Sized", async () => {
  135. await measureComputeUnits("boxedAccountSized");
  136. });
  137. it("Boxed Account Unsized", async () => {
  138. await measureComputeUnits("boxedAccountUnsized");
  139. });
  140. it("Boxed Interface Account Mint", async () => {
  141. await measureComputeUnits("boxedInterfaceAccountMint", {
  142. generatePublicKey: () => mintPk,
  143. });
  144. });
  145. it("Boxed Interface Account Token", async () => {
  146. await measureComputeUnits("boxedInterfaceAccountToken", {
  147. generatePublicKey: () => tokenPk,
  148. });
  149. });
  150. it("Interface Account Mint", async () => {
  151. await measureComputeUnits("interfaceAccountMint", {
  152. generatePublicKey: () => mintPk,
  153. });
  154. });
  155. it("Interface Account Token", async () => {
  156. await measureComputeUnits("interfaceAccountToken", {
  157. generatePublicKey: () => tokenPk,
  158. accountCounts: [1, 2, 4],
  159. });
  160. });
  161. it("Interface", async () => {
  162. await measureComputeUnits("interface", {
  163. generatePublicKey: () => token.SPL_TOKEN_PROGRAM_ID,
  164. });
  165. });
  166. it("Program", async () => {
  167. await measureComputeUnits("program", {
  168. generatePublicKey: () => anchor.web3.SystemProgram.programId,
  169. });
  170. });
  171. it("Signer", async () => {
  172. await measureComputeUnits("signer");
  173. });
  174. it("SystemAccount", async () => {
  175. await measureComputeUnits("systemAccount");
  176. });
  177. it("UncheckedAccount", async () => {
  178. await measureComputeUnits("uncheckedAccount");
  179. });
  180. after(async () => {
  181. const bench = await BenchData.open();
  182. await bench.update({ computeUnits });
  183. });
  184. });