Skip to content

Commit

Permalink
Merge pull request #62 from blockiosaurus/main
Browse files Browse the repository at this point in the history
Adding benchmarking suite
  • Loading branch information
blockiosaurus authored Apr 11, 2024
2 parents ec08909 + 39ff6a4 commit 8eded81
Show file tree
Hide file tree
Showing 9 changed files with 528 additions and 4 deletions.
69 changes: 69 additions & 0 deletions .github/workflows/benchmark.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Benchmark
on:
workflow_call:

permissions:
# deployments permission to deploy GitHub pages website
deployments: write
# contents permission to update benchmark contents in gh-pages branch
contents: write

env:
CACHE: true

jobs:
benchmark:
name: Performance regression check
runs-on: ubuntu-latest
steps:
- name: Git checkout
uses: actions/checkout@v3

- name: Load environment variables
run: cat .github/.env >> $GITHUB_ENV

- name: Start validator
uses: metaplex-foundation/actions/start-validator@v1
with:
node: 18.x
solana: ${{ env.SOLANA_VERSION }}
cache: ${{ env.CACHE }}

- name: Install dependencies
uses: metaplex-foundation/actions/install-node-dependencies@v1
with:
folder: ./clients/js
cache: ${{ env.CACHE }}
key: clients-js

- name: Build
working-directory: ./clients/js
run: pnpm build

- name: Test
working-directory: ./clients/js
run: pnpm bench

# Download previous benchmark result from cache (if exists)
- name: Download previous benchmark data
uses: actions/cache@v4
with:
path: ./cache
key: ${{ runner.os }}-benchmark

# Run `github-action-benchmark` action
- name: Store benchmark result
uses: benchmark-action/github-action-benchmark@v1
with:
# What benchmark tool the output.json came from
tool: "customSmallerIsBetter"
# Where the output from the benchmark tool is stored
output-file-path: ./clients/js/output.json
# Where the previous data file is stored
# external-data-json-path: ./cache/benchmark-data.json
# Workflow will fail when an alert happens
fail-on-alert: true
# Access token to deploy GitHub Pages branch
github-token: ${{ secrets.GITHUB_TOKEN }}
# Push and deploy GitHub pages branch automatically
auto-push: true
8 changes: 7 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,19 @@ jobs:
uses: ./.github/workflows/test-js-client.yml
secrets: inherit

benchmark:
if: ${{ github.event_name == 'push' && github.ref == 'refs/heads/main' }}
name: Benchmark
needs: generate_clients
uses: ./.github/workflows/benchmark.yml
secrets: inherit

build_rust_client:
if: needs.changes.outputs.rust_client == 'true'
name: Rust Client
needs: generate_clients
uses: ./.github/workflows/build-rust-client.yml
secrets: inherit

test_rust_client:
if: needs.changes.outputs.rust_client == 'true'
name: Rust Client
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ dist
corebAsZhvD7wa9UiwMiqdbmminpxCp1AxvTrFJbizL.json
CoREENxT6tW1HoK8ypY1SxRMZTcVPm7R94rH4PZNhX7d.json
clients/js/yarn.lock
clients/js/output.json
7 changes: 7 additions & 0 deletions clients/js/bench/_setup.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
/* eslint-disable import/no-extraneous-dependencies */
import { createUmi as basecreateUmi } from '@metaplex-foundation/umi-bundle-tests';
import {
mplCore,
} from '../src';

export const createUmi = async () => (await basecreateUmi()).use(mplCore());
218 changes: 218 additions & 0 deletions clients/js/bench/create.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,218 @@
import { generateSigner, TransactionBuilder } from "@metaplex-foundation/umi";
import test from "ava";
import { existsSync, readFileSync, writeFileSync } from "fs";
import { createCollectionV1, createV1, pluginAuthorityPair, ruleSet } from "../src";
import { createUmi } from "./_setup";

test('create a new, empty asset', async (t) => {
// Given an Umi instance and a new signer.
const umi = await createUmi();
const assetAddress = generateSigner(umi);

const tx = await createV1(umi, {
asset: assetAddress,
name: "Test",
uri: "www.test.com",
}).sendAndConfirm(umi);

const compute = Number((await umi.rpc.getTransaction(tx.signature))?.meta.computeUnitsConsumed);
const account = await umi.rpc.getAccount(assetAddress.publicKey);
const space = account.exists ? account.data.length : 0;

const cuResult = {
name: `CU: ${t.title}`,
unit: "Compute Units",
value: compute,
}

const spaceResult = {
name: `Space: ${t.title}`,
unit: "Bytes",
value: space,
}

// Read the results array from output.json
let output = [];
if (existsSync("./output.json")) {
output = JSON.parse(readFileSync("./output.json", 'utf-8'));
}

// Push the result to the array
output.push(cuResult);
output.push(spaceResult);
// Write the array to output.json
writeFileSync("./output.json", JSON.stringify(output, null, 2));

t.pass();
});

test('create a new, empty asset with empty collection', async (t) => {
// Given an Umi instance and a new signer.
const umi = await createUmi();
const collectionAddress = generateSigner(umi);
const assetAddress = generateSigner(umi);

let builder = new TransactionBuilder();
builder = builder.add(createCollectionV1(umi, {
collection: collectionAddress,
name: "Test",
uri: "www.test.com",
}));
builder = builder.add(createV1(umi, {
asset: assetAddress,
collection: collectionAddress.publicKey,
name: "Test",
uri: "www.test.com",
}));

const tx = await builder.sendAndConfirm(umi);

const compute = Number((await umi.rpc.getTransaction(tx.signature))?.meta.computeUnitsConsumed);
const account = await umi.rpc.getAccount(assetAddress.publicKey);
const space = account.exists ? account.data.length : 0;

const cuResult = {
name: `CU: ${t.title}`,
unit: "Compute Units",
value: compute,
}

const spaceResult = {
name: `Space: ${t.title}`,
unit: "Bytes",
value: space,
}

// Read the results array from output.json
let output = [];
if (existsSync("./output.json")) {
output = JSON.parse(readFileSync("./output.json", 'utf-8'));
}

// Push the result to the array
output.push(cuResult);
output.push(spaceResult);
// Write the array to output.json
writeFileSync("./output.json", JSON.stringify(output, null, 2));

t.pass();
});

test('create a new asset with plugins', async (t) => {
// Given an Umi instance and a new signer.
const umi = await createUmi();
const assetAddress = generateSigner(umi);

const tx = await createV1(umi, {
asset: assetAddress,
name: "Test",
uri: "www.test.com",
plugins: [
pluginAuthorityPair({
type: 'Royalties',
data: {
basisPoints: 5,
creators: [{ address: umi.identity.publicKey, percentage: 100 }],
ruleSet: ruleSet('None'),
},
}),
pluginAuthorityPair({ type: 'FreezeDelegate', data: { frozen: true } }),
pluginAuthorityPair({ type: 'TransferDelegate' }),
pluginAuthorityPair({ type: 'BurnDelegate' }),
],
}).sendAndConfirm(umi);

const compute = Number((await umi.rpc.getTransaction(tx.signature))?.meta.computeUnitsConsumed);
const account = await umi.rpc.getAccount(assetAddress.publicKey);
const space = account.exists ? account.data.length : 0;

const cuResult = {
name: `CU: ${t.title}`,
unit: "Compute Units",
value: compute,
}

const spaceResult = {
name: `Space: ${t.title}`,
unit: "Bytes",
value: space,
}

// Read the results array from output.json
let output = [];
if (existsSync("./output.json")) {
output = JSON.parse(readFileSync("./output.json", 'utf-8'));
}

// Push the result to the array
output.push(cuResult);
output.push(spaceResult);
// Write the array to output.json
writeFileSync("./output.json", JSON.stringify(output, null, 2));

t.pass();
});

test('create a new asset with plugins and empty collection', async (t) => {
// Given an Umi instance and a new signer.
const umi = await createUmi();
const collectionAddress = generateSigner(umi);
const assetAddress = generateSigner(umi);

let builder = new TransactionBuilder();
builder = builder.add(createCollectionV1(umi, {
collection: collectionAddress,
name: "Test",
uri: "www.test.com",
}));
builder = builder.add(createV1(umi, {
asset: assetAddress,
name: "Test",
uri: "www.test.com",
plugins: [
pluginAuthorityPair({
type: 'Royalties',
data: {
basisPoints: 5,
creators: [{ address: umi.identity.publicKey, percentage: 100 }],
ruleSet: ruleSet('None'),
},
}),
pluginAuthorityPair({ type: 'FreezeDelegate', data: { frozen: true } }),
pluginAuthorityPair({ type: 'TransferDelegate' }),
pluginAuthorityPair({ type: 'BurnDelegate' }),
],
}));

const tx = await builder.sendAndConfirm(umi);

const compute = Number((await umi.rpc.getTransaction(tx.signature))?.meta.computeUnitsConsumed);
const account = await umi.rpc.getAccount(assetAddress.publicKey);
const space = account.exists ? account.data.length : 0;

const cuResult = {
name: `CU: ${t.title}`,
unit: "Compute Units",
value: compute,
}

const spaceResult = {
name: `Space: ${t.title}`,
unit: "Bytes",
value: space,
}

// Read the results array from output.json
let output = [];
if (existsSync("./output.json")) {
output = JSON.parse(readFileSync("./output.json", 'utf-8'));
}

// Push the result to the array
output.push(cuResult);
output.push(spaceResult);
// Write the array to output.json
writeFileSync("./output.json", JSON.stringify(output, null, 2));

t.pass();
});
Loading

0 comments on commit 8eded81

Please sign in to comment.