diff --git a/package.json b/package.json index 6c58766f..4ed62b5c 100644 --- a/package.json +++ b/package.json @@ -70,6 +70,8 @@ "@typescript-eslint/parser": "^4.29.2", "arlocal": "1.1.13", "cheerio": "^1.0.0-rc.10", + "cli-table": "0.3.11", + "colors": "^1.4.0", "cors": "^2.8.5", "eslint": "^7.32.0", "eslint-config-prettier": "^8.3.0", @@ -79,13 +81,14 @@ "jest": "^27.4.3", "prettier": "^2.3.2", "rimraf": "^3.0.2", + "safe-stable-stringify": "2.3.1", + "simple-statistics": "^7.7.0", "smartweave": "0.4.45", "sqlite3": "^5.0.2", "ts-jest": "^27.0.7", "ts-node": "^10.2.1", "tsc-alias": "1.3.10", "tsconfig-paths": "^3.10.1", - "typescript": "4.5.2", - "safe-stable-stringify": "2.3.1" + "typescript": "4.5.2" } } diff --git a/src/contract/Contract.ts b/src/contract/Contract.ts index 839fc992..9feb6308 100644 --- a/src/contract/Contract.ts +++ b/src/contract/Contract.ts @@ -11,6 +11,7 @@ import { import { NetworkInfoInterface } from 'arweave/node/network'; export type CurrentTx = { interactionTxId: string; contractTxId: string }; +export type BenchmarkStats = { gatewayCommunication: number; stateEvaluation: number; total: number }; /** * A base interface to be implemented by SmartWeave Contracts clients @@ -175,4 +176,9 @@ export interface Contract { * the same as the evaluation options of the root contract. */ evaluationOptions(): EvaluationOptions; + + /** + * returns benchmark results for the last performed readState call + */ + lastReadStateStats(): BenchmarkStats; } diff --git a/src/contract/HandlerBasedContract.ts b/src/contract/HandlerBasedContract.ts index b5214515..5173f609 100644 --- a/src/contract/HandlerBasedContract.ts +++ b/src/contract/HandlerBasedContract.ts @@ -2,6 +2,7 @@ import { ArTransfer, ArWallet, Benchmark, + BenchmarkStats, Contract, ContractCallStack, ContractInteraction, @@ -53,6 +54,8 @@ export class HandlerBasedContract implements Contract { private readonly _callDepth: number; + private _benchmarkStats: BenchmarkStats = null; + /** * wallet connected to this contract */ @@ -117,6 +120,12 @@ export class HandlerBasedContract implements Contract { const total = (initBenchmark.elapsed(true) as number) + (stateBenchmark.elapsed(true) as number); + this._benchmarkStats = { + gatewayCommunication: initBenchmark.elapsed(true) as number, + stateEvaluation: stateBenchmark.elapsed(true) as number, + total + }; + this.logger.info('Benchmark', { 'Gateway communication ': initBenchmark.elapsed(), 'Contract evaluation ': stateBenchmark.elapsed(), @@ -574,4 +583,8 @@ export class HandlerBasedContract implements Contract { evaluationOptions(): EvaluationOptions { return this._evaluationOptions; } + + lastReadStateStats(): BenchmarkStats { + return this._benchmarkStats; + } } diff --git a/src/core/modules/impl/ContractHandlerApi.ts b/src/core/modules/impl/ContractHandlerApi.ts index 5e1d8bd3..3dd86b6d 100644 --- a/src/core/modules/impl/ContractHandlerApi.ts +++ b/src/core/modules/impl/ContractHandlerApi.ts @@ -16,7 +16,7 @@ import { } from '@smartweave'; import BigNumber from 'bignumber.js'; import * as clarity from '@weavery/clarity'; -import * as v8 from "v8"; +import * as v8 from 'v8'; export class ContractHandlerApi implements HandlerApi { private readonly contractLogger: RedStoneLogger; diff --git a/tools/contract.ts b/tools/contract.ts index 8fea729e..afc58c23 100644 --- a/tools/contract.ts +++ b/tools/contract.ts @@ -1,6 +1,7 @@ /* eslint-disable */ import Arweave from 'arweave'; import { + BenchmarkStats, LoggerFactory, MemCache, RedstoneGatewayContractDefinitionLoader, RedstoneGatewayInteractionsLoader, SmartWeaveWebFactory @@ -12,19 +13,28 @@ import path from 'path'; import {FromFileInteractionsLoader} from './FromFileInteractionsLoader'; import {SmartWeaveNodeFactory} from '../src/core/node/SmartWeaveNodeFactory'; import {readContract} from "smartweave"; +import {inspect} from "util"; +import colors = module +import {max, mean, median, min, standardDeviation, variance} from "simple-statistics"; const stringify = require('safe-stable-stringify') const logger = LoggerFactory.INST.create('Contract'); +let os = require("os"); + //LoggerFactory.use(new TsLogFactory()); -LoggerFactory.INST.logLevel("error"); -LoggerFactory.INST.logLevel("debug", "ArweaveGatewayInteractionsLoader"); +LoggerFactory.INST.logLevel("fatal"); +LoggerFactory.INST.logLevel("info", "Contract"); + + +//LoggerFactory.INST.logLevel("info", "HandlerBasedContract"); + +/*LoggerFactory.INST.logLevel("debug", "ArweaveGatewayInteractionsLoader"); LoggerFactory.INST.logLevel("debug", "HandlerBasedContract"); LoggerFactory.INST.logLevel("debug", "ContractDefinitionLoader"); -LoggerFactory.INST.logLevel("debug", "CacheableContractInteractionsLoader"); - +LoggerFactory.INST.logLevel("debug", "CacheableContractInteractionsLoader");*/ async function main() { @@ -36,34 +46,102 @@ async function main() { logging: false // Enable network request logging }); - const contractTxId = 'Daj-MNSnH55TDfxqC7v4eq0lKzVIwh98srUaWqyuZtY'; + const contractTxId = 'Daj-MNSnH55TDfxqC7v4eq0lKzVIwh98srUaWqyuZtY'; //844916 //const contractTxId = 't9T7DIOGxx4VWXoCEeYYarFYeERTpWIC1V3y-BPZgKE'; //749180 //const interactionsLoader = new FromFileInteractionsLoader(path.join(__dirname, 'data', 'interactions.json')); // const smartweave = SmartWeaveWebFactory.memCachedBased(arweave).setInteractionsLoader(interactionsLoader).build(); - const smartweaveR = SmartWeaveWebFactory - .memCachedBased(arweave, 1) - .setInteractionsLoader( - new RedstoneGatewayInteractionsLoader("https://gateway.redstone.finance")) - .setDefinitionLoader( - new RedstoneGatewayContractDefinitionLoader("http://localhost:5666", arweave, new MemCache())) - .build(); - - /* const usedBefore = Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100 - const contractR = smartweaveR.contract(contractTxId); - const {state, validity} = await contractR.readState(); - const usedAfter = Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100 - logger.warn("Heap used in MB", { - usedBefore, - usedAfter - });*/ - - const smartweave = SmartWeaveWebFactory.memCached(arweave); - const contract = smartweaveR.contract(contractTxId).setEvaluationOptions({ - updateCacheForEachInteraction: true + + + /* const usedBefore = Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100 + const contractR = smartweaveR.contract(contractTxId); + const {state, validity} = await contractR.readState(); + const usedAfter = Math.round((process.memoryUsage().heapUsed / 1024 / 1024) * 100) / 100 + logger.warn("Heap used in MB", { + usedBefore, + usedAfter + });*/ + + const colors = require('colors'); + const stats = require('simple-statistics') + + console.log("Test info ".bgRed); + console.log("==============="); + + console.log(" ", "OS ".bgGrey, os.type() + " " + os.release() + " " + os.arch()); + console.log(" ", "Node.JS ".bgGrey, process.versions.node); + console.log(" ", "V8 ".bgGrey, process.versions.v8); + + let cpus = os.cpus().map(function (cpu) { + return cpu.model; + }).reduce(function (o, model) { + if (!o[model]) o[model] = 0; + o[model]++; + return o; + }, {}); + + cpus = Object.keys(cpus).map(function (key) { + return key + " \u00d7 " + cpus[key]; + }).join("\n"); + + console.log(" ", "CPU ".bgGrey, cpus); + console.log(" ", "Memory ".bgGrey, (os.totalmem() / 1024 / 1024 / 1024).toFixed(0), "GB"); + + console.log("==============="); + console.log(" ", "Contract ".bgGrey, "t9T7DIOGxx4VWXoCEeYYarFYeERTpWIC1V3y-BPZgKE"); + console.log(" ", "Height ".bgGrey, 749180); + + + const Table = require('cli-table'); + +// instantiate + const table = new Table({ + head: ['# Test:'.green, 'Gateway communication:'.green, 'State evaluation:'.green, 'Total:'.green] + , colWidths: [10, 30, 20, 20] }); - const result = await contract.readState(); + + const results: BenchmarkStats[] = []; + + for (let i = 1; i <= 10; i++) { + + const smartweaveR = SmartWeaveWebFactory + .memCachedBased(arweave, 1) + .setInteractionsLoader( + new RedstoneGatewayInteractionsLoader("https://gateway.redstone.finance", {confirmed: true})) + .setDefinitionLoader( + new RedstoneGatewayContractDefinitionLoader("https://gateway.redstone.finance", arweave, new MemCache())) + .build(); + + const contract = smartweaveR.contract(contractTxId); + await contract.readState(844916); + + const result = contract.lastReadStateStats(); + + results.push(result); + + table.push( + [`${i}`.magenta, result.gatewayCommunication + "ms", result.stateEvaluation + "ms", result.total + "ms"] + ); + } + + console.log(table.toString()); + + const tableStats = new Table({ + head: ['Statistics:'.green, 'Gateway communication:'.green, 'State evaluation:'.green, 'Total:'.green] + , colWidths: [20, 30, 20, 20] + }); + + tableStats.push( + ["Mean".cyan, mean(results.map(r => r.gatewayCommunication)) + "ms", mean(results.map(r => r.stateEvaluation)) + "ms", mean(results.map(r => r.total)) + "ms"], + ["Median".cyan, median(results.map(r => r.gatewayCommunication)) + "ms", median(results.map(r => r.stateEvaluation)) + "ms", median(results.map(r => r.total)) + "ms"], + ["Min".cyan, min(results.map(r => r.gatewayCommunication)) + "ms", min(results.map(r => r.stateEvaluation)) + "ms", min(results.map(r => r.total)) + "ms"], + ["Max".cyan, max(results.map(r => r.gatewayCommunication)) + "ms", max(results.map(r => r.stateEvaluation)) + "ms", max(results.map(r => r.total)) + "ms"], + ["Std. Dev.".cyan, standardDeviation(results.map(r => r.gatewayCommunication)).toFixed(2) + "ms", standardDeviation(results.map(r => r.stateEvaluation)).toFixed(2) + "ms", standardDeviation(results.map(r => r.total)).toFixed(2) + "ms"], + ); + + console.log(tableStats.toString()); + //const result2 = await readContract(arweave, "t9T7DIOGxx4VWXoCEeYYarFYeERTpWIC1V3y-BPZgKE") @@ -71,7 +149,7 @@ async function main() { //fs.writeFileSync(path.join(__dirname, 'data', 'validity.json'), JSON.stringify(validity)); //fs.writeFileSync(path.join(__dirname, 'data', 'validity_old.json'), JSON.stringify(result.validity)); - fs.writeFileSync(path.join(__dirname, 'data', 'state_new.json'), stringify(result.state).trim()); + //fs.writeFileSync(path.join(__dirname, 'data', 'state_new.json'), stringify(result.state).trim()); //fs.writeFileSync(path.join(__dirname, 'data', 'state_old.json'), stringify(result2).trim()); //fs.writeFileSync(path.join(__dirname, 'data', 'state_arweave.json'), JSON.stringify(result.state)); diff --git a/yarn.lock b/yarn.lock index cb4b73ef..2b6adf16 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2108,6 +2108,13 @@ cli-spinners@^2.5.0: resolved "https://registry.yarnpkg.com/cli-spinners/-/cli-spinners-2.6.1.tgz#adc954ebe281c37a6319bfa401e6dd2488ffb70d" integrity sha512-x/5fWmGMnbKQAaNwN+UZlV79qBLM9JFnJuJ03gIi5whrob0xV0ofNVHy9DhwGdsMJQc2OKv0oGmLzvaqvAVv+g== +cli-table@0.3.11: + version "0.3.11" + resolved "https://registry.yarnpkg.com/cli-table/-/cli-table-0.3.11.tgz#ac69cdecbe81dccdba4889b9a18b7da312a9d3ee" + integrity sha512-IqLQi4lO0nIB4tcdTpN4LCB9FI3uqrJZK7RC515EnhZ6qBaglkIgICb1wjeAqpdoOabm1+SuQtkXIPdYC93jhQ== + dependencies: + colors "1.0.3" + cli-width@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-3.0.0.tgz#a2f48437a2caa9a22436e794bf071ec9e61cedf6" @@ -2205,7 +2212,12 @@ colorette@2.0.16: resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.16.tgz#713b9af84fdb000139f04546bd4a93f62a5085da" integrity sha512-hUewv7oMjCp+wkBv5Rm0v87eJhq4woh5rSR+42YSQJKecCqgIqNkZ6lAlQms/BwHPJA5NKMRlpxPRv0n8HQW6g== -colors@^1.3.3: +colors@1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/colors/-/colors-1.0.3.tgz#0433f44d809680fdeb60ed260f1b0c262e82a40b" + integrity sha1-BDP0TYCWgP3rYO0mDxsMJi6CpAs= + +colors@^1.3.3, colors@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== @@ -6080,6 +6092,11 @@ signal-exit@^3.0.0, signal-exit@^3.0.2, signal-exit@^3.0.3: resolved "https://registry.yarnpkg.com/signal-exit/-/signal-exit-3.0.6.tgz#24e630c4b0f03fea446a2bd299e62b4a6ca8d0af" integrity sha512-sDl4qMFpijcGw22U5w63KmD3cZJfBuFlVNbVMKje2keoKML7X2UzWbc4XrmEbDwg0NXJc3yv4/ox7b+JWb57kQ== +simple-statistics@^7.7.0: + version "7.7.0" + resolved "https://registry.yarnpkg.com/simple-statistics/-/simple-statistics-7.7.0.tgz#cfef964473c940f2adfec85ffde8591a5a933a46" + integrity sha512-TAsZRUJ7FD/yCnm5UBgyWU7bP1gOPsw9n/dVrE8hQ+BF1zJPgDJ5X/MOnxG+HE/7nejSpJLJLdmTh7bkfsFkRw== + sisteransi@^1.0.5: version "1.0.5" resolved "https://registry.yarnpkg.com/sisteransi/-/sisteransi-1.0.5.tgz#134d681297756437cc05ca01370d3a7a571075ed"